#28438: Initial migration creates fields not listed in the migration if mixin 
class
changes
-------------------------------------+-------------------------------------
     Reporter:  Michal Dabski        |                    Owner:  nobody
         Type:  Uncategorized        |                   Status:  new
    Component:  Uncategorized        |                  Version:  1.11
     Severity:  Normal               |               Resolution:
     Keywords:                       |             Triage Stage:
  migration,models,mixin,sql         |  Unreviewed
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Description changed by Michal Dabski:

Old description:

> Consider the sample model with a mixin class:
> {{{
> from django.db import models
>
> class TestMixin(object):
>     pass
>

> class TestModel(TestMixin, models.Model):
>     sample_field = models.CharField(max_length=20)
> }}}
>
> When running makemigrations, django creates a perfectly good and valid
> migration:
> {{{
> # -*- coding: utf-8 -*-
> # Generated by Django 1.11.3 on 2017-07-26 17:03
> from __future__ import unicode_literals
>
> from django.db import migrations, models
> import migration_test.models
>

> class Migration(migrations.Migration):
>
>     initial = True
>
>     dependencies = [
>     ]
>
>     operations = [
>         migrations.CreateModel(
>             name='TestModel',
>             fields=[
>                 ('id', models.AutoField(auto_created=True,
> primary_key=True, serialize=False, verbose_name='ID')),
>                 ('sample_field', models.CharField(max_length=20)),
>             ],
>             bases=(migration_test.models.TestMixin, models.Model),
>         ),
>     ]
>
> }}}
> SQL:
>
> {{{
> BEGIN;
> --
> -- Create model TestModel
> --
> CREATE TABLE "migration_test_testmodel" ("id" serial NOT NULL PRIMARY
> KEY, "sample_field" varchar(20) NOT NULL);
> COMMIT;
> }}}
>

>
> Next, refactor mixin class to add a field to it - need to change mixin's
> base class to {{{models.Model}}}, otherwise field will not be correctly
> inherited by models:
> {{{
> class TestMixin(models.Model):
>     mixin_field = models.CharField(max_length=20, default='test')
>
>     class Meta:
>         abstract = True
> }}}
>
> This creates a new migration which adds {{{mixin_field}}} to it - nothing
> special. However, when applying both migrations after the model changes,
> second migration fails with the following error
> {{{django.db.utils.ProgrammingError: column "mixin_field" of relation
> "migration_test_testmodel" already exists}}}. as it turns out, the first
> migration's SQL has now changed to include {{{mixin_field}}}:
> {{{
> BEGIN;
> --
> -- Create model TestModel
> --
> CREATE TABLE "migration_test_testmodel" ("id" serial NOT NULL PRIMARY
> KEY, "mixin_field" varchar(20) NOT NULL, "sample_field" varchar(20) NOT
> NULL);
> COMMIT;
> }}}
> The python code of the migration obviously has not changed, but the
> resulting SQL did, and it includes a field not explicitly listed in the
> migration's {{{fields}}}.
>
> Note:
> - this does not happen if the mixin class extends {{{models.Model}}} to
> begin with.
> - if model extends a mixin that extends {{{object}}}, it ends up in
> model's {{{bases}}}, however if mixin extends {{{model.Model}}} it does
> not.
>

> Tested with Django 1.11.3, Python 2.7

New description:

 Consider the sample model with a mixin class:
 {{{
 from django.db import models

 class TestMixin(object):
     pass


 class TestModel(TestMixin, models.Model):
     sample_field = models.CharField(max_length=20)
 }}}

 When running makemigrations, django creates a perfectly good and valid
 migration:
 {{{
 # -*- coding: utf-8 -*-
 # Generated by Django 1.11.3 on 2017-07-26 17:03
 from __future__ import unicode_literals

 from django.db import migrations, models
 import migration_test.models


 class Migration(migrations.Migration):

     initial = True

     dependencies = [
     ]

     operations = [
         migrations.CreateModel(
             name='TestModel',
             fields=[
                 ('id', models.AutoField(auto_created=True,
 primary_key=True, serialize=False, verbose_name='ID')),
                 ('sample_field', models.CharField(max_length=20)),
             ],
             bases=(migration_test.models.TestMixin, models.Model),
         ),
     ]

 }}}
 SQL:

 {{{
 BEGIN;
 --
 -- Create model TestModel
 --
 CREATE TABLE "migration_test_testmodel" ("id" serial NOT NULL PRIMARY KEY,
 "sample_field" varchar(20) NOT NULL);
 COMMIT;
 }}}



 Next, refactor mixin class to add a field to it - need to change mixin's
 base class to {{{models.Model}}}, otherwise field will not be correctly
 inherited by models:
 {{{
 class TestMixin(models.Model):
     mixin_field = models.CharField(max_length=20, default='test')

     class Meta:
         abstract = True
 }}}

 This creates a new migration which adds {{{mixin_field}}} to it - nothing
 special. However, when applying both migrations after the model changes,
 second migration fails with the following error
 {{{django.db.utils.ProgrammingError: column "mixin_field" of relation
 "migration_test_testmodel" already exists}}}. as it turns out, the first
 migration's SQL has now changed to include {{{mixin_field}}}:
 {{{
 BEGIN;
 --
 -- Create model TestModel
 --
 CREATE TABLE "migration_test_testmodel" ("id" serial NOT NULL PRIMARY KEY,
 "mixin_field" varchar(20) NOT NULL, "sample_field" varchar(20) NOT NULL);
 COMMIT;
 }}}
 The python code of the migration obviously has not changed, but the
 resulting SQL did, and it includes a field not explicitly listed in the
 migration's {{{fields}}}.

 Note:
 - this does not happen if the mixin class extends {{{models.Model}}} to
 begin with.
 - if model extends a mixin that extends {{{object}}}, it ends up in
 model's {{{bases}}}, however if mixin extends {{{model.Model}}} it does
 not.
 - when mixin's base class changes, schema migration does not reflect this
 change in model's {{{bases}}}

 Tested with Django 1.11.3, Python 2.7

 Proposed solution:
 migration should not create database fields for model fields not
 explicitly listed in {{{fields}}}

--

-- 
Ticket URL: <https://code.djangoproject.com/ticket/28438#comment:3>
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/064.0129d722709a9b980e238475beb72763%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to