Opened 48 minutes ago

Last modified 30 minutes ago

#37208 new Bug

db_persist has problem with postgres

Reported by: Twili Spar Owned by:
Component: Database layer (models, ORM) Version: 6.0
Severity: Normal Keywords: db_persist, GeneratedField
Cc: Twili Spar Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Environment:

Python version: 3.13.3
Django version: 6.0.7
psycopg version: 3.2.12
Postgres version: 18.1


Explain the bug:

Here is my code:

tmp = models.GeneratedField(
        db_persist=False,  # In here, I've set the column type as virtual
        expression=models.Q(standard_symbol_tmp__isnull=False),
        null=True,
        output_field=models.BooleanField(),
    )

And when I do makemigrations all things are good, but on migrating, django says:

SystemCheckError: System check identified some issues:

ERRORS:
symbol.Symbol.tmp: (fields.E221) PostgreSQL does not support non-persisted GeneratedFields.
        HINT: Set db_persist=True on the field.

While I know that Postgres supports the VIRTUAL and STORED generated columns, here is the postgres release note ref:
https://www.postgresql.org/about/news/postgresql-18-released-3142/

While the migration file is created and is like:

class Migration(migrations.Migration):

    # ...rest of the code ...

    operations = [
        migrations.AddField(
            model_name="symbol",
            name="tmp",
            field=models.GeneratedField(
                db_persist=False,
                expression=models.Q(("standard_symbol_tmp__isnull", False)),
                null=True,
                output_field=models.BooleanField(),
            ),
        ),
    ]

And when I set db_persist to True AND DO NOT TOUCH THE migration file and migrate, all things are good, column creates as VIRTUAL in postgres, NOT STORED.

Change History (1)

comment:1 by Twili Spar, 45 minutes ago

A quick fix

I played with the migration files, when I changed the db_persist=True in my models.py file, I knew that if I run makemigrations one more time, django will create a new migration file for me which changes my VIRTUAL column to STORED, that I didn't wanted, so I did like:

  1. Updated the models.py, db_persist=True
  2. Run makemigrations one more time to have a migration file like:
    operations = [
            migrations.AlterField(
                model_name="symbol",
                name="tmp",
                field=models.GeneratedField(
                    db_persist=True,  # 👈 This would change the VIRTUAL to STORED in DB, which is not good
                    expression=models.Q(("standard_symbol_tmp__isnull", False)),
                    null=True,
                    output_field=models.BooleanField(),
                ),
            ),
        ]
    
  3. I didn't want to run migrate with --fake, so I migration file to
    operations = [
            migrations.SeparateDatabaseAndState(
                state_operations=[
                    migrations.AlterField(
                        model_name="symbol",
                        name="tmp",
                        field=models.GeneratedField(
                            db_persist=True,
                            expression=models.Q(("standard_symbol_tmp__isnull", False)),
                            null=True,
                            output_field=models.BooleanField(),
                        ),
                    ),
                ],
                database_operations=[],  # 👈 nothing runs against the DB
            ),
        ]
    

Then when I run the migrate, Django's migration state updated and my DB DIDN'T CHANGED.

Last edited 30 minutes ago by Twili Spar (previous) (diff)
Note: See TracTickets for help on using tickets.
Back to Top