Dynamically including Django apps using entry points

For the Airavata Django Portal project I’ve been looking at how third-party contributors could contribute Django apps that would integrate nicely with the portal. This is not something that Django has built-in support for, as can be seen in Django ticket #29554, but people have come up with workarounds.

The most compelling workaround I found was discussed on the django-developers mailing list and is implemented by the pretix open source application. One clever thing they are doing is using python packaging entry points:
https://packaging.python.org/specifications/entry-points/. The entry points can be dynamically introspected, for example pretix iterates over pretix.plugin entry points in its settings.py.

This can be used in the setup.py of a Django app to register its AppConfig. A Django project can introspect any such Django apps that installed in the current virtual environment and add all of these to the INSTALLED_APPS in settings.py, just like pretix above.

The other things to configure when adding a Django app are to add the app’s urls to the project’s urls.py. Again we can loop over all Django app entry points and get their AppConfig. We can either allow these apps to specify what URL prefix they want, as a property on the AppConfig, or we can just assign a URL for these apps with a common prefix of plugins/ then the app’s label (which must be unique). The urls module of the Django app can be figure out if you have an instance of its AppConfig:

from importlib import import_module
# where app is an AppConfig instance retrieved from django.apps
urls = import_module(".urls", app.name)

In the case of the Airavata Django Portal I want to know a bit more about the apps in order to integrate them into the overall navigation. The following are properties that can be specified on the AppConfig:

  • url_home: This is the home URL of this app. I could default it to just the first URL pattern of the app:
from importlib import import_module
urls = import_module(".urls", app.name)
url_home = urls.app_name + ":" + urls.urlpatterns[0].name
  • app_order: the desired order of the app in the listing. Could default to last if not specified
  • fa_icon_class: FontAwesome icon class to use for this app. Could default to something generic
  • app_description: a description of the app. Could default to just the verbose_name of the app

As indicated in the list above, all of these extra bits of metadata should be optional and be provided as needed.

There’s still the issue of sub-menu items that need to be integrated. Currently apps should inherit from the base.html template and add sub-menu items into a navigation items block that appears on the left side of the portal page. This could perhaps be better implemented as AppConfig metadata.

Leave a comment

Leave a Reply

%d bloggers like this: