Django Best Practices
Django Best Practices#
Some Important Rules#
Keep it simple#
When building software projects, each piece of unnecessary complexity makes it harder to add new features and maintain old ones
Simplicity is the ultimate sophistication - Leonardo Da Vinci
Fat Models, Utility Modules, Thin Views, Stupid Templates#
Stay away from putting more logic in views and templates
Start with Django by default#
Forget custom modules, keep to standard Django as far as possible
Stay Aware of Django design philosophies#
Check Django’s design philosophy
12 Factor App#
Be aware and implement The 12 factor app
Coding Style#
Make your code readable#
Readable code is easier to maintain.
- Avoid abbreviating variable names
- Write out function argument names
- Document your classes and methods
- Comment code where needed
- Refactor repeated code into reusable functions
- Keep functions short, you should not need to scroll
Shortcuts come at the expense of hours of technical debt
Write out the entire variable name balance_sheet_decrease instead of bsd
Pep-8#
There is a linter for your text editor
Also use flake8 for checking code quality and as part of CI
79 Character limit#
The 79 character limit should be adhered to on open source projects, 99 on closed projects
Imports#
Imports should be grouped in this order:
- Standard library imports
- Related third-party imports, including Django and packages related to it
- Local app or library specific imports
Use explicit relative imports#
With hardcoded imports, you cannot just change the name of the app, you have to change all the imports as well.
It also helps to tell global imports from local imports
# Use this relative import
from .models import WaffleCone
# Instead of
from cones.models import WaffleCone
Avoid using import *#
# Use
from Django import forms
# Instead of
from Django.forms import *
because there can be naming collisions like:
# Don't do this, models overrite the forms
from Django.forms import CharField
from django.db.models import CharField
To overcome naming collisions you can use as
from django.db.models import CharField as ModelCharField
from Django.forms import CharField as FormCharField
Django coding Style#
Use Django style guide#
Use underscore in url() name#
# wrong
name='add-topping'
# correct
name='add_topping'
url(
regex='^add/$',
view=views.add_topping,
name='add_topping'
),
Also use underscores in template block names, eg. content_block
Choose a JavaScript or HTML style guide#
Never Code to the IDE or Text Editor#
Never code to the IDE or Text Editor
Development Setup#
- Use the same database engine in all environments everywhere
- Fixtures are not a reliable tool for moving data between environments, although they are fine for basic data with
loaddataanddumpdata - Do not use
sqlite3with Django in production - Use pip and virtualenv. A good option is virtualenvwrapper, although it can be difficult to set up
Instead of typing something like
source Users/surfer190/projects/my-project/env/bin/activate
You can simply type
workon my-project
- Install Django with
pipand userequirementsfiles - Eliminate differences between environments
* Operating system differences - Windows, macOS, Ubuntu * Python set up differences - Python version * Developer set up differences
How to Layout Django Projects#
Use Cookie Cutter as a reference
Recommended set up#
- Top level repo root:
readme.md,gitignore,requirements.txt - Second level: project root,
Django project - Configuration root:
settings module
Django App Design Fundamentals#
Write apps that do one thing and do it well
Each app should be tightly focused on its task
It should be explained in a simple sentence (with not many ands...)
Naming your Apps#
Your app’s name should be the plural form of the app’s main model: flavours, animals, polls, dreams
There are many exceptions: blog being one
Also be aware of the URL so that it separates which part of the site
When in doubt, keep apps small
Uncommon App Modules#
api/- A package for isolating various modules when creating an APIbehaviours.py- Option for locating model mixinsconstants.py- App level settings, if there are enough of themdecorators.py- Where we locate decoratorsdb/- custom database fields or componentsfields.py- used for form fields, sometimes for model fields when there is not enough code for a fulldbpackagefactories.py- where we create test data factorieshelpers.py- helper functionsmanagers.py- move custom model managers to this modulesignals.py- custom signals, although they are often avoidedutils.py- same ashelpersviewmixins.py- view modules can be thinned by putting view mixins here
Settings#
- All settings should be version controlled
- Do not repeat yourself - inherit from a base settings file
- Keep secret keys safe, outside version control
Do not use local_settings.py as it can cause headaches
Instead of having a single settings.py file, use multiple files in the settings/ directory
base.pylocal.pystaging.pytest.pyproduction.py
Each settings module should have its own requirements file
python manage.py runserver --settings=twoscoops.settings.local
Specify the settings file to use
Rather make use of environment settings
Models#
Don’t rush into creating models
If an app has more than 20 models, it should be broken down into smaller apps
Avoid multi-table inheritance
The TimeStamed Model#
Adds a created and modified fields for all models, as an abstract class.
No separate tables are created. The fields are merely added to the new models.
from django.db import models
class TimeStampedModel(models.Model):
"""
An abstract base class model that provides self-
updating ``created`` and ``modified`` fields
"""
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
Usage:
from django.db import models
from core.models import TimeStampedModel
class Flavour(TimeStampedModel):
title = models.CharField(max_length=200)
Migrations#
Make a new migration after each new model
Always put data migration code into version control
Stay normalised and use caching, try and avoid denormalisation asmuch as possible
Null and Blank#
When to use null=True and blank=True
FileField and ImageField use blank
BooleanField, use NullBooleanField