docs
  • Overview
  • 🐍 PYTHON
    • Type Hints
    • PEP8 Style Guide for Python Code
    • 🏡Pipenv
    • Pathlib
  • 🕸Django
    • 🗄models
      • 🎯Best Practices
      • 🚦Django Signals
    • ⚙️ settings
    • DRF
      • Serializer
      • Authentication
      • Permissions
      • Viewsets
    • Testing
      • Faker and Factory Boy
    • 🧪Test Coverage
    • 💦Python-Decouple
    • Django Tips:
    • 💾Django ORM Queryset
    • Custom Exceptions
    • Celery
    • Resources
  • Deploy
    • 🚀Django Deployment
    • 🔒Setup SSL Certificate
  • 💾Database
    • MongoDB
  • 🛠️DevOps
    • 🖥Scripting
      • A First Script
      • Loops
      • Test
      • Variables
      • External programs
      • Functions
    • Command Line Shortcuts
    • Basic Linux Commands
    • 🎛Microservices
    • 🐳Docker
      • Docker Commands
      • Docker Compose
      • Django project
    • Kubernates
  • 📝Software IDE
    • EditorConfig
    • Linters
    • VsCode
Powered by GitBook
On this page
  • Q Objects for Complex Queries:
  • Prefetch Related and Select Related:
  • Annotate Querysets to Fetch Specific Values
  • Use Prefetch Objects to Control Your Prefetch Related
  • Define Custom Query Sets and Model Managers for Code Reuse
  • Use of functools.partial in Django
  • Creating many upload_to callables for FileFields
  • transaction.on_commit() callbacks
  • Database functions

Was this helpful?

  1. 🕸Django

Django Tips:

Advanced Django Tips

Q Objects for Complex Queries:

Example models.

from django.db import models


class Ticker(models.Model):
    symbol = models.CharField(max_length=50, unique=True)


class TickerPrice(models.Model):
    ticker = models.ForeignKey(
        Ticker, on_delete=models.CASCADE, related_name="ticker_prices"
    )
    price = models.DecimalField(max_digits=7, decimal_places=2)
    close_date = models.DateField()

Q objects can be combined together with &, representing AND, or |, representing OR. Let’s look at an example query.

today_and_yesterday_prices = TickerPrice.objects.filter(
    models.Q(close_date=today) | models.Q(close_date=yesterday)
)

# Fetching the ticker prices with close dates of today or yesterday.

today_and_yesterday_greater_than_1000 = TickerPrice.objects.filter(
    models.Q(price__gt=1000),
    (models.Q(close_date=today) | models.Q(close_date=yesterday)),
)

# Getting all prices with a close date of today or yesterday
# with a price greater than 1000


today_and_yesterday_greater_than_1000_without_BRK = (
    TickerPrice.objects.filter(
        models.Q(price__gt=1000),
        ~models.Q(ticker__symbol__startswith="BRK"),
        (models.Q(close_date=today) | models.Q(close_date=yesterday)),
    )
)

# Fetching all ticker prices greater than 1000 that don’t start
# with BRK with close dates of either today or yesterday. 
# We added the condition that the ticker’s symbol does not start with BRK

Prefetch Related and Select Related:

Optimize Database Calls with Prefetch Related and Select Related. We use prefetch related when we want to fetch a reverse foreign key or a many to many relationship. We use select related when we want to fetch a foreign key or a one to one relationship.

# prefetch related
apple_with_all_prices = Ticker.objects.prefetch_related(
    "ticker_prices"
).get(symbol="AAPL")


# Select related
latest_prices = TickerPrice.objects.filter(
    close_date=today
).select_related("ticker")

Annotate Querysets to Fetch Specific Values

Annotating a queryset enables us to add attributes to each object in the queryset. Annotations can be a reference to a value on the model or related model or an expression such as a sum or count.

tickers_with_latest_price = Ticker.objects.annotate(
    latest_price=TickerPrice.objects.filter(
        ticker=models.OuterRef("pk")
    )
    .order_by("-close_date")
    .values("price")[:1]
)


tickers_with_latest_price = (
    Ticker.objects.annotate(
        latest_price=TickerPrice.objects.filter(ticker=models.OuterRef("pk"))
        .order_by("-close_date")
        .values("price")[:1]
    )
    .filter(latest_price__gte=50)
)

This queryset fetches all the tickers and annotates each ticker object with a latest_price attribute. The latest price comes from the most recent related ticker price. The OuterRef allows us to reference the primary key of the ticker object. We use order_by to get the most recent price and use values to select only the price. Finally, the [:1] ensures we retrieve only one TickerPrice object.

Use Prefetch Objects to Control Your Prefetch Related

Prefetch objects enable Django developers to control the operation of prefetch related. When we pass in a string argument to prefetch related, we’re saying fetch all of the related objects. A prefetch object lets us pass in a custom queryset to fetch a subset of the related objects.

tickers_with_prefetch = Ticker.objects.all().prefetch_related(
    models.Prefetch(
        "ticker_prices",
        queryset=TickerPrice.objects.filter(
            models.Q(close_date=today)
            | models.Q(close_date=yesterday)
        ),
    )
)

Define Custom Query Sets and Model Managers for Code Reuse

Custom model managers and custom querysets let Django developers add extra methods to or modify the initial queryset for a model. Using these promotes the “don’t repeat yourself” (DRY) principle in software development and promotes reuse of common queries.

import datetime

from django.db import models


class TickerQuerySet(models.QuerySet):
    def annotate_latest_price(self):
        return self.annotate(
            latest_price=TickerPrice.objects.filter(
                ticker=models.OuterRef("pk")
            )
            .order_by("-close_date")
            .values("price")[:1]
        )

    def prefetch_related_yesterday_and_today_prices(self):
        today = datetime.datetime.today()
        yesterday = today - datetime.timedelta(days=1)
        return self.prefetch_related(
            models.Prefetch(
                "ticker_prices",
                queryset=TickerPrice.objects.filter(
                    models.Q(close_date=today)
                    | models.Q(close_date=yesterday)
                ),
            )
        )


class TickerManager(models.Manager):
    def get_queryset(self):
        return TickerQuerySet(self.model, using=self._db)


class Ticker(models.Model):
    symbol = models.CharField(max_length=50, unique=True)

    objects = TickerManager()


class TickerPrice(models.Model):
    ticker = models.ForeignKey(
        Ticker, on_delete=models.CASCADE, related_name="ticker_prices"
    )
    price = models.DecimalField(max_digits=7, decimal_places=2)
    close_date = models.DateField()

In the above code, we’ve created a custom queryset with some of the previously demonstrated queries as methods. We added this new queryset to our custom manager and overrode the default objects manager on the Ticker model with our custom manager. With the custom manager and queryset, we can do the following.

tickers_with_prefetch = (
    Ticker.objects.all().prefetch_related_yesterday_and_today_prices()
)

tickers_with_latest_price = Ticker.objects.all().annotate_latest_price()

Instead of having to write the actual query for each of these examples, we call the methods defined in the custom queryset. This is especially useful if we use these queries in multiple places throughout the codebase.

Use of functools.partial in Django

Creating many upload_to callables for FileFields

from functools import partial

from django.db import models


def user_upload_to(instance, filename, category):
    return f"users/{instance.id}/{category}/{filename}"


class User(models.Model):
    ...
    profile_picture = models.ImageField(
        upload_to=partial(user_upload_to, category="profile"),
    )
    background_picture = models.ImageField(
        upload_to=partial(user_upload_to, category="background"),

transaction.on_commit() callbacks

from django.db import transaction

# With a function


def send_email():
    send_admin_email(
        subject="Author created",
        # ...
    )


transaction.on_commit(send_email)

# With a lambda

transaction.on_commit(
    lambda: send_admin_email(
        subject="Author created",
        # ...
    )
)

# With partial 

transaction.on_commit(
    partial(
        send_admin_email,
        subject="Author created",
        # ...
    )
)

Database functions

from functools import partial

from django.db.models.functions import Substr

FirstChar = partial(Substr, pos=1, length=1)


from example.core.models import Author, FirstChar

Author.objects.annotate(first_char=FirstChar("name")).filter(first_char="C")
Previous💦Python-DecoupleNextDjango ORM Queryset

Last updated 3 years ago

Was this helpful?