How Django Generative Fields Simplify Database Operations
There is a category of fields in a database that you never want to calculate manually at query time. Things like a full name derived from first and last name, a total price calculated from quantity and unit price, or a slug generated from a title. In older Django patterns, you would either store these as regular fields and keep them in sync through signals or overridden save methods, or you would calculate them in Python every time you needed them. Both approaches have tradeoffs. Django’s GeneratedField, introduced in Django 5.0, offers a cleaner solution by pushing the computation down to the database itself.
Thank you for reading this post, don't forget to subscribe!What Is a GeneratedField?
A GeneratedField is a model field whose value is always computed from other fields in the same row, using a database-level expression. The value is never set by application code — the database calculates it automatically whenever a row is inserted or updated.
This mirrors what relational databases call generated columns, a feature supported in PostgreSQL, MySQL, MariaDB, and SQLite (with some variation). Django’s GeneratedField is the ORM-level abstraction that lets you define these columns in a standard Django model without writing raw SQL.
The Two Types: STORED and VIRTUAL
Django’s GeneratedField supports two modes, controlled by the db_persist argument:
STORED (db_persist=True): The computed value is physically stored in the database table alongside the other columns. It is calculated when the row is written and stored as a real value. Reading it is fast because no computation happens at read time. This is the equivalent of a STORED generated column in SQL.
VIRTUAL (db_persist=False): The value is computed on the fly every time the row is read. It is never stored on disk. This saves storage space but adds a small computation cost on every read. Not all databases support virtual generated columns — SQLite does not, for example.
A Simple Example
Say you have a model for products and want to keep a discounted_price field that is always 10% off the regular price:
from django.db import models
from django.db.models import F, ExpressionWrapper, DecimalField
class Product(models.Model):
name = models.CharField(max_length=200)
price = models.DecimalField(max_digits=10, decimal_places=2)
discounted_price = models.GeneratedField(
expression=ExpressionWrapper(
F("price") * 0.9,
output_field=DecimalField(max_digits=10, decimal_places=2),
),
output_field=DecimalField(max_digits=10, decimal_places=2),
db_persist=True,
)
With this setup, every time a Product row is saved with a new price, the database automatically recalculates and stores discounted_price. Your application code never needs to compute it or remember to update it. You just read product.discounted_price and get the correct value.
Key Constraints to Know
There are some important rules that come with GeneratedField:
You cannot write to it. Attempting to set a value on a GeneratedField will raise an error. The database owns this field entirely.
The expression can only reference columns in the same row. You cannot reference other tables, subqueries, or non-deterministic functions like the current timestamp.
Migrations are required. Like any model field change, adding a GeneratedField requires creating and running a migration. The database column is created with the appropriate generated column definition.
Database support varies. PostgreSQL and MySQL have the broadest support. SQLite supports STORED but not VIRTUAL. Always check what your database version supports before using it.
When to Use It
GeneratedField is a good fit when a value is always derivable from other columns in the same row, when you find yourself writing signals or overriding save() just to keep a field in sync, or when you want to ensure consistency at the database level regardless of what application code does.
It is not a fit for computations that depend on related models, external data, or anything that changes independently of the row itself.
Django’s GeneratedField is one of those additions that quietly solves a common pain point. By letting the database handle derived values instead of relying on application-level logic, you get better consistency, less code to maintain, and the full performance benefits of database-native computed columns. If you are on Django 5.0 or later and find yourself manually syncing a field that is always derived from other fields, GeneratedField is worth reaching for.
