Django cookbook · Intermediate

Enforce rules with database constraints

Validation in forms and serializers isn't enough: scripts, imports and race conditions still create bad data.

Create a free account to save this page, take notes and track your progress. Sign in

Approach

Declare rules in Meta.constraints so the database enforces them for every write path. Conditional unique constraints express rules like "one active subscription per user".

Solution

from django.conf import settings
from django.db import models
from django.db.models import Q
from django.db.models.functions import Lower


class Subscription(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    plan = models.CharField(max_length=20)
    status = models.CharField(max_length=20, default="active")
    seats = models.PositiveIntegerField(default=1)
    starts_on = models.DateField()
    ends_on = models.DateField(null=True, blank=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["user"], condition=Q(status="active"), name="one_active_subscription_per_user"
            ),
            models.CheckConstraint(condition=Q(seats__gte=1, seats__lte=500), name="seats_in_range"),
            models.CheckConstraint(
                condition=Q(ends_on__isnull=True) | Q(ends_on__gt=models.F("starts_on")), name="ends_after_start"
            ),
        ]


class Team(models.Model):
    name = models.CharField(max_length=100)

    class Meta:
        constraints = [models.UniqueConstraint(Lower("name"), name="unique_team_name_ci")]

Pitfalls

  • Relying on unique=True alone when uniqueness depends on another field's value.
  • Not handling IntegrityError in the view, turning a conflict into a 500 error.
  • Adding constraints to tables with existing bad data; clean the data in a migration first.

Variations

  • Model.full_clean() validates constraints in Python too, so forms show friendly errors.
  • On PostgreSQL, ExclusionConstraint prevents overlapping date ranges (bookings).

Further reading: Model constraints

Reference: https://docs.djangoproject.com/en/6.0/ref/models/constraints/

More Django recipes

Was this page helpful?