Django: Dynamically add apps as plugin, building urls and other settings automatically

后端 未结 5 1326
慢半拍i
慢半拍i 2021-01-03 01:29

I have following structure of my folders in Django:

./project_root
    ./app
       ./fixtures/
       ./static/
       ./templates/
       ./blog/
       ./         


        
相关标签:
5条回答
  • 2021-01-03 01:58

    What you'll want is something like this:

    from django.utils.importlib import import_module
    for app in settings.INSTALLED_APPS:
    
        try:
            mod = import_module('%s.urls' % app)
            # possibly cleanup the after the imported module?
            #  might fuss up the `include(...)` or leave a polluted namespace
        except:
            # cleanup after module import if fails,
            #  maybe you can let the `include(...)` report failures
            pass
        else:
            urlpatterns += patterns('',
                url(r'^%s/' % slugify(app), include('%s.urls' % app)
            )
    

    You'll also want to steal and implement your own slugify from django template or utils (I'm not exactly sure where it lives these days?) and slightly modify it to strip out any 'dots' and other useless namespacing you don't want in your 'url' e.g. you might not want your urls looking like so: 'example.com/plugin.some_plugin_appname/' but like example.com/nice_looking_appname/

    You might even not want it automagicly named after all, and instead made a configurable 'setting' in your plugins own 'settings' module or something like so:

    # plugin settings conf
    url_namespace = 'my_nice_plugin_url/'
    
    # root urls.py:
    url(r'^%s/' % mod.url_namespace, include(...))
    # or:
    url(r'^%s/' % app.settings.url_namespace, inc..
    

    You probably get the gist of it.

    Kind regards,

    0 讨论(0)
  • 2021-01-03 02:03

    I've modified @Progressify's code a bit further to do away with the app prefix.

    # in your project's urls.py
    from django.apps import apps
    from django.contrib import admin
    from django.conf import settings
    from django.urls import include, path
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        # ...
        # any other paths you may have
    ]
    
    # Load urls from any app that defines a urls attribute in appname.apps.AppConfig
    for app in settings.INSTALLED_APPS:
        try:
            app_config = apps.get_app_config(app.rsplit('.')[0])
            try:
                urls = app_config.urls
                urlpatterns += [
                    path(f'{app_config.urls}', include(f'{app_config.name}.urls', namespace=f'{app_config.urls_namespace}')),
                ]
            except AttributeError:
                pass
                
        except LookupError:
            pass
    

    This will look for an AppConfig in your project's apps.py that defines a prefix and a namespace, and include those accordingly. Admittedly the error catching is a bit crude, so you may want to refine it a bit depending on whether you want to allow your app to define urls but not a namespace, etc.

    # in each of your apps' apps.py
    from django.apps import AppConfig
    
    class UsersConfig(AppConfig): # name the class whatever you want
        name = 'users'            # name of your app
        urls = ''                 # prefix for this app's urls –– may need to append slash
        urls_namespace = 'users'  # app url namespace –– it's "best practice" to define one
    
    0 讨论(0)
  • 2021-01-03 02:04

    I have modified the logic of @wdh and I have tested with Django 2.2.9 (latest stable in Jan 2020) and python 3.8

    urls.py

    from django.apps import apps
    from django.conf import settings
    
    for app in settings.INSTALLED_APPS:
        if app.startswith('myappsprefix_'):
            app_config = apps.get_app_config(app.rsplit('.')[0])
            urlpatterns += i18n_patterns(
                path(f'{app_config.urls}/', include(f'{app_config.name}.urls')),
            )
    

    i18n_patterns is for internationalization.

    apps.py of every app

    class MyCustomAppsConfig(AppConfig):
        name = 'myappsprefix_mycustomapp'
        urls = 'mybaseurlforapp'  # required!
    

    in my settings.py

    INSTALLED_APPS = [
        ...
    
        'myappsprefix_mycustomapp.apps.MyCustomAppsConfig',
        ...
    ]
    
    0 讨论(0)
  • 2021-01-03 02:10

    Have you tried something like:

    for app in INSTALLED_APPS:
    
        # You'll want to check that you can import the urls of that app here
    
        urlpatterns += patterns('',
            url(r'^%s/' % app, include('%s.urls' % app) )
        )
    
    0 讨论(0)
  • 2021-01-03 02:14

    It is best practice not to access INSTALLED_APPS directly as stated in django docs but to use the registry instead:

    from django.apps import apps
    
    for app in apps.get_app_configs():
        app_name = app.name
        try:
            ...
    
    0 讨论(0)
提交回复
热议问题