Doing more with your Django models

Share this article

So you have a Django app, but sometimes you find the Django models too constraining. We will guide you through using Django models to get more out of them. This is an intermediate tutorial, as some familiarity with Django is assumed. For example, we assume you know how to write a basic Django model, you know how to override Python methods, as well as how .filter and .exclude work.

We will talk about these topics

  1. Proxy Models
  2. Overriding .save
  3. Using signals
  4. Optimizing your DB access using .extra
  5. Advanced lookups using Q objects
  6. Aggregation and Annotation
  7. Using F() expressions

Lets look at some common operations you may want to perform using Django and how the above Django functionality will help you achieve them.

How can I get two Python representation of the same Database table?

You may want to have two model classes corresponding to a single database table. For example, admin.site.register allows a Model to be registered only once. However, you may want the same model twice in the Admin area. Proxy models can help you do that!

from django.contrib.auth.models import User

class NewUser(User):
    class Meta:
        proxy = True

Now in your admin.py you can register NewUser again and customize your ModelAdmin. (For example, if you want to show only some of the fields, add a custom ordering and so on).

How can I take action before saving a model to database?

Sometime you may have some denormalized data. Consider this model:

class Poll(models.Model):
    ###...
    num_choices = models.PositiveIntegerField()

class Choice(models.Model):
    poll = models.ForeignKey(Poll)
    ###...

You want to increment the num_choices before saving Choice. You can do that by overriding .save like this.

def save(self, *args, **kwargs):
    self.poll.num_choices += 1
    self.poll.save()
    super(Choice, self).save(*args, **kwargs)

How can I take action before saving the models to database if I didn’t write the model?

Overriding .save is great when you are writing all the models. However for example you have a Subscription model and when someone sings up they are assigned a subscription. However since you didn’t write the User model, you can not override the .save model.

Django emits signals before taking any action. You can connect your functions to signals to take action when interesting stuff happens. Django comes with two signals pre_save and post_save which you can connect to.

from django.db.models.signals import pre_save
from django.contrib.auth.models import User

def subscription_handler(**kwargs):
    #Do something with the Subscription model


pre_save.connect(subscription_handler, sender=User, dispatch_uid="subscription_handler")

How can I get related objects without hitting the database many times?

Assume we have these models:

class Subject(models.Model):
    ###...

class Score(models.Model):
    ###...
    subject = models.ForeignKey(Subject)
    score = models.PositiveIntegerField()

Now you are iterating over a Subject queryset, and you want the sum of all the Score objects which have a foreign key to current object. You can do this by getting individual Score objects and then summing them in Python, but it would be faster to do that in the database. Django has a method .extra which allows you to insert arbitrary clauses in the sql generated by the queryset. For example here you can do

Subject.objects.extra(select={"total_scores": "select sum(score) from poll_score where poll_score.subject_id = poll_subject.id"})

assuming that the app is called poll for which the default names for tables are poll_subject and poll_score.

How can you compose OR, NOT and other SQL operations?

By default Django will AND all criteria passed to the filtering methods. If you want to use OR/NOT operator, you will need to use Q objects.

We have a model like:

class Score(models.Model):
    ###...
    subject = models.ForeignKey(Subject)
    score = models.PositiveIntegerField()
    date = models.DateField()

So, if you want all Score objects for Physics which have either score > 95 or are in 2012.

    criteria = Q(subject__name="Physics") & (Q(score__gt=95)|Q(date__year=2012))

We used the double underscore notation to apply filters and joined them together using boolean operators. You can pass them to .filter. (Or to .exclude)

Score.objects.filter(criteria)

How can I get group_by type of operations?

Django provides two methods on its querysets – .aggregate and .annotate. Aggregates convert the queryset in a dictionary on name, value pairs.

E.g., if you want the maximum, minimum, and average of Score objects. You can get them as

from django.db.models import Avg, Max, Min

Score.objects.all().aggregate(Max('score'), Avg('score'), Min('score'))

For more, see the guide on aggregation

How can I compare within rows?

Django provides F objects which are used to create queries which compare within rows.

We have a model like this:

class Department(models.Model):
    ##...
    num_employees = models.PositiveIntegerField()
    num_managers = models.PositiveIntegerField()

You want to find all departments which have more managers than employees.

from django.db.models import F  
Department.objects.filter(num_managers__gt=F('num_employees'))

F objects support addition, subtraction, multiplication, division so you can do things like

Department.objects.filter(num_employees__lt=F('num_managers')*2)

Frequently Asked Questions (FAQs) about Django Models

What are Django models and why are they important?

Django models are a single, definitive source of information about your data. They contain the essential fields and behaviors of the data you’re storing. Generally, each model maps to a single database table. Models are important because they help Django to abstract the underlying database system, allowing you to use a simple Python API to interact with it. This means you can switch between different database systems with minimal code changes.

How can I use Django models to define my database schema?

Django models are used to define the database schema by defining the fields and their types. Each field is specified as a class attribute, and each attribute maps to a database column. Django uses the field types to determine how to interact with the database.

What is a proxy model in Django and when should I use it?

A proxy model is a type of model inheritance in Django. It allows you to create a proxy for the actual model, where you can change things like the default ordering, default manager, or add new methods. It’s useful when you want to modify the Python-level behavior of a model, without changing the model’s fields.

How can I use Django models in a Domain-Driven Design (DDD) approach?

In a DDD approach, the focus is on the domain logic and the business rules. Django models can be used to encapsulate these rules and logic. You can define methods in your models that implement business rules, and use Django’s ORM to handle the persistence of these rules in the database.

How can I create multiple user types using Django models?

Django provides a built-in User model for authentication. However, if you need to create multiple user types with different attributes, you can extend the User model and add additional fields. Alternatively, you can use a OneToOneField to a Profile model, which can contain the additional fields and behaviors.

How can I save a legacy project using Django and DDD?

Saving a legacy project using Django and DDD involves refactoring the existing codebase to align with DDD principles. This includes identifying the domain models, encapsulating the business logic in these models, and using Django’s ORM to handle the persistence.

How can I use Django models to handle complex queries?

Django’s ORM provides a powerful query API that allows you to perform complex database queries using Python code. You can chain together filter conditions, perform joins, aggregate data, and more. All of this can be done without writing a single line of SQL.

How can I optimize the performance of my Django models?

There are several ways to optimize the performance of Django models. This includes using the select_related and prefetch_related methods to reduce database queries, indexing your database fields, and using Django’s caching framework.

How can I use Django models to handle relationships between tables?

Django provides several field types to handle relationships between tables, including ForeignKey, OneToOneField, and ManyToManyField. These fields allow you to model one-to-many, one-to-one, and many-to-many relationships, respectively.

How can I validate data using Django models?

Django provides a powerful form system that includes automatic data validation. You can also define custom validation rules in your models by overriding the clean method. This method is called when you call the model’s full_clean method.

Shabda RaajShabda Raaj
View Author

This is a post by Shabda Raaj of Agiliq. Their blog talks about many technical topics including Django.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week