#23521: removal of concrete Model from bases doesn't remove it from ModelState
bases
---------------------------------+-------------------------------------
     Reporter:  Sergey Fedoseev  |                    Owner:  Ian Foote
         Type:  Bug              |                   Status:  assigned
    Component:  Migrations       |                  Version:  master
     Severity:  Normal           |               Resolution:
     Keywords:                   |             Triage Stage:  Accepted
    Has patch:  1                |      Needs documentation:  0
  Needs tests:  0                |  Patch needs improvement:  1
Easy pickings:  0                |                    UI/UX:  0
---------------------------------+-------------------------------------

Comment (by Karolis Ryselis):

 There is one more case that needs to be addressed related to bases in
 migrations.

 **Step 1:**
 Consider this model:
 {{{
 class Good(GoodServiceBase):
     title = models.CharField(max_length=16)
 }}}
 this creates migration with the following operation:

 {{{
 migrations.CreateModel(
     name='Good',
     fields=[
         ('id', models.AutoField(auto_created=True, primary_key=True,
 serialize=False, verbose_name='ID')),
         ('title', models.CharField(max_length=16)),
     ],
 ),
 }}}

 **Step 2:**
 Then we add a new model and change existing model to inherit from the new
 model.

 {{{
 class GoodServiceBase(models.Model):
     active = models.BooleanField(default=True)

 class Good(GoodServiceBase):
     title = models.CharField(max_length=16)
 }}}

 This generates migration like this:
 {{{
 migrations.CreateModel(
     name='GoodServiceBase',
     fields=[
         ('id', models.AutoField(auto_created=True, primary_key=True,
 serialize=False, verbose_name='ID')),
         ('active', models.BooleanField(default=True)),
     ],
 ),
 migrations.RemoveField(
     model_name='good',
     name='id',
 ),
 migrations.AddField(
     model_name='good',
     name='goodservicebase_ptr',
     field=models.OneToOneField(auto_created=True, default=1,
 on_delete=django.db.models.deletion.CASCADE, parent_link=True,
 primary_key=True, serialize=False, to='goods.GoodServiceBase'),
     preserve_default=False,
 ),
 }}}

 If we have no data in database, this will pass.

 **Step 3 with crash:**
 Try to access field {{{active}}} in datamigration and migration crashes.
 Operation in migration to reproduce this:
 {{{
 def migrate_my_data(apps, schema_editor):
     for good in apps.get_model("goods", "Good").objects.all():
         print(good.active)

 migrations.RunPython(migrate_my_data, reverse_code=lambda x, y: None)
 }}}

 This crashes with the error:
 {{{
 <...>
 File "<project home>/goods/migrations/0003_auto_20191025_1157.py", line 8,
 in migrate_my_data
   print(good.active)
 AttributeError: 'Good' object has no attribute 'active'
 }}}

 As far as I understand this happens because {{{bases}}} is not altered in
 migration and fake models do not inherit fields from their true parent
 models. Thus, currently if we change inheritance chain of existing models,
 we lose the ability to use those models in data migrations.

 Tested in version 2.2.6.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/23521#comment:26>
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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/068.b6f8c638872eaa9a23acc8f9e0dd831c%40djangoproject.com.

Reply via email to