Django-Specific Standards
These are some best practices to use when developing Django projects.
General
- Always subscribe to the idea of "Fat models, thin views, stupid templates, and utility modules."
- View and template logic should be as minimal as humanly possible.
- Logic that cannot be added as a Model or Manager method should be in a utility module (
utils.py
file within an app) - Logic that applies across apps should live in a project-wide
utils
"app"
- For implementing APIs, use Django REST Framework; we have our own best practices for using DRF.
- Always import settings via the
django.conf
package:from django.conf import settings
Models
- Always use a
models.Manager
subclass if the logic is operating on a set of models.- Prefer class methods of
models.Manager
subclasses for overriding__init__
on a model. - Leverage
models.Manager
subclass helpers for operating on tables or collections, e.g.get_or_create()
,update()
,delete()
,bulk_insert()
.
- Prefer class methods of
- Define methods on your Model classes to handle logic that operates on a single object instance.
- Use
filter()
for AND conditions in queries, andQ
objects for OR conditions. - Use
values()
andvalues_list()
to avoid instantiation of a model whenever possible. - Use
select_related()
if traversing a related field to avoid additional queries for ForeignKey relations. - Use
prefectch_related()
to reduce the number of queries necessary for M2M relations. - Django's QueryAPI documentation is your friend; read it as often as necessary.
Blank vs Null
Django's ORM maps models and querysets back to a relational database. Most fields can be defined with options to allow NULL
values (null=True
) or blank values (blank=True
). What's important to remember is that null
relates to the database itself, whereas blank
relates to field validation only. Typically, NULL
values indicate that a field on a model wasn't set to anything, and so there's no value to put in the corresponding database column. However, allowing TextFields or CharFields to be both null and blank leads to a situation where two values could represent the absence of data. For this reason, you should not use null=True
for these fields (or subclasses of these fields, such as EmailField).
The database does not care if a TextField
or CharField
is blank or not, however. This is only taken into account when validating input when creating a model instance (or deserializing input from an API.) In almost all circumstances, you should generally set text-type fields to blank=True
if a field is allowed to have no value, and default=''
in order to fill the field when no input is specified.
Routes
- Always provide a name field for a route for easy reverse lookups
- End all your URL regex strings with
/?$
; this makes the trailing slash optional
Project Layout
In general, we stick to the basic Django project layout. Your root repository folder will typically contain configuration files like the main README.md
, Dockerfile
, fabfile
, .drone.yml
, etc, as well as the main project filter generated with the startproject
command. Inside the project folder is another folder of the same name, where settings.py
, wsgi.py
and the root urls.py
live, known as the configuration folder. The manage.py
command also lives here.
When you use the startapp
command, it will create a folder with models.py
, views.py
, admin.py
, and tests.py
files inside. Try to limit each app to a specific feature, and only create those models needed to implement that feature.
Settings
Since we primarily use Django combined with Django REST Framework (DRF) to build RESTful API servers, some of the default Django applications and middleware are unecessary and add memory and performance overhead for unneeded features. It is therefore recommended to remove the following default apps and middleware from the INSTALLED_APPS
and MIDDLEWARE_CLASSES
settings, respectively:
# Unnecessary apps
'django.contrib.sessions'
'django.contrib.messages'
'django.contrib.staticfiles'
# Unnecessary middleware
'django.contrib.sessions.middleware.SessionMiddleware'
'django.middleware.csrf.CsrfViewMiddleware'
'django.contrib.auth.middleware.SessionAuthenticationMiddleware'
'django.contrib.messages.middleware.MessageMiddleware'
Note: If you are using DRF's built-in browsable API feature, it relies on Django's session-based authentication to work. You'll need to leave in django.contrib.sessions
in INSTALLED_APPS
as well as django.contrib.sessions.middleware.SessionMiddleware
and django.contrib.auth.middleware.SessionAuthenticationMiddleware
in MIDDLEWARE_CLASSES
.
Django REST Framework
For more specifics around building a Django-based API project, check our DRF best practices.