<aside> š This style guide is publicly accessible, linked to from the engineering site.
</aside>
Models and relationships are the most important part of structuring a Django project. Database schemata are harder than most other things to change once in use.
related_name
for all ForeignKey
fields, becauseā¦
foo_set
names are not particularly informative.
When we donāt prefix model names with the app name (e.g: āInviteā instead of āEventInviteā) we can quickly run into collisions across apps. Specifying our own related_name
lets us add informative prefixes when we need them.
For example, foreign keys back to auth.User
should typically be prefixed with the app name, so that models registration.Invite
and events.Invite
do not collide on user.invite_set
.
Lastly, adding a custom related_name
specifies that this is a āpublicā model that itās okay to play around with from other apps. Put another way, it is acceptable to omit the related_name
for denormalised data in order to signal that it is not the canonical source, or a āpublicā API.
models.py
should contain only models, constants required for models, and imports.
The only exception to this is functions passed to the default
kwarg on fields, as for migrations to work the default must be importable.
Use database-level constraints (e.g: unique_together
) where possible, instead of programatically checking something does not exist before inserting it. If this does not appear possible consider refactoring the data model or schema to make this possible.
Donāt prefix model names with the name of the app.
null=False
and blank=False
should not be explicitly specified. These are the defaults, and by leaving them unspecified, fields that are nullable are more obvious.
Avoid lazily creating model instance as it can lead to deadlock situations and makes the current state of the data model at any time more difficult to predict.
Do not use @classmethod
or @staticmethod
methods on models; move these to a model manager, without the decorator.
Empty models.py
files should not be committed.
Denormalised counts should be updated with .save(..)
and .delete()
model instance methods, and should be calculated using models.F
and .update()
.
save(..)
methods that need to perform side-effects on model creation should follow this pattern:
def save(self, *args, **kwargs):
created = self._state.adding
super(MyModel, self).save(*args, **kwargs)
if created:
# Do first-time things
If the side-effect is to create a related model, consider using django-auto-one-to-one instead of overriding .save()
.
Do not override __repr__
āitās a standard format across Django models.
__str__
.__str__
with the @python_2_unicode_compatible
decorator on the class.Try to avoid having two āmodesā of instance on the same model, each with different fields. This will usually result in conditionals everywhere on the site. Try having two separate models instead, perhaps hanging off a common root model.
Avoid nullable fields where possible.
When the database in use is Postgres, as it typically is at Thread, a reasonable place to use nullable fields more is for fast migrations on large database tables. Nullable fields can be added in a cheap Ī(1) operation, whereas non-nullable fields with a default are an Ī(n) operation to add. We may therefore decide for migration performance reasons, to start with a nullable field, populate data outside of the migration, and then add the NOT NULL
constraint later.
No nullable CharField
or TextField
. The empty string indicates a lack of value, and having two empty values can confuse conditional checking. The exception to this rule is when the empty string and NULL
are two distinct values in the domain being modelled.