web-dev-qa-db-ja.com

django)の移行とデータを使用して既存のManyToManyFieldにスルーオプションを追加する方法

ドキュメントやオンラインで特定の問題への参照を見つけることができません。

私は既存の多対多の関係を持っています。

_class Books(models.Model):
    name = models.CharField(max_length=100)

class Authors(models.Model):
    name = models.CharField(max_length=100)
    books = models.ManyToManyField(Books)
_

これには移行とデータがあります。ここで、多対多の関係を保持するテーブルに1つのフィールドを追加するために、throughオプションを使用する必要があります。

_class Authorship(models.Model):
    book = models.ForeignKey(Books)
    author = models.ForeignKey(Authors)
    ordering = models.PositiveIntegerField(default=1)

class Authors(models.Model):
    name = models.CharField(max_length=100)
    books = models.ManyToManyField(Books, through=Authorship)
_

移行を実行すると、DjangoはAuthorshipモデルの新しい移行を作成します。orderingAuthorship列を追加して手動で移行ファイルを作成しようとしました。テーブルとbooksテーブルのAuthors列の変更ですが、移行の問題が発生します。

_operations = [
    migrations.AddField(
        model_name='authorship',
        name='ordering',
        field=models.PositiveIntegerField(default=1),
    ),
    migrations.AlterField(
        model_name='authors',
        name='books',
        field=models.ManyToManyField(to='app_name.Books', through='app_name.Authorship'),
    ),
]
_

移行しようとすると、KeyError: ('app_name', u'authorship')が表示されます。影響を受けるものが他にもあるため、エラーが発生する可能性があります。

私は何が欠けていますか?これを処理する他のアプローチはありますか?

21
chhantyal

データ移行を行わずにオプションを使用する方法はないようです。そのため、データ移行アプローチに頼らざるを得なかったので、@ pista329の回答からいくつかのアイデアを取り入れ、次の手順を使用して問題を解決しました。

  • Authorshipモデルを作成する

    class Authorship(models.Model):
        book = models.ForeignKey(Books)
        author = models.ForeignKey(Authors)
        ordering = models.PositiveIntegerField(default=1)
    
  • 元のManyToManyField関係を保持しますが、上記で定義したモデルを使用して、モデルを介して別のフィールドを追加します。

    class Authors(models.Model):
        name = models.CharField(max_length=100)
        books = models.ManyToManyField(Books)
        published_books = models.ManyToManyField(Books, through=Authorship, related_name='authors_lst') # different related name is needed.
    
  • データ移行を追加して、すべてのデータを古いテーブルから新しいAuthorshipテーブルにコピーします。

この後、published_booksという名前の新しいフィールドがあるため、booksモデルのAuthorsフィールドを削除できます。

11
chhantyal

データを移行せずに「スルー」を追加する方法があります。 この@MatthewWilkesの答え に基づいてなんとかできました。

したがって、それをデータモデルに変換するには:

  1. Authorshipフィールドとbookフィールドのみを使用してauthorモデルを作成します。すでに持っている自動生成されたM2Mテーブルと同じ名前を使用するようにテーブル名を指定します。 'through'パラメーターを追加します。

    _class Authorship(models.Model):
        book = models.ForeignKey(Books)
        author = models.ForeignKey(Authors)
    
        class Meta:
            db_table = 'app_name_authors_books'
    
    class Authors(models.Model):
        name = models.CharField(max_length=100)
        books = models.ManyToManyField(Books, through=Authorship)
    _
  2. 移行を生成しますが、まだ実行しないでください。

  3. 生成された移行を編集し、移行操作を_migrations. SeparateDatabaseAndState_操作にラップします。すべての操作は_state_operations_フィールド内にあります(_database_operations_は空のままです)。あなたはこのようなものになってしまうでしょう:

    _operations = [
        migrations.SeparateDatabaseAndState(state_operations=[
            migrations.CreateModel(
                name='Authorship',
                fields=[
                    ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                    ('book', models.ForeignKey(to='app_name.Books')),
                ],
                options={
                    'db_table': 'app_name_authors_books',
                },
            ),
            migrations.AlterField(
                model_name='authors',
                name='books',
                field=models.ManyToManyField(through='app_name.Authorship', to='app_name.Books'),
            ),
            migrations.AddField(
                model_name='authorship',
                name='author',
                field=models.ForeignKey( to='app_name.Author'),
            ),
        ])
    ]
    _
  4. これで、移行を実行して、追加のorderingフィールドをM2Mテーブルに追加できます。

編集:どうやら、DBの列名は、自動M2Mテーブルとモデル定義テーブルでわずかに異なる方法で生成されます。 (私はDjango 1.9.3を使用しています。)

説明した手順の後、2ワード名(two_words=models.ForeignKey(...))のフィールドの列名を_twowords_id_から_two_words_id_に手動で変更する必要もありました。

25
grain

移行が煩雑になる場合があります。

Throughを使用してm2mフィールドを変更する場合は、変更されたフィールドの名前をAuthors.booksからAuthors.bookに変更することをお勧めします。 makemigrationsから尋ねられたとき、名前を本から本に変更した場合はどうなりますか? [yN]、番号として「N」を選択します。Djangoはbooksを削除し、変更する代わりにbookフィールドを作成します。

class Authorship(models.Model):
    book = models.ForeignKey("Books")
    author = models.ForeignKey("Authors")
    ordering = models.PositiveIntegerField(default=1)

class Authors(models.Model):
    name = models.CharField(max_length=100)
    book = models.ManyToManyField("Books", through="Authorship")

とにかくbooksを使用したい場合は、bookbooksに変更し、名前の変更に関するmakemigrationsの質問への回答としてyを使用して移行プロセスを繰り返します。

2
pista329