How to fake migrations for not to create a specifi

2019-06-22 13:18发布

问题:

I have following models

class VucutBolgesi(models.Model):
    site = models.ForeignKey(Site)
    bolge = models.CharField(verbose_name="Bölge", max_length=75)
    hareketler = models.ManyToManyField("Hareket", verbose_name="Hareketler", null=True, blank=True, help_text="Bölgeyi çalıştıran hareketler")


class Hareket(models.Model):
    site = models.ForeignKey(Site)
    hareket = models.CharField(verbose_name="Hareket", max_length=75 )
    bolgeler = models.ManyToManyField(VucutBolgesi, verbose_name="Çalıştırdığı Bölgeler", null=True, blank=True,
                                      help_text="Hareketin çalıştırdığı bölgeler")

I have the same M2M on both table since I wish to display same intermediate table on both admin forms. They also have to use the same table (not create two separate tables) since one change in one admin form must be reflected to the other. Like, If I add a new Hareket to VucutBolgesi through HareketAdmin then the same result shoudl be visible on VucutBolgesiAdmin too.

For achieving this, I first remove hareketler M2M field from VucutBolgesi so Hareketler model would create the intermediate table. I migrate this and then add hareketler to VucutBolgesi with db_table attribute so it will recognize the same intermediary table.

final look of the field is as folows

hareketler = models.ManyToManyField("Hareket", verbose_name="Hareketler", db_table="antrenman_hareket_bolgeler",
                                    null=True, blank=True, help_text="Bölgeyi çalıştıran hareketler")

When I try to migrate this, django throw following exception

django.db.utils.OperationalError: table "antrenman_hareket_bolgeler" already exists

How should I fake this migration?

Following is the migration django creates each time I run makemigrations

dependencies = [
    ('antrenman', '0005_vucutbolgesi_hareketler'),
]

operations = [
    migrations.AddField(
        model_name='vucutbolgesi',
        name='hareketler',
        field=models.ManyToManyField(to='antrenman.Hareket', db_table=b'antrenman_hareket_bolgeler', blank=True, help_text=b'B\xc3\xb6lgeyi \xc3\xa7al\xc4\xb1\xc5\x9ft\xc4\xb1ran hareketler', null=True, verbose_name=b'Hareketler'),
        preserve_default=True,
    ),
]

Note: Editing related migration file and removing migrations.AddField fo not work since django creates the same migrations.AddField with each makemigrations

回答1:

Is it possible to make a migration always to be faked, just override the apply and unapply methods. The consequences of this are not sufficiently investigated, but this far it works for me.

In the following example we create a migration that reuses django.contrib.auth.User.group's M2M table b'profile_user_groups:

from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('profile', '0001_initial'),
    ]

    operations = [
        migrations.AddField(
            model_name='user',
            name='organizations',
            field=models.ManyToManyField(db_column=b'group_id', db_table=b'profile_user_groups', related_name='members', to='profile.Organization'),
        ),
    ]

    def apply(self, project_state, schema_editor, collect_sql=False):
        return project_state.clone()

    def unapply(self, project_state, schema_editor, collect_sql=False):
        return project_state.clone()


回答2:

Solution was so simple.

You must be sure related migration is the only migration operation that needed to be faked. You must first create the migration with

python manage.py makemigrations antrenman

Then apply that migration with --fake

python manage.py migrate --fake antrenman

Handicap is, other developers should know that they have to fake related migration. If there are others migrations alongside with this one, they should make them first and then fake this one.

It is too bad there is no parameter that tells related migration should be real or fake.