Opened 6 years ago
Closed 6 years ago
#29745 closed Bug (fixed)
Argument based equality check of BaseExpression causes unstable migration state
Reported by: | bacilla | Owned by: | Simon Charette |
---|---|---|---|
Component: | Migrations | Version: | 2.0 |
Severity: | Normal | Keywords: | migration meta ordering expression |
Cc: | Triage Stage: | Accepted | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Changing a model's meta option ordering
that contains expressions causes Django to generate an infinite number of migrations.
Test model:
from django.db import models from django.db.models.functions import Cast, Concat, Right class TestEntity(models.Model): some_property = models.TextField() class Meta: ordering = ('some_property',)
Makemigrations log:
$ python manage.py makemigrations app Migrations for 'app': app/migrations/0001_initial.py - Create model TestEntity
Then ordering
have been changed.
class TestEntity(models.Model): some_property = models.TextField() class Meta: ordering = ( Cast(models.Func(models.F('some_property'), models.Value('^\d+'), function='SUBSTRING'), models.IntegerField()), Right(Concat(models.Value('00000'), models.F('some_property')), models.Value(5)) )
Makemigrations log:
$ python manage.py makemigrations app Migrations for 'app': app/migrations/0002_auto_20180907_1712.py - Change Meta options on testentity $ python manage.py makemigrations app Migrations for 'app': app/migrations/0003_auto_20180907_1713.py - Change Meta options on testentity $ python manage.py makemigrations app Migrations for 'app': app/migrations/0004_auto_20180907_1713.py - Change Meta options on testentity
Attachments (1)
Change History (10)
by , 6 years ago
Attachment: | migrations_bug.tar.gz added |
---|
comment:1 by , 6 years ago
Summary: | Infinte migrations when changing 'ordering' Meta option that contains expressions → Infinite migrations when changing 'ordering' Meta option that contains expressions |
---|
comment:2 by , 6 years ago
Triage Stage: | Unreviewed → Accepted |
---|---|
Type: | Uncategorized → Bug |
Version: | 2.1 → 2.0 |
Thanks for filling up a new bug report.
This is caused by the fact models.Field.__eq__
is based of creation_counter which makes models.IntegerField() != models.IntegerField()
and thus Cast(expr, models.IntegerField())
!= Cast(expr, models.IntegerField())
.
I feel like this is going to be hard to solve at the Field
level without revisiting how we determine field definition order. This has always been a hack to work around unordered type.__new__(attrs)
anyway which is not necessary in Python 3.6+ and can be worked around in Python 3.5+ thanks to PEP 520's type.__prepare__
which could be added to ModelBase
.
Another solution could be to special case output_field
equality checks in BaseExpression.__eq__
even more. This would surely be a less invasive change if we plan to backport the adjustment.
Related to #26257.
comment:3 by , 6 years ago
Summary: | Infinite migrations when changing 'ordering' Meta option that contains expressions → Meta.ordering containing Field instances causes infinite migrations |
---|
comment:4 by , 6 years ago
Seems like a working hack.
class ToInteger(models.IntegerField): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.creation_counter = 1 class TestEntity(models.Model): some_property = models.TextField() class Meta: ordering = ( Cast(models.Func(models.F('some_property'), models.Value('^\d+'), function='SUBSTRING'), ToInteger()), Right(Concat(models.Value('00000'), models.F('some_property')), models.Value(5)), )
comment:5 by , 6 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:6 by , 6 years ago
Has patch: | set |
---|---|
Patch needs improvement: | set |
Summary: | Meta.ordering containing Field instances causes infinite migrations → Argument based equality check of BaseExpression causes unstable migration state |
Patch which is still missing a few tests
comment:7 by , 6 years ago
@bacilla by the way a less invasive workaround is to pass Cast(output_field)
as a keyword argument instead of a positional one.
class TestEntity(models.Model): some_property = models.TextField() class Meta: ordering = ( Cast(models.Func(models.F('some_property'), models.Value('^\d+'), function='SUBSTRING'), output_field=models.IntegerField()), Right(Concat(models.Value('00000'), models.F('some_property')), models.Value(5)) )
comment:8 by , 6 years ago
Patch needs improvement: | unset |
---|
test project