What Django best practices is Django breaking?
Tags: django, webdev, python, codequality
Published: 2021-01-04 08:50:37

Django Doctor audits codebases to find and auto fix Django anti-patterns. We checked the Django codebase itself for  anti-patterns that Django documentation suggests we avoid. In short they did very well, but there is small room for improvement.

[Read the audit](https://django.doctor/django/django)

## 1. Hard-coded static asset URL in template

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/gyezk5x05pkjr9d4vfmr.png)

Django best practice suggest avoiding hard-coding static assets and instead using `{% static ...`. However, the Django codebase is hard-coding a font file for [this admin template](https://github.com/django/django/blob/966ed414b2adfc9ecc26a9d529dec99d94262cd9/django/views/templates/default_urlconf.html#L9). This file is used in the [technical 404 response](https://github.com/django/django/blob/4cd77f97a2d30d58841041a73d277ec4bfb7b530/django/views/debug.py#L506). The impact is low: the font might not load.

If you’re unfamiliar: `{% static ...` returns the path the browser can use to request the file. At it's simplest, that would return a path that looks up the file on your local file system. That's fine for local dev but in prod we will likely use [third-party libraries](https://djangopackages.org/grids/g/storage-backends/) such as [whitenoise](http://whitenoise.evans.io/en/stable/django.html) or [django-storages](https://github.com/jschneier/django-storages) to improve performance of the production web server.

There are reasons other than "because S3" for why we use `{% static ...`:

### File revving

Whitenoise has a storage backend `STATICFILES_STORAGE` that performs [file revving](https://www.alainschlesser.com/bust-cache-content-hash/) for cache busting. As a result the file path rendered in the template is renamed: `script.js` may be renamed to `script-23ewd.js`. The `STATICFILES_STORAGE` generates a hash from the file’s contents and renames the file and generates a manifest file that looks like:

`{ 'scripts.js': 'scripts-23ewd.js'}`

This is called [revving](https://www.alainschlesser.com/bust-cache-content-hash/).  `{% static 'script.js' ...` renders `'script-23ewd.js'` in the template because `'script.js'` is looked up in the manifest, resulting in `'script-23ewd.js'` being resolved.

This is for cache busting: when the file content change the file name changes too, so a revved file can have cache headers set to cache forever in the CDN or browser. On the next deployment if the file contents changes so too does the file name being requested and thus the most up to date file is retrieved by the browser. 

So `{% static ...` handles abstracting away cache-busting mechanisms so we can focus on templatey things in the template instead.

_How would you solve these anti-patterns? Try [our Django performance refactor challenge](https://django.doctor/challenge/models)._

### Websites can move home

Given enough time a website can change where it's served from: 

- From subdomain to path: abc.example.com to example.com/abc/ and xyz.example.com to example.com/xyz/. This has happened in a project I was in for SEO reasons.
- Merging multiple repositories into one. This happened to a few projects I was involved with to make development more efficient.

At this point it would be unclear which app's files are served from /static/. Yes a developer can find a replace all of /static/ with /abc/static/ and /abc/static/. But if `{% static ...` was used they would not need to.

So `{% static ...` prevents Javascript and CSS from breaking because _where the website is served from_ changed.

### Serving from S3
Django suggest [not serving static files from the web server in production](https://docs.djangoproject.com/en/3.1/ref/contrib/staticfiles/). One way to do that is to serve the files from S3. If `STATICFILES_STORAGE` serves from S3, then the static domain will likely be different from webserver domain unless some configuration in Cloudfront ensures otherwise.

So in that case using `{% static 'script.js' ...` ultimately renders `https://s3-example-url.com/script.js` instead of `/static/script.js`.

[Read more](https://django.doctor/advice/C7001).

### 2. Django code style: Model method order

Increasing standardization and predictability of your code style helps other developers read your code and ultimately makes it easier and cheaper to work with as it grows.

The [Django Coding Style](https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/#model-style) suggests the following order of inner classes, methods and attributes:

- Field choice tuples
- Database fields
- Custom manager attributes
- class Meta
- `def __str__()`
- `def save()`
- `def delete`
- `def get_absolute_url()`
- Any custom methods

However, Django broke this advice twice. Oddly both are in the same file.:

- [django/db/models/base.py:L517](https://github.com/django/django/blob/f13bedf102a8e65272d8b10e82eecdaab092a470/django/db/models/base.py#L517)
- [django/db/models/base.py:L648](https://github.com/django/django/blob/f13bedf102a8e65272d8b10e82eecdaab092a470/django/db/models/base.py#L648)

Again, the impact is low, so 👏 Django core devs.

[Read more](https://django.doctor/advice/C3002)

### Does your code follow Django best practice?

Over time it's easy for tech debt to slip into your codebase. I can check that for you at [django.doctor](https://django.doctor), or can [review your GitHub PRs](https://github.com/marketplace/django-doctor/):

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/d10epulva9d373jgldhu.png)

Or try out [Django refactor challenges](https://django.doctor/challenge).