I have following structure of my folders in Django:
./project_root
./app
./fixtures/
./static/
./templates/
./blog/
./
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,
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
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',
...
]
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) )
)
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:
...