Skip to content

My best practices for: Django

Whenever I am working intensively with a new programming language or a new framework, sooner or later I get into a stressful state of mind:

Am I doing this right?
Is this a standard way of doing things, or am I hacking it my way?
What would other coders think about this code?

So soon after I start looking for “best practices ” and dig through hundreds of opinions. Usually, I end up with a mix of “that feels right for me” and “everyone says to do it like this”. All in all, it is a strategy that works okay for me, and leads to some pretty clean code.
The problem is: I tend to forget these collected best practices, and for every new project, I have to start again with my search for the TRUE best practice.

Now that I have a blog, this has to change. So I will start formalizing my best practices for any framework or project I work on, and hopefully it will be constantly updated.

I assume that you are already a little familiar with Django and Python 3. So, enough talk, let us begin with Django. Note: In this post I will work with Django 1.11, so some things will be different in older or newer versions.

Project structure

Since the project I am working on is called “StudLife”, you are going to read it a few times during this post.

When you create the project, you probably use the django-admin utility:
django-admin startproject studlife
See the Getting Started from the Django docs for more.

When you create a project like this, what you get is a folder named after your project, with some default Django files. It should look like this:

studlife/
├── manage.py
└── studlife
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py

Projects and Apps

As you may know, Django distinguished between projects and apps. projects consist of one or more apps. Each app contains its own models, views and logic. Apps are supposed to be reusable, which means that you can use an app in different projects without much adaptions to the existing code.

After you create the project, with django-admin or manage.py you can create an app, like this:

django-admin startapp dashboard

or this

./manage.py startapp dashboard

Using the built-in tools, you get the following structure:

studlife/
├── actions  # app
├── dashboard  # app
├── db.sqlite3
├── login  # app
├── manage.py
├── requirements.txt
├── static
├── studlife  # app
└── templates

You can see 4 different apps here, all belonging to the project. The static and templates directories are for project-specific shared content, such as CSS and JS files, Bootstrap headers and some base templates. But more about that later!

Structure of an app

An app also has a specific structure, that should be similar in all your apps. Let’s see what a typical app looks like:

dashboard/
├── admin.py
├── apps.py
├── fixtures
│   └── init.json
├── forms.py
├── migrations
│   ├── 0001_initial.py
│   ├── 0002_auto_20170526_2226.py
├── models.py
├── static
│   └── dashboard
├── templates
│   └── dashboard
├── tests.py
├── urls.py
└── views.py

There is a lot of stuff in the dashboard app folder:
admin.py is used to register your models with the django-admin interface.
apps.py is used for any application-specific configuration.
Fixtures are JSON, XML or YAML files with initial data for your database. So you can avoid any hard-coded entries in your code and pre-populate your database. Really useful!
forms.py is used to define your own Django Forms, which helps you create forms without all the manual work.
migrations contains all the model migrations for this app.
models.py contains all the models for your app. There is a design pattern that prefers fat models and thin views, but more about that later.
static and templates are for static content and your templates. Important here is to have another folder named after your app. From the Django docs:

Static file namespacing
Now we might be able to get away with putting our static files directly in my_app/static/ (rather than creating another my_app subdirectory), but it would actually be a bad idea. Django will use the first static file it finds whose name matches, and if you had a static file with the same name in a different application, Django would be unable to distinguish between them. We need to be able to point Django at the right one, and the easiest way to ensure this is by namespacing them. That is, by putting those static files inside another directory named for the application itself.

tests.py is for tests (duh!). I admit I only written very few test cases per app, so one file was enough. The docs on Testing suggest to use your own scheme for tests, the framework finds the tests itself.

urls.py is where your url mappings go. This file has to be imported in your project’s urls.py file.

views.py contains Django views. A popular pattern is to have minimal logic code here and leave the creation and handling of objects to the models.

Fat models, skinny views?

There is this widely known design pattern that defines where to put the business logic in your Django project. The views are only there to respond to HTTP requests and call the specific parts of your application that manage the logic. The models define lots of methods that act upon the model’s data. An example of a fat model is the AbstractUser class, which is the base of User in the Django Admin module. It contains even a method for sending emails.

What is even the problem with putting the logic in the views? For small projects with less logic, there is none at all. The problem arises when the logic gets complex and a view.py spans multiple (>= 500) LOC. Also, the code is less portable. Imagine integrating your business logic (e.g. credit card payment) into a different project. You would need to change the existing view to get the functionality for your models.

The general consensus, at least from my researches, is that the logic should reside in the models file (except for code that acts upon the request itself). If this file gets too big, you might be doing too much inside of a single app, and need to split it up into two apps.

My goal is to get clean and readable code, so only time will tell if this approach is really the best, or if any alternatives might be better suited. But here are some alternative approaches I came up with:

  • Divide the models.py file into multiple files containing the logic. Still fat models, but more readable and organized with bigger projects.
  • Define all your logic inside a specific services.py. The methods in there interact with the specific models and you have a clean separation of models, views, and logic. Idea is mainly taken from a Stack Overflow post.
  • Put the business logic inside a separate app module. It is a modular approach which can benefit refactoring or changes to the logic, but code reuse gets worse.

Templates

For template files, I use a modular approach, using template inheritance and blocks. You can see my base template file here, which I will use as an example.

Elements that are only included into a template through {% include ... %} are named with a preceding underscore (like a navigation header or the sidebar). Blocks are also named for better readability, Now say you want to extend the stylesheets block in a view, then you do the following in the sub-template:

{% block stylesheets %}{{ block.super }}
    <link type="text/css" media="all" rel="stylesheet" href="my_new_style.css" />
{% endblock stylesheets %}

Note the block.super instruction, which loads the block from the base template.

Frequently Asked Questions (I ask myself)

  • How do I use imports correctly?
  • Use relative import inside the app.

  • What goes inside an app?
  • That’s a question I often ask myself. Apps should be self-contained and as small (and modular) as possible. Every app encapsulates one functionality. Example: a login app, an events app, a “business logic” app. This follows the Single Responsibility principle.

  • What other resources are there for best practices?
  • The official Django Coding Style guide is a good start.

Share this post: Print this pageTweet about this on TwitterShare on RedditShare on Google+Share on Facebook
Published inPersonalTips and Tricks

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *