Opened 5 years ago
Closed 5 years ago
#32675 closed Bug (fixed)
Migration autodetector detects unnecessary changes.
| Reported by: | Mariusz Felisiak | Owned by: | David Wobrock |
|---|---|---|---|
| Component: | Migrations | Version: | 4.0 |
| Severity: | Release blocker | Keywords: | |
| Cc: | David Wobrock, Simon Charette | 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
Migration autodetector detects nonexistent changes in ForeignKey, it's probably because previous migrations have the to_field parameter. For Django itself it detects two changes:
django/contrib/admin/migrations/0004_alter_logentry_content_type.pymigrations.AlterField( model_name='logentry', name='content_type', field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='contenttypes.contenttype', verbose_name='content type') ),django/contrib/auth/migrations/0013_alter_permission_content_type.pymigrations.AlterField( model_name='permission', name='content_type', field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype', verbose_name='content type'), )
Regression in aa4acc164d1247c0de515c959f7b09648b57dc42 (see #29899).
Change History (7)
comment:1 by , 5 years ago
comment:2 by , 5 years ago
| Triage Stage: | Unreviewed → Accepted |
|---|
The idea of considering to_field='id' equivalent to a non-specified one is tempting but it could be wrong in theory even if it's rarely the case in practice.
I personally don't think it's a big deal in practice since the detected AlterField is basically a noop if actually applied if I understand correctly?
I'd be up for documenting that some noop operations might be generated due to this and we could alter the historical admin and auth ones to drop the to_field which is a remnant of Django 1.7 IIRC?
If this is not deemed acceptable we could always resolve to_field as David described to prevent any behavior change.
comment:3 by , 5 years ago
I agree with Simon. Let's add release notes about changes in the migration autodetector with a warning that in some rare case a no-op operations might be generated. We should also edit old migrations in Django itself.
comment:4 by , 5 years ago
| Has patch: | set |
|---|---|
| Owner: | changed from to |
| Status: | new → assigned |
Suggested a PR to add this to the release note. Feel free to rephrase.
https://github.com/django/django/pull/14323
comment:5 by , 5 years ago
| Triage Stage: | Accepted → Ready for checkin |
|---|
Hi,
Indeed it seems that we didn't anticipate that this will generate new migrations because of the missing
to_field.A lot of the modified tests in the initial PR are linked to the absence of
to_field(here for instance)My question would be: does this actually change any behaviour of the existing migrations?
1/ If not, I guess we could stop taking
to_fieldinto account and basically considermodels.ForeignKey( to_field='id', on_delete=models.SET_NULL, blank=True, null=True, to='contenttypes.ContentType', verbose_name='content type', )and
models.ForeignKey( on_delete=models.SET_NULL, blank=True, null=True, to='contenttypes.ContentType', verbose_name='content type', )(without
to_field) as identical. Which therefore wouldn't generate new migrations.2/ If it makes a behavioural difference, we would have to compute again the
to_fieldfor the new model state.When we dig into the technical whys, when deconstructing the old and new fields (and checking if they are different), we have this one difference of
to_field:(Pdb) p old_field_dec ('django.db.models.ForeignKey', [], {'verbose_name': 'content type', 'blank': True, 'null': True, 'on_delete': <function SET_NULL at 0x7fa5f297f9d0>, 'to': 'contenttypes.contenttype', 'to_field': 'id'}) (Pdb) p new_field_dec ('django.db.models.ForeignKey', [], {'verbose_name': 'content type', 'blank': True, 'null': True, 'on_delete': <function SET_NULL at 0x7fa5f297f9d0>, 'to': 'contenttypes.contenttype'})So we can dig into how this
to_fieldis generated for aForeignKey=> https://github.com/django/django/blob/main/django/db/models/fields/related.py#L889to_meta = getattr(self.remote_field.model, "_meta", None) if self.remote_field.field_name and ( not to_meta or (to_meta.pk and self.remote_field.field_name != to_meta.pk.name)): kwargs['to_field'] = self.remote_field.field_namewhere
self.remote_fieldis aManyToOneRelthat has a Nonefield_nameattribute.And if I follow the
ManyToOneRelcreation back correctly, we end up in theForeignKeyconstructor: https://github.com/django/django/blob/main/django/db/models/fields/related.py#L786-L822Where we can either define the
ManyToOneRel.field_namein two ways:_metais available. However, it was the main goal of the initial ticket #29899 to remove the rendering of models and remove availability of_meta. (the code there referenced an old ticket #12190, maybe that can help understand its purpose?)I hope that helps in some way, and I'd be more than happy to work on a patch if we can define what we want to do for this case :) (I mean, I introduced this bug, so I definitely want to fix it ;-) )