Apps are a fundamental concept in Django. But what exactly are they and when should we use them?
If you’ve ever built anything using Django, you’ll be used to including apps in your
This is something we take for granted, but it’s not always obvious why we need to do this. For something we use so routinely, it’s a concept we’re surprisingly vague about.
So, what are Django apps for? Why do we need to install them? Why can’t they just be plain old Python packages? Before you read on, think for a moment and try to answer these questions for yourself.
What apps are for: a general rule
Here’s a general rule for the purpose of a Django app:
When you declare a particular Python package as an app (by including it in
INSTALLED_APPS), you allow your project to ‘look into itself’ (i.e. introspect) and discover and enable functionality within that package.
By way of contrast, look at an aspect of Django that doesn’t use the apps system: middleware. Middleware classes can sit anywhere in your project, but they need to be explicitly configured:
In contrast, features which use the apps system (e.g. models) don’t need any specific configuration to let the framework know about them other than app installation.
- Models: The most obvious one; Django looks for any
modelsmodule within your package and handles the necessary database migrations.
- Admin: You register models in the Django Admin Site within an
- Template tags: You may include a
templatetagspackage to allow loading of custom template tags.
- Template directories: Assuming you have configured your
True, Django will know about any files within a
templatessubdirectory in your app.
- Celery tasks: If you’re using Celery, you can include your tasks in a
tasks.pyand Celery will automatically pick them up.
Notice that all of these examples follow a similar pattern: for each piece of functionality, Django looks in every installed app for a particular module/directory name; if it’s there, it will use it. This is the foundation of Django’s pluggable architecture; providing a package is in your Python path, adding it to
INSTALLED_APPS allows it to hook in to other parts of the system.
This process is called autodiscovery, and there is a built in function for it:
django.utils.module_loading.autodiscover_modules. To use, simply call it with the name of the module you want, and Django will import any modules that have that name (providing they are in
INSTALLED_APPS, of course). For example, to import every module named
foo.py, just call:
A good place to call
autodiscover_modules is in the
ready() method of your application configuration class.
Understanding INSTALLED_APPS can improve your Django
Why is knowing this useful? If you understand the fundamental concept of what an app is, you should be able to structure your projects better. Apps should be pluggable, discoverable modules, so design them accordingly.
That means if you have a large amount of interdependent functionality, there really is no need to split it up into multiple apps. You’re probably better off splitting up the code into a manageable file structure. For example, there’s nothing special about
models.py other than it gets automatically imported during certain model-related tasks. To make things more manageable, you could turn
models.py into a subpackage within your app, distribute the models within different files inside it, and import those models within the
Conversely, if parts of your project could be designed as plugins, consider making them apps (even if they don’t declare any models). Let’s say you have a site that defines a range of different products by inheriting from a base
Product class. Rather than manually importing them, or configuring them in your settings, you could have your core products app autodiscover modules named
products. This would allow you to have separate, per-product apps that declare their products in
products.py files. (For more ideas about this, you may like to watch my talk on Encapsulated Django.)
To sum up, the concept of a Django app is a powerful one that goes beyond mere models. It’s the magic that allows the framework to find out about what’s inside your project. Knowing this can help you understand how certain things get imported, and give you confidence to be inventive in the way you architect your code.