#29177: Unmanaged models with ForeignKeys do not get those fields serialized into their migration state when CreateModel happens. ------------------------------+-------------------------------------- Reporter: Keryn Knight | Owner: nobody Type: Bug | Status: new Component: Migrations | Version: master Severity: Normal | Resolution: Keywords: | Triage Stage: Unreviewed Has patch: 0 | Needs documentation: 0 Needs tests: 0 | Patch needs improvement: 0 Easy pickings: 0 | UI/UX: 0 ------------------------------+-------------------------------------- Description changed by Keryn Knight:
Old description: > Given models like the following, where `B` is unmanaged, and `C` is a > normal concrete `Model`: > > {{{ > from django.db import models > > class A(models.Model): > class Meta: > db_table = "test_a" > > class B(models.Model): > a = models.ForeignKey(A, related_name="+", on_delete=models.CASCADE) > > class Meta: > managed = False > db_table = "test_b" > > class C(models.Model): > a = models.ForeignKey(A, related_name="+", on_delete=models.CASCADE) > > class Meta: > db_table = "test_c" > }}} > > when `python manage.py makemigrations <appname>` is called, `B` ends up > looking like: > {{{ > migrations.CreateModel( > name='B', > fields=[ > ('id', models.AutoField(auto_created=True, primary_key=True, > serialize=False, verbose_name='ID')), > ], > options={ > 'db_table': 'test_b', > 'managed': False, > }, > ) > }}} > whilst `C` correctly gets: > {{{ > migrations.CreateModel( > name='C', > fields=[ > ('id', models.AutoField(auto_created=True, primary_key=True, > serialize=False, verbose_name='ID')), > ('a', > models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, > related_name='+', to='whee.A')), > ], > options={ > 'db_table': 'test_c', > }, > ) > }}} > Because `B` doesn't have the `a` attribute, any subsequent data > migrations which would like to query on it, like so: > {{{ > def forwards(apps, schema_editor): > B = apps.get_model('whee', 'B') > B.objects.filter(a=1) > }}} > will crash. > > With the assistance of Markus on IRC: > {{{ > MarkusH: do you want to try what's gonna happen when you just drop the > if-condition in > https://github.com/django/django/blob/75527c0f8360ae3555fcf67ce19b4cb910b69b9d/django/db/migrations/autodetector.py#L570-L572 > ? > }}} > > commenting out the lines > {{{ > if not model_opts.managed: > continue > }}} > > allows the autodetector to generate the following: > {{{ > migrations.CreateModel( > name='A', > fields=[ > ('id', models.AutoField(auto_created=True, primary_key=True, > serialize=False, verbose_name='ID')), > ], > options={ > 'db_table': 'test_a', > }, > ), > migrations.AddField( > model_name='b', > name='a', > field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, > related_name='+', to='whee.A'), > ) > }}} > and subsequently the data migration can access `B.a` > > If `A` itself is `managed=False` the migration generated is instead: > {{{ > migrations.CreateModel( > name='B', > fields=[ > ('id', models.AutoField(auto_created=True, primary_key=True, > serialize=False, verbose_name='ID')), > ('a', > models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, > related_name='+', to='whee.A')), > ], > options={ > 'db_table': 'test_b', > 'managed': False, > }, > ) > }}} > > Running the test suite via `./runtests.py` as of commit > 75527c0f8360ae3555fcf67ce19b4cb910b69b9d doesn't seem to cause any > failures, with those lines commented out. > > Markus also pointed out commit > https://github.com/django/django/commit/215aa4f53b6bbd07d5c1eecfa94e7fcd00da813e > which references #23415 > > I noticed this "issue" while bringing a project up from 1.9, so it's not > a recent problem. New description: Given models like the following, where `B` is unmanaged, and `C` is a normal concrete `Model`: {{{ from django.db import models class A(models.Model): class Meta: db_table = "test_a" class B(models.Model): a = models.ForeignKey(A, related_name="+", on_delete=models.CASCADE) class Meta: managed = False db_table = "test_b" class C(models.Model): a = models.ForeignKey(A, related_name="+", on_delete=models.CASCADE) class Meta: db_table = "test_c" }}} when `python manage.py makemigrations <appname>` is called, `B` ends up looking like: {{{ migrations.CreateModel( name='B', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ], options={ 'db_table': 'test_b', 'managed': False, }, ) }}} whilst `C` correctly gets: {{{ migrations.CreateModel( name='C', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('a', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='whee.A')), ], options={ 'db_table': 'test_c', }, ) }}} Because `B` doesn't have the `a` attribute, any subsequent data migrations which would like to query on it, like so: {{{ def forwards(apps, schema_editor): B = apps.get_model('whee', 'B') B.objects.filter(a=1) }}} will crash. With the assistance of Markus on IRC: {{{ MarkusH: do you want to try what's gonna happen when you just drop the if- condition in https://github.com/django/django/blob/75527c0f8360ae3555fcf67ce19b4cb910b69b9d/django/db/migrations/autodetector.py#L570-L572 ? }}} commenting out the lines {{{ if not model_opts.managed: continue }}} allows the autodetector to generate the following: {{{ migrations.CreateModel( name='B', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ], options={ 'db_table': 'test_b', 'managed': False, }, ), migrations.AddField( model_name='b', name='a', field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='whee.A'), ) }}} and subsequently the data migration can access `B.a` If `A` itself is `managed=False` the migration generated is instead: {{{ migrations.CreateModel( name='B', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('a', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='whee.A')), ], options={ 'db_table': 'test_b', 'managed': False, }, ) }}} Running the test suite via `./runtests.py` as of commit 75527c0f8360ae3555fcf67ce19b4cb910b69b9d doesn't seem to cause any failures, with those lines commented out. Markus also pointed out commit https://github.com/django/django/commit/215aa4f53b6bbd07d5c1eecfa94e7fcd00da813e which references #23415 I noticed this "issue" while bringing a project up from 1.9, so it's not a recent problem. -- -- Ticket URL: <https://code.djangoproject.com/ticket/29177#comment:2> 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 django-updates+unsubscr...@googlegroups.com. To post to this group, send email to django-updates@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-updates/067.94b2a873fc7fdb319a5477c90314e257%40djangoproject.com. For more options, visit https://groups.google.com/d/optout.