3

I am importing an existing database into it's own Django project. I have generated the initial models from the database, via inspectdb, and am enabling Django to control each table one at a time by commenting the managed=False lines in the table meta settings. I've started with the simple models and am hitting a snag when enabling tables with foreign keys. Django keeps generating the same migration for the foreign key DocTagID and I'm not sure why it is doing so ?

The table in question is shown below, everything is as generated by inspectdb with the exception of the commented line which is where I pass control of the table over to Django.

class Doctagversion(models.Model):
    id = models.IntegerField(db_column='Id', primary_key=True, blank=True)  # Field name made lowercase.
    doctagid = models.ForeignKey(DocTag, models.DO_NOTHING, db_column='DocTagId')  # Field name made lowercase.
    groupname = models.TextField(db_column='GroupName')  # Field name made lowercase.
    name = models.TextField(db_column='Name')  # Field name made lowercase.
    creationdate = models.DateTimeField(db_column='CreationDate')  # Field name made lowercase.
    lasteditdate = models.DateTimeField(db_column='LastEditDate', blank=True, null=True)  # Field name made lowercase.
    lastedituserid = models.IntegerField(db_column='LastEditUserId')  # Field name made lowercase.
    lastedituserdisplayname = models.TextField(db_column='LastEditUserDisplayName')  # Field name made lowercase.
    releasedate = models.DateTimeField(db_column='ReleaseDate', blank=True, null=True)  # Field name made lowercase.

    class Meta:
#         managed = False
        db_table = 'DocTagVersion'

Before passing on this control an initial migration for the schema in question is generated using python -m manage.py makemigrations, and applied with python -m manage.py migrate. This initial migration for the table is as follows, managed is set to False initially and the commented line is an entry I believe I should add to inform Django of the foreign key (inspectdb states as much in the generated models.py).

    migrations.CreateModel(
        name='Doctagversion',
        fields=[
            ('id', models.IntegerField(blank=True, db_column='Id', primary_key=True, serialize=False)),
            # ('doctagid',models.ForeignKey(db_column='DocTagId', default=-1, on_delete=models.deletion.DO_NOTHING, to='DocTag')),
            ('groupname', models.TextField(db_column='GroupName')),
            ('name', models.TextField(db_column='Name')),
            ('creationdate', models.DateTimeField(db_column='CreationDate')),
            ('lasteditdate', models.DateTimeField(blank=True, db_column='LastEditDate', null=True)),
            ('lastedituserid', models.IntegerField(db_column='LastEditUserId')),
            ('lastedituserdisplayname', models.TextField(db_column='LastEditUserDisplayName')),
            ('releasedate', models.DateTimeField(blank=True, db_column='ReleaseDate', null=True)),
        ],
        options={
            'db_table': 'DocTagVersion',
            'managed': False,
        },
    ),

When I enable control over the table the first migration simply changes the table options.

    migrations.AlterModelOptions(
        name='doctagversion',
        options={},
    ),

Django adds the foreign key in question if it's not present in the initial migration as follows.

    migrations.AddField(
        model_name='doctagversion',
        name='doctagid',
        field=models.ForeignKey(db_column='DocTagId', default=-1, on_delete=django.db.models.deletion.DO_NOTHING, to='docutoo.DocTag'),
        preserve_default=False,
    ),

Thereafter it repeatedly generates the following migration as one cycles between python m manage.py makemigrations and python -m manage.py migrate.

    migrations.AlterField(
        model_name='doctagversion',
        name='doctagid',
        field=models.ForeignKey(db_column='DocTagId', on_delete=django.db.models.deletion.DO_NOTHING, to='docutoo.DocTag'),
    ),

Perhaps my strategy is wrong and I should simply enable all tables in one migration ?

As far as I can tell the following related question(s) do not account for my situation :

Carel
  • 3,289
  • 2
  • 27
  • 44

1 Answers1

2

From this Bug Report it seems Django migrations are quite sensitive to naming, I had commented out the db_table in all of my models. I believe I did this after creating the initial migration unwittingly breaking later ones.

  class Meta : 
    ...
#     db_table = "TableName"

Because I broke the naming makemigrations could not see that the table name had changed and was doing it's best to resolve it by repeatedly declaring the field.

Aside

Coincidentally when one creates the initial migration for an existing database as described in my question (inspectdb -> makemigrations -> migrate), Django traverses the models alphabetically creating tables in the same order and ignoring ones foreign keys. It creates the tables alphabetically and then modifies them later to include foreign keys, clobbering any data that might be present in a existing database. It seems one must define the foreign keys as integer fields initially and change them back as one lets Django manage the tables. Alternatively one can comment out all of their models and generate migrations as one uncomments them in a fashion that resolves the foreign keys; Django will then link them accordingly. One may then squash the migrations into a single migration and set this as the initial migration.

Carel
  • 3,289
  • 2
  • 27
  • 44