#34744 closed Bug (fixed)

Migration re-add constraints when check condition contains a dict_keys object.

Reported by: bcail Owned by: David Sanders
Component: Migrations Version: dev
Severity: Normal Keywords:
Cc: Adam Zapletal, David Sanders Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

A CheckConstraint using dict_keys like this has an issue:

models.CheckConstraint(
        name='animal_values',
        check=models.Q(animal__in={'c': 'cat', 'd': 'dog'}.keys())
)

It continually creates migrations and prompts you to create another migration - it doesn't realize that the migration has already been generated and applied.

If I change it to a list like this, it works - the problem goes away:

models.CheckConstraint(
        name='animal_values',
        check=models.Q(animal__in=list({'c': 'cat', 'd': 'dog'}.keys()))
)

Should Django properly handle dict_keys, or should the docs say that they have to be converted to a list?

Change History (7)

comment:1 by Adam Zapletal, 14 months ago

Cc: Adam Zapletal added

comment:2 by David Sanders, 14 months ago

Cc: David Sanders added

Without offering opinion on whether this is a bug or not, here are some notes:

  • Migration change detection compares the loaded state from migration files with the current project's state
  • Migrations are serialised before written to disk
  • When the project's state is loaded it's not serialised first
  • tuples & lists are serialised independently; all other iterables get serialised into tuples (see django/db/migrations/serializer.py)


So we have a situation where migrations is comparing serialized state to pre-serialized state.

While this could potentially be a bug, the workaround is to simply use a list and "fixing" this issue could break expected current behaviour? 🤔

comment:3 by Mariusz Felisiak, 14 months ago

Summary: Migrations with dict_keys CheckConstraintMigration re-add constraints when check condition contains a dict_keys object.
Triage Stage: UnreviewedAccepted
Type: UncategorizedBug

I think it's worth fixing (check out a similar ticket #30350).

comment:4 by bcail, 14 months ago

Thanks David and Mariusz.

I added print statements to MigrationAutodetector.create_altered_constraints, and that confirms the serialized vs non-serialized states:

old_constraints=[<CheckConstraint: check=(AND: ('animal__in', ('c', 'd'))) name='animal_values'>]
new_constraints=[<CheckConstraint: check=(AND: ('animal__in', dict_keys(['c', 'd']))) name='animal_values'>]

I'm not sure how to best fix this, though.

comment:5 by David Sanders, 14 months ago

I have a draft PR which serialises during the change detection: https://github.com/django/django/pull/17114

I'm not particularly fond of this though. For one thing this probably would've masked the real issue behind #30350? 🤔 It also only narrowly focuses on one part of the project state.

comment:6 by Mariusz Felisiak, 13 months ago

Has patch: set
Owner: changed from nobody to David Sanders
Status: newassigned
Triage Stage: AcceptedReady for checkin

comment:7 by Mariusz Felisiak <felisiak.mariusz@…>, 13 months ago

Resolution: fixed
Status: assignedclosed

In 76c3e310:

Fixed #34744 -- Prevented recreation of migration for constraints with a dict_keys.

Co-authored-by: Mariusz Felisiak <felisiak.mariusz@…>

Note: See TracTickets for help on using tickets.
Back to Top