Designing Models in Django

In this article, I would like to share my experience and help in designing and formatting your Django code. Hopefully you are already using PEP8, but it is also a good tone to use Django's code formatting style guidelines.

Below you can read the recommendations on Django code formatting.

Model names

Since the model is a class, we always use the CapWords convention, i.e. we start each word in the model name with a capital letter, without underscores. For example User, Permission, ContentType, etc.

For model attributes, we use snake_case, i.e. all words with a small letter separated by an underscore. For example first_name, last_name.

Example:

from django.db import models

class Company(models.Model):
    name = models.CharField(max_length=30)
    vat_identification_number = models.CharField(max_length=20)

Also, always use the singular for the model name: Company instead of Companies. A model definition is a representation of a single object (in this example, a company), not a set.

It's a bit confusing at first if you're used to thinking in terms of database tables. Eventually the model will be translated into a table. It is quite correct to use the plural for the name of the table, because a table is a collection of objects.

The sequence of placement within the model class

Django's recommendations for the sequence of placement of internal classes, methods, and attributes are as follows:

  • if choices are defined for a model field, define each choice as a tuple of tuples, using uppercase letters;
  • model data fields;
  • user manager attributes;
  • class Meta;
  • def __str__();
  • def save();
  • def get_absolute_url();
  • the rest of your class methods.

Example:

from django.db import models
from django.urls import reverse

class Company(models.Model):
    # CHOICES
    PUBLIC_LIMITED_COMPANY = 'PLC'
    PRIVATE_COMPANY_LIMITED = 'LTD'
    LIMITED_LIABILITY_PARTNERSHIP = 'LLP'
    COMPANY_TYPE_CHOICES = (
        (PUBLIC_LIMITED_COMPANY, 'Public limited company'),
        (PRIVATE_COMPANY_LIMITED, 'Private company limited by shares'),
        (LIMITED_LIABILITY_PARTNERSHIP, 'Limited liability partnership'),
    )

    # DATABASE FIELDS
    name = models.CharField('name', max_length=30)
    vat_identification_number = models.CharField('VAT', max_length=20)
    company_type = models.CharField('type', max_length=3, choices=COMPANY_TYPE_CHOICES)

    # MANAGERS
    objects = models.Manager()
    limited_companies = LimitedCompanyManager()

    # META CLASS
    class Meta:
        verbose_name = 'company'
        verbose_name_plural = 'companies'

    # TO STRING METHOD
    def __str__(self):
        return self.name

    # SAVE METHOD
    def save(self, *args, **kwargs):
        do_something()
        super().save(*args, **kwargs)  # Call the "real" save() method.
        do_something_else()

    # ABSOLUTE URL METHOD
    def get_absolute_url(self):
        return reverse('company_details', kwargs={'pk': self.id})

    # OTHER METHODS
    def process_invoices(self):
        do_something()

Reverse relationship

родственное_имя

This attribute is used in the ForeignKey field. It allows you to specify a normal and convenient name for the reverse relationship.

Example:

class Company:
    name = models.CharField(max_length=30)

class Employee:
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='employees')

This code means that the model object Company will have an attribute employees that returns QuerySet with all employee objects of the specified company.

google = Company.objects.get(name='Google')
google.employees.all()

Empty and null fields

The difference between empty and null fields has already been discussed, but I'll repeat:

  • null: related to database, determines whether a given database column will take null values or not.
  • blank: related to Django validation, used during form validation when calling form.is_valid ().

Do not use the value null=True for text fields that are optional. Otherwise you will get two possible values for "no data": "None" and an empty string. Having two possible values for "no data" is redundant. The Django convention should use the empty string, not NULL.

# The default values of `null` and `blank` are `False`.
# значение по умолчанию для null и blank - False
class Person(models.Model):
    name = models.CharField(max_length=255)  # обязательное
    bio = models.TextField(max_length=500, blank=True) # необязательное (не используйте null=True для текстовых полей)
    birth_date = models.DateField(null=True, blank=True) # необязательное (здесь нужно использовать оба атрибута null и blank)

Closure

Model definition is an important part of your application. You can see a detailed discussion of the guidelines in the Django's Coding Style documentation.

Back to Top