Using GeneratedField with Postgres

I have an app using the new (in Django 5) GeneratedField field. Here’s the model:

class Artist(models.Model):
    first_name = models.CharField(max_length=50, verbose_name="First Name")
    middle_name = models.CharField(max_length=50, verbose_name="Middle Name", null=True, blank=True)
    last_name = models.CharField(max_length=50, verbose_name="Last Name")
    full_name = models.GeneratedField(
        expression=Concat("first_name", models.Value(" "), "middle_name", models.Value(" "),"last_name"),
        output_field=models.CharField(max_length=100),
        db_persist = True,
        verbose_name="Full Name"
    )
    birth_year = models.IntegerField(verbose_name="Year of Birth")
    nationality = models.CharField(max_length=100, verbose_name="Nationality")

    def __str__(self):
        return f"{self.full_name}"

When attempting to deploy to Fly.io, the deployment fails with the error:

django.db.utils.ProgrammingError: generation expression is not immutable

which causes the the release command to fail. It looks like the GeneratedField is the culprit. If I remove the GeneratedField, the app deploys without error. According the the Model Field Reference, " PostgreSQL requires functions and operators referenced in a generated column to be marked as IMMUTABLE ." Is there a way to successfully use a GeneratedField with Postgres?

PostgreSQL requires functions and operators referenced in a generated column to be marked as IMMUTABLE. Unfortunately, PostgreSQL’s CONCAT is not considered an immutable, that’s why in Django 5.1 we’ve changed the implementation of Django’s Concat() to use ||

2 Likes

Thanks for the info – certainly answers my question. Are you aware of a workaround pending the release of 5.1? I’m one of those “non-expert users” to whom Paolo refers.

You can copy the new implementation of Concat() and ConcatPair() into your code, and use it instead of builtin version:

I wrote some articles about GeneratedField, and one specific for PostgreSQL with a text concatenation example
https://www.paulox.net/2023/11/24/database-generated-columns-part-2-django-and-postgresql/#a-calculated-concatenated-field

Let me know if it’s clear for you and if it solves your problem.

2 Likes

Gentlemen, thank you so much for your help. I’ve tried both solutions. The good news is both do solve the deployment problem – the app deploys without error. However, when saving a model instance that uses a GeneratedField (using the Django Admin), I get a new error:

IntegrityError at /admin/art/artist/add/
null value in column "full_name" of relation "art_artist" violates not-null constraint
DETAIL:  Failing row contains (10, Pablo, null, Picasso, null, 1889, Spanish).
Request Method:	POST
Request URL:	http://curated-arts.fly.dev/admin/art/artist/add/
Django Version:	5.0.1
Exception Type:	IntegrityError
Exception Value:	
null value in column "full_name" of relation "art_artist" violates not-null constraint
DETAIL:  Failing row contains (10, Pablo, null, Picasso, null, 1889, Spanish).
Exception Location:	/usr/local/lib/python3.12/site-packages/psycopg/cursor.py, line 732, in execute
Raised during:	django.contrib.admin.options.add_view
Python Executable:	/usr/local/bin/python
Python Version:	3.12.1
Python Path:	
['/code',
 '/usr/local/bin',
 '/usr/local/lib/python312.zip',
 '/usr/local/lib/python3.12',
 '/usr/local/lib/python3.12/lib-dynload',
 '/usr/local/lib/python3.12/site-packages']
Server time:	Sat, 20 Jan 2024 20:56:41 +0000

I assume the not null constraint is a default setting for GeneratedField? Is there a way to remove the not null constraint?