python
37 lines · 7 steps
Enforcing one default address per user in Django
A Django model uses a partial unique constraint so each user can have only one default address of each kind.
Explained by
highlit
1from django.db import models
2from django.db.models import Q
3from django.conf import settings
4
5
6class Address(models.Model):
7 class Kind(models.TextChoices):
8 SHIPPING = "shipping", "Shipping"
9 BILLING = "billing", "Billing"
10
11 user = models.ForeignKey(
12 settings.AUTH_USER_MODEL,
13 on_delete=models.CASCADE,
14 related_name="addresses",
15 )
16 kind = models.CharField(max_length=16, choices=Kind.choices)
17 line1 = models.CharField(max_length=255)
18 line2 = models.CharField(max_length=255, blank=True)
19 city = models.CharField(max_length=120)
20 postal_code = models.CharField(max_length=20)
21 country = models.CharField(max_length=2)
22 is_default = models.BooleanField(default=False)
23
24 class Meta:
25 constraints = [
26 models.UniqueConstraint(
27 fields=["user", "kind"],
28 condition=Q(is_default=True),
29 name="unique_default_address_per_user_kind",
30 ),
31 ]
32 indexes = [
33 models.Index(fields=["user", "kind"]),
34 ]
35
36 def __str__(self):
37 return f"{self.get_kind_display()} address for {self.user_id}"
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A UniqueConstraint with a condition becomes a partial index, enforcing uniqueness only for rows that match.
- 2TextChoices gives you a validated enum plus a human-readable display method for free.
- 3Pushing business rules like 'one default per kind' into the database prevents race conditions application code can't reliably catch.
Related explainers
python
import hashlib import json from fastapi import APIRouter, Request, Response, Depends, HTTPException, status
HTTP ETag caching in a FastAPI route
http-caching
etag
conditional-requests
Intermediate
9 steps
python
from typing import Any _MISSING = object()
Recursively diffing two JSON structures
recursion
sentinel
tree-traversal
Intermediate
8 steps
python
from contextlib import contextmanager from typing import Iterator import psycopg2
Streaming Postgres rows with a server-side cursor
generators
context-managers
database-streaming
Intermediate
7 steps
python
import wave import os from dataclasses import dataclass
Reading WAV metadata into a dataclass
dataclass
audio
file-io
Beginner
5 steps
python
from pathlib import Path from collections import defaultdict from PIL import Image
Finding near-duplicate images by perceptual hash
perceptual-hashing
clustering
hamming-distance
Intermediate
9 steps
python
from starlette.middleware.base import BaseHTTPMiddleware from starlette.requests import Request from starlette.responses import JSONResponse, Response from starlette.status import HTTP_413_REQUEST_ENTITY_TOO_LARGE
Enforcing a max request body size in FastAPI
middleware
streaming
request-limits
Advanced
6 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/enforcing-one-default-address-per-user-in-django-explained-python-b96b/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.