#35854 closed Bug (duplicate)
Order of "choices" on CharField randomly changing forcing new migrations despite no changes.
Reported by: | Karl | Owned by: | |
---|---|---|---|
Component: | Migrations | Version: | 5.1 |
Severity: | Normal | Keywords: | |
Cc: | Karl | Triage Stage: | Unreviewed |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
I have a field attachment_type
in a class that uses
TYPE_CHOICES = { ("image", "Image"), ("document", "Document"), ("audio", "Audio"), ("video", "Video"), }
Every makemigrations
call generates a new migration for that field:
migrations.AlterField( model_name="genericattachment", name="attachment_type", field=models.CharField( choices=[ ("audio", "Audio"), ("document", "Document"), ("video", "Video"), ("image", "Image"), ], default="image", max_length=10, ), ),
I noticed that the order of the choices in the migration changes every time. It doesn't seem to have a consistent method to compare the set of tuples properly and so it generates a new migration every time. I have not yet observed that it even accidentally collides in a way that does not generate a new one.
Change History (10)
comment:1 by , 2 months ago
Keywords: | CharField removed |
---|---|
Resolution: | → duplicate |
Status: | new → closed |
follow-ups: 3 4 comment:2 by , 2 months ago
I don't think that a set()
is a supported data structure for model field choices. Maybe we should replace A mapping or iterable in the format described below
by A mapping or sequence in the format described below
in the docs?
follow-up: 5 comment:3 by , 2 months ago
Replying to Claude Paroz:
I don't think that a
set()
is a supported data structure for model field choices. Maybe we should replaceA mapping or iterable in the format described below
byA mapping or sequence in the format described below
in the docs?
Code wise, we really do not mind that these are sets... Do you see a concrete issue with using sets? (other than dropdowns or options being shown in a random order).
comment:4 by , 2 months ago
Replying to Claude Paroz:
I don't think that a
set()
is a supported data structure for model field choices. Maybe we should replaceA mapping or iterable in the format described below
byA mapping or sequence in the format described below
in the docs?
Figured this out. It isn't explicitly *not* allowed, but it causes the issue I reported. If it is going to be allowed in the docs explicitly, migrations should handle it well.
Beyond that, I don't know why one would need a set()
. Pretty sure I was just combining a couple of sections of the docs when I wrote it because I can't ever remember the specific syntax and made a mistake.
Curious if a check could be created to prevent my (and other's) future confusion?
follow-up: 6 comment:5 by , 2 months ago
Replying to Natalia Bidart:
Do you see a concrete issue with using sets? (other than dropdowns or options being shown in a random order).
The main issue is that in 99% of the case, it will be an oversight by the dev, and cause the issue at hand. Do you see a valid use case using sets? I do agree with the reporter that if we support sets(), we should also fix the migration issue.
follow-up: 8 comment:6 by , 2 months ago
Replying to Claude Paroz:
Replying to Natalia Bidart:
Do you see a concrete issue with using sets? (other than dropdowns or options being shown in a random order).
The main issue is that in 99% of the case, it will be an oversight by the dev, and cause the issue at hand. Do you see a valid use case using sets?
Thank you for the response! My use case is, perhaps, a little simplistic or even a stretch, but let me share it:
You want your site users to pay attention when choosing an option so you use a set to have a non deterministic order of the displayed options.
I do agree with the reporter that if we support sets(), we should also fix the migration issue.
I disagree with this one. If a Django user uses a set(), I think they may be up to two goals:
- Either they made a mistake, and the infinite migrations make them notice (and fix that), or
- They intentionally did this and solve the migration issue by using a callable.
In any case, unordered choices and the migration issues is really what's #26609 is about (back when dicts weren't sorted), which was wontfixed. Perhaps disallowing unordered iterables should be discussed and potentially re-opened there? Thanks!
comment:8 by , 2 months ago
Replying to Natalia Bidart:
I disagree with this one. If a Django user uses a set(), I think they may be up to two goals:
- Either they made a mistake, and the infinite migrations make them notice (and fix that), or
- They intentionally did this and solve the migration issue by using a callable.
Here's where I think that's a mistake: I didn't notice it right away and created several migrations (3?) before noticing. I had even patched my prod environment with it. A simple "check" on the model (not explicitly part of migrations) likely would have caught it. Instead, I broke my migrations trying to "undo" it and lost some data.
Of course, this is in a one-man-shop environment with no review, so perhaps it's not a real issue on the global scale.
A lot of that is on me, but I don't see a "check" as a large burden and it can be ignored if the dev is so inclined.
follow-up: 10 comment:9 by , 2 months ago
Claude, Karl, your points make sense and I'm happy to go with the majority here. We already have a check for choices, it would be trivial to check against "unorderable iterables":
if not isinstance(self.choices, Iterable) or isinstance(self.choices, str): return [ checks.Error( "'choices' must be a mapping (e.g. a dictionary) or an iterable " "(e.g. a list or tuple).", obj=self, id="fields.E004", ) ]
I'm thinking we may need to reopen #26609 and repurpose that ticket to grow the system check for choices? Claude what do you think?
comment:10 by , 2 months ago
Replying to Natalia Bidart:
I'm thinking we may need to reopen #26609 and repurpose that ticket to grow the system check for choices? Claude what do you think?
Sure, +1!
Hello WoosterInitiative, thank you for taking the time to create this report.
This ticket is, strictly speaking, a duplicate of #26609, which was closed as
wontfix
for the reasons detailed in the ticket:26609#comment:3. But I'm happy to say that you can also workaround this issue by defining a callable for your choices (see #24561 and release notes), and this will avoid having migrations being generated because the order of the choices changed.