Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Python Django database constraints: enforce receipt identity per owner

Last updated: 30 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

A database uniqueness constraint rejects a conflicting stored identity even when application validation was bypassed.

Download Python source kit

Operation contract

The owned model defines receipt identity as owner_id plus receipt_id and requires a nonnegative amount at the database level. Two different owners may store the same receipt ID; a second row for the same owner is rejected. A bulk_create with a negative amount is rejected too, despite skipping model-level cleaning. Each expected failure is inside its own atomic block and caught outside it, leaving earlier accepted rows readable.

Failure and ownership boundary

A declared owner field is not authentication. A request must obtain its owner from a trusted principal and filter reads as well as writes. Django REST Framework creation ownership: assign the owner from the principal, Django REST Framework list visibility: filter the principal’s rows before paging and Django atomic transactions: rollback the batch and defer callbacks are all required for a service policy. This fixture checks an owned in-memory SQLite schema, not another engine’s isolation or production migration history.

Tested environment

Dependency check: this program was executed on CPython 3.14.6 with Django==5.2.17. Install these versions in a separate virtual environment. The download includes the recorded environment snapshot; no third-party package is part of the website runtime.

Working program

python
from django.conf import settings
settings.configure(INSTALLED_APPS=[], DATABASES={"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}}, DEFAULT_AUTO_FIELD="django.db.models.AutoField")
import django
django.setup()
from django.db import connection, models, transaction, IntegrityError

class OwnedReceipt(models.Model):
    owner_id = models.PositiveIntegerField()
    receipt_id = models.CharField(max_length=6)
    amount = models.IntegerField()
    class Meta:
        app_label = "owned_receipt_fixture"
        constraints = [models.UniqueConstraint(fields=["owner_id", "receipt_id"], name="owner_receipt_identity"),
                       models.CheckConstraint(condition=models.Q(amount__gte=0), name="owned_amount_nonnegative")]

with connection.schema_editor() as schema:
    schema.create_model(OwnedReceipt)
try:
    OwnedReceipt.objects.create(owner_id=1, receipt_id="R-0041", amount=125)
    OwnedReceipt.objects.create(owner_id=2, receipt_id="R-0041", amount=250)
    try:
        with transaction.atomic():
            OwnedReceipt.objects.create(owner_id=1, receipt_id="R-0041", amount=500)
    except IntegrityError:
        print("same-owner duplicate rejected")
    try:
        with transaction.atomic():
            OwnedReceipt.objects.bulk_create([OwnedReceipt(owner_id=1, receipt_id="R-0042", amount=-1)])
    except IntegrityError:
        print("bulk negative amount rejected")
    print("stored:", list(OwnedReceipt.objects.order_by("owner_id").values_list("owner_id", "amount")))
finally:
    connection.close()

Output

Output
same-owner duplicate rejected
bulk negative amount rejected
stored: [(1, 125), (2, 250)]

Costs and limits

Constraint enforcement has engine/index-dependent costs; application preflight checks are not a replacement for it under concurrent writers. The fixture contains two rows and provides no measured production plan. CharField length and receipt spelling also need explicit schema validation; the uniqueness check alone does not enforce that grammar.

Common Mistakes

  • Application full_clean is not automatically called by every write path.
  • Uniqueness per owner does not authorize a received owner_id.

Connected lessons

Django REST Framework creation ownership: assign the owner from the principal, Django REST Framework list visibility: filter the principal’s rows before paging, Django atomic transactions: rollback the batch and defer callbacks, Django migrations: generate and apply an owned application schema.

python
django-owner-constraints
Storage details