#35969: Changing output_field for GeneratedField leads to ProgrammingError with
Postgres 16.5+
-----------------------------+-----------------------------------------
     Reporter:  Ryan Schave  |                     Type:  Uncategorized
       Status:  new          |                Component:  Uncategorized
      Version:  5.1          |                 Severity:  Normal
     Keywords:               |             Triage Stage:  Unreviewed
    Has patch:  0            |      Needs documentation:  0
  Needs tests:  0            |  Patch needs improvement:  0
Easy pickings:  0            |                    UI/UX:  0
-----------------------------+-----------------------------------------
 Consider the following model and assume the initial migration has been
 applied:

 {{{#!python
 class Order(models.Model):
     order_no = models.IntegerField()
     item_no = models.CharField(max_length=25)
     qty = models.IntegerField()
     cost = models.DecimalField(max_digits=10, decimal_places=2)
     total_cost = models.GeneratedField(
         expression=F("cost") * F("qty"),
         output_field=models.BigIntegerField(),
         db_persist=True,
     )
 }}}

 During a code review we determined the output field should be a Decimal
 field and the field was modified as follows:

 {{{#!python
 total_cost = models.GeneratedField(
     expression=F("cost") * F("qty"),
     output_field=models.DecimalField(decimal_places=2, max_digits=16),
     db_persist=True,
 )
 }}}

 And a new migration was generated:

 {{{#!python
 migrations.AlterField(
     model_name='order',
     name='total_cost',
     field=models.GeneratedField(db_persist=True,
 expression=django.db.models.expressions.CombinedExpression(models.F('cost'),
 '*', models.F('qty')), output_field=models.DecimalField(decimal_places=2,
 max_digits=16)),
 ),
 }}}

 In Postgres 16.4 and earlier, this migration is applied without error.
 (I'm aware that the value of the total_cost field is not recomputed for
 existing records when this migration is applied.).

 Starting with Postgres 16.5 and up, this migration fails with the
 following error:

 {{{
 psycopg2.errors.InvalidColumnDefinition: cannot specify USING when
 altering type of generated column
 DETAIL:  Column "total_cost" is a generated column.
 ...
 django.db.utils.ProgrammingError: cannot specify USING when altering type
 of generated column
 }}}

 This appears to be a result of the following change in Postgres 16.5
 ([https://www.postgresql.org/docs/release/16.5/ release notes]):

   Disallow a `USING` clause when altering the type of a generated column
 (Peter Eisentraut) [§](https://postgr.es/c/5867ee005)
   A generated column already has an expression specifying the column
 contents, so including `USING` doesn't make sense.

 The Django
 [https://docs.djangoproject.com/en/5.1/ref/models/fields/#generatedfield
 documentation] for GeneratedField makes it clear that

    There are many database-specific restrictions on generated fields that
 Django doesn’t validate and the database may raise an error

 For this reason, I almost didn't open a ticket.  However, there is logic
 in db/backends/base/schema.py that checks if the expression of a
 GeneratedField changed.  Consider this migration (which changes the
 expression from multiplication to addition):

 {{{#!python
 migrations.AlterField(
     model_name='order',
     name='total_cost',
     field=models.GeneratedField(db_persist=True,
 expression=django.db.models.expressions.CombinedExpression(models.F('cost'),
 '+', models.F('qty')), output_field=models.BigIntegerField()),
 ),
 }}}

 Attempting to apply this migration will raise the following error (even in
 Postgres 16.4):

 {{{
 ValueError: Modifying GeneratedFields is not supported - the field
 sales.Order.total_cost must be removed and re-added with the new
 definition.
 }}}

 This error is more helpful.  It explains the problem better and even
 suggests a workaround.

 Should we throw a similar error if the output_field of a GeneratedField is
 changed?  Or add a tip to the documentation?

 The above was tested with:
 Django version 5.1.3
 psycopg2 version 2.9.10
 Postgres versions: 16.4, 16.5, 16.6, 17.0, and 17.2
-- 
Ticket URL: <https://code.djangoproject.com/ticket/35969>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/django-updates/010701939003d291-5ca80d4a-6011-4f24-b6b9-16b6db20c37f-000000%40eu-central-1.amazonses.com.

Reply via email to