In a Django model, which field options does the database enforce, and which does only model validation check?
answer
- what appears in the CREATE TABLE
- NOT NULL, UNIQUE, VARCHAR(n)
- blank, choices, validators stay in Python
- save() skips full_clean()
- db_index adds no rule
basics
~10 sThe database enforces what reaches the schema: null=False (NOT NULL), unique=True (a unique constraint), CharField max_length (VARCHAR length) and db_default. blank, choices, validators and unique_for_date are checked only by full_clean() and forms.
solid answer
~40 sAsk whether the option changes the SQL. `null=False` becomes `NOT NULL`; `unique=True` becomes a unique constraint and is also checked by `validate_unique()`; a `CharField`'s `max_length` becomes `VARCHAR(n)` and a `MaxLengthValidator`; `db_default` becomes a column `DEFAULT`; `db_index=True` only builds an index and enforces nothing. The rest lives in Python: `blank`, `choices`, `validators`, `unique_for_date` and a `TextField`'s `max_length` are checked only when `full_clean()` runs — in `ModelForm`s, the admin, or when you call it — and a plain `default` is applied when the instance is created in Python. `save()` does not call `full_clean()`, and `update()` and `bulk_create()` skip it too, so any rule that must hold for every row needs a database-level form: a column option or a `Meta` constraint.
go deeper
Name the options that change the table — null, unique, CharField max_length — and know that blank, choices and validators are checked by forms and full_clean().
Walk through each option's schema and validation effect and list the write paths that skip full_clean(), such as save(), update() and bulk_create().
Move rules that must always hold into constraints, handle IntegrityError from unique races, and keep validators for friendly errors.
Set the policy for which invariants live in the database versus application code when several services or imports write the same tables.
## The dividing line Every option on a Django model field ends up in one of two places: - **The schema** — the `CREATE TABLE`/`ALTER TABLE` Django generates. The database enforces these on **every** write, whoever makes it. - **Python validation** — `Model.full_clean()`, which `ModelForm`s and the admin call, and which you may call yourself. These run only on paths that validate. `Model.save()` does **not** call `full_clean()`, and queryset `update()`, `bulk_create()`, data migrations and raw SQL bypass model validation entirely. So the practical question for any rule is: which layer holds it? ## Option by option | option | in the schema | in model validation | |---|---|---| | `null=False` (default) | `NOT NULL` | rejects `None`, unless `blank=True` skips the empty value | | `unique=True` | unique constraint, which also creates an index | `validate_unique()` checks for duplicates | | `CharField(max_length=n)` | `VARCHAR(n)` (PostgreSQL and SQLite also allow no limit) | `MaxLengthValidator` | | `db_default` (5.0+) | column `DEFAULT` | skipped for unsaved defaults | | `db_index=True` | an index, no rule | nothing | | `default` | nothing on the column | nothing; applied when the instance is built | | `blank` | nothing | allows empty values | | `choices` | nothing | rejects values outside the choices | | `validators` | nothing | runs each validator | | `unique_for_date` | nothing | checked by `validate_unique()` | | `TextField(max_length=n)` | nothing | nothing; only the form widget's `maxlength` | A few of these deserve a note: - **`unique=True`** is enforced twice. Validation gives a friendly error, but two concurrent requests can both pass it; the database constraint is what actually prevents the duplicate, surfacing as `IntegrityError` from `save()`. Setting `db_index=True` alongside `unique=True` is redundant. - **`db_index=True`** speeds lookups but allows any value. Django's documentation now prefers `Meta.indexes`, and says `db_index` may be deprecated in future. - **`default`** is Python-only. When a migration adds a column with a `default`, Django uses it to fill existing rows and then drops the `DEFAULT` from the column, so rows inserted outside Django get nothing. `db_default` keeps a real `DEFAULT`. - **`FileField`** is stored as a `VARCHAR` holding the file's name, `max_length=100` by default, so long upload paths can exceed it. ## The menu item, annotated ```python from decimal import Decimal from django.core.validators import MinValueValidator from django.db import models class MenuItem(models.Model): name = models.CharField(max_length=120, unique=True) # VARCHAR(120) + UNIQUE description = models.TextField(blank=True) # blank: validation only price = models.DecimalField( max_digits=7, decimal_places=2, validators=[MinValueValidator(Decimal("0.01"))], # validation only ) diet = models.CharField( max_length=5, choices=[("none", "No restriction"), ("vegan", "Vegan")], # validation only default="none", # Python only ) photo = models.FileField(upload_to="menu/", blank=True) # VARCHAR(100) ``` A nightly import that calls `MenuItem.objects.bulk_create(...)` can therefore store a price of `0` and a diet of `"keto"`, but not a duplicate `name`, a missing `name`, or a 200-character `name` on PostgreSQL. ## Checking a model quickly The fastest way to settle an argument about what the database enforces is to read the SQL Django will run: ```bash python manage.py sqlmigrate menu 0001 ``` Whatever appears in that `CREATE TABLE` — `NOT NULL`, `UNIQUE`, `varchar(120)`, a `DEFAULT`, a `CHECK` — the database enforces. Anything you declared on the model that is missing from it lives only in Python validation. ## Moving a rule into the database When a Python-only rule must hold for every row, express it in the schema: 1. Tighten the column: `null=False`, `unique=True`, a narrower `max_length`. 2. Add a `CheckConstraint` or `UniqueConstraint` in `Meta.constraints` for value ranges, allowed values or conditional uniqueness. Since Django 4.1, `full_clean()` also validates those constraints, so forms still get friendly errors. 3. Keep the Python validator too when you want a clear message before the database refuses the write. ## Interview framing The crisp answer is: *the database enforces what changes the SQL; everything else is advice to validation*. Then name the paths that skip validation — `save()` on its own, `update()`, `bulk_create()`, raw SQL — because that is where the difference bites.
- If unique=True is enforced by the database, why does validation also check it?`validate_unique()` gives a readable form error before any write. It cannot prevent a race — two requests can both pass the check — so the database's unique constraint is the real guarantee, and code that saves must still handle `IntegrityError`.
- Does a model field's default end up in the database schema?No. Django applies `default` when the instance is created in Python; a migration that adds the column uses it to fill existing rows and then drops the column `DEFAULT`. Use `db_default` (Django 5.0+) when the database itself should supply the value.
saying these in an interview costs you the question
- save() runs full_clean(), so validators always apply
- choices and validators become database CHECK constraints
- db_index=True prevents duplicate values
- a field's default is written into the column DEFAULT
- TextField(max_length=...) limits the column length