Как создать миграцию Django для отношения ManyToMany, включающую каскад удаления?
Я использую PostGres 10, Python 3.9 и Django 3.2. Я создал эту модель с сопутствующими отношениями "многие-ко-многим" ...
class Account(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
...
crypto_currencies = models.ManyToManyField(CryptoCurrency)
После генерации и запуска миграций Django была создана следующая таблица...
\d cbapp_account_crypto_currencies;
Table "public.cbapp_account_crypto_currencies"
Column | Type | Modifiers
-------------------+---------+------------------------------------------------------------------------------
id | integer | not null default nextval('cbapp_account_crypto_currencies_id_seq'::regclass)
account_id | uuid | not null
cryptocurrency_id | uuid | not null
Indexes:
"cbapp_account_crypto_currencies_pkey" PRIMARY KEY, btree (id)
"cbapp_account_crypto_cur_account_id_cryptocurrenc_38c41c43_uniq" UNIQUE CONSTRAINT, btree (account_id, cryptocurrency_id)
"cbapp_account_crypto_currencies_account_id_611c9b45" btree (account_id)
"cbapp_account_crypto_currencies_cryptocurrency_id_685fb811" btree (cryptocurrency_id)
Foreign-key constraints:
"cbapp_account_crypto_account_id_611c9b45_fk_cbapp_acc" FOREIGN KEY (account_id) REFERENCES cbapp_account(id) DEFERRABLE INITIALLY DEFERRED
"cbapp_account_crypto_cryptocurrency_id_685fb811_fk_cbapp_cry" FOREIGN KEY (cryptocurrency_id) REFERENCES cbapp_cryptocurrency(id) DEFERRABLE INITIALLY DEFERRED
Как изменить мое полевое отношение или создать миграцию, чтобы каскадное отношение было ON-DELETE CASCADE? То есть, когда я удаляю учетную запись, я хочу, чтобы сопутствующие записи в этой таблице также были удалены.
Когда вы используете ManyToManyField
, Django создает для вас промежуточную таблицу, в данном случае с именем cbapp_account_crypto_currencies
. Что вы хотите сделать в будущем, так это всегда явно создавать промежуточную модель, AccountCryptoCurrencies
, а затем устанавливать атрибут through
в ManyToManyField
. Это позволит вам в будущем добавлять больше полей в промежуточную модель. См. подробнее здесь: https://docs.djangoproject.com/en/3.2/ref/models/fields/#django.db.models.ManyToManyField.through.
Теперь вам нужно создать эту промежуточную таблицу:
class AccountCryptoCurrencies(models.Model):
account = models.ForeignKey(Account)
cryptocurrency = models.ForeignKey(CryptoCurrency)
class Meta:
db_table = 'cbapp_account_crypto_currencies'
class Account(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
...
crypto_currencies = models.ManyToManyField(CryptoCurrency, through=AccountCryptoCurrencies)
Теперь вам нужно создать миграцию, но пока не применяйте ее! Измените миграцию, обернув ее в SeparateDatabaseAndState
. Я не создал ваш файл миграции, потому что у меня нет полной модели, но вы можете посмотреть здесь, как это сделать: Как добавить сквозную опцию к существующему ManyToManyField с миграциями и данными в django
Теперь вы можете применить миграцию, и теперь у вас должна быть явная промежуточная таблица без потери данных. Теперь вы также можете добавлять дополнительные поля в промежуточную таблицу и изменять существующие поля. Вы можете добавить on_delete=models.CASCADE
к полю account
и мигрировать изменение.
Посмотрел на это внимательнее. Я попытался воспроизвести ваши модели, и я также вижу, что промежуточная таблица не имеет каскада. У меня нет ответа на ваш главный вопрос о том, как добавить каскад, но кажется, что django делает поведение каскада, которое уже поддерживает это:
Когда я удаляю учетную запись, я хотел бы, чтобы сопутствующие записи в этой таблице также были удалены.
Для демонстрации:
a = Account.objects.create(name='test')
c1 = CryptoCurrency.objects.create(name='c1')
c2 = CryptoCurrency.objects.create(name='c2')
c3 = CryptoCurrency.objects.create(name='c3')
a.crypto_currencies.set([c1, c2, c3])
Если вы делаете:
a.delete()
Django выполняет следующий SQL, который имитирует каскад на промежуточной таблице:
[
{
'sql': 'DELETE FROM "myapp_account_crypto_currencies" WHERE "myapp_account_crypto_currencies"."account_id" IN (3)', 'time': '0.002'
},
{
'sql': 'DELETE FROM "myapp_account" WHERE "myapp_account"."id" IN (3)', 'time': '0.001'
}
]
Я не могу найти в документации, почему это делается именно так.