Opened 7 years ago

Last modified 13 months ago

#28438 new Bug

Initial migration creates fields not listed in the migration if mixin class changes — at Version 3

Reported by: Michal Dabski Owned by: nobody
Component: Migrations Version: 1.11
Severity: Normal Keywords: migration, models, mixin, postgresql
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Michal Dabski)

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

Change History (3)

comment:1 by Michal Dabski, 7 years ago

Description: modified (diff)

comment:2 by Michal Dabski, 7 years ago

Keywords: migration models mixin sql added

comment:3 by Michal Dabski, 7 years ago

Description: modified (diff)
Note: See TracTickets for help on using tickets.
Back to Top