В комментариях @AlexandrTatarinov предлагает использовать add_to_class
для добавления ManyToManyField
к Car
сразу после создания таблицы through
. Я все еще открыт для лучших ответов, но это, безусловно, ответ (и в настоящее время лучший из доступных). Предположим, мы используем код вроде:
# guard against repeated import
if not hasattr(Car, 'users'):
field = models.ManyToManyField(User, through=UserCar, related_name='cars')
field.contribute_to_class(Car, 'users')
Эффект (включая недостатки) этого подхода:
- Волшебные аксессуары добавляются к обеим сторонам отношений
- Миграция (добавление поля M2M) размещена в приложении для
Car
. Эта миграция не будет зафиксирована в хранилище для моего пакета и может привести к некоторым странным проблемам. Например,
- Если разработчик использует мой пакет в качестве зависимости, миграция будет (заново) создана, когда разработчик вызовет
makemigrations
. Если позднее пакет Car
будет обновлен (и будет включать новые миграции), это может привести к ошибке, такой как «два конечных узла» на компьютере разработчика.
- Когда я удаляю миграцию
Car
для имитации сценария производственного сценария, тривиальный тест работает (например, после создания двух взаимосвязанных объектов, user.places
и place.users
работает правильно)
- Поскольку таблица M2M определяется как таблица
through
, такие методы, как place.users.add()
, не работают "из коробки" (даже несмотря на то, что таблица M2M точно такая же, как таблица, автоматически создаваемая полем).
Проблема с place.users.add()
возникает из-за auto_created=False
на модели through=
, созданной вручную. Если я установлю auto_created=Car
в UserCar.Meta
, магические методы работают, но миграции не создаются. Чтобы воспользоваться преимуществами миграций по умолчанию и восстановления с помощью поведений, я смог использовать следующее:
@receiver(request_started)
def set_auto_created(**kwargs):
UserCar._meta.auto_created = Car
РЕДАКТИРОВАТЬ: На более ранней итерации я использовал auto_created=True
. Это вызывало проблему с FLUSH
в TransactionTestCase
. После просмотра кода M2MField, похоже, что auto_created
необходимо указать на модель, содержащую ManyToManyField
. Я думаю, что это как-то связано с тем, как автоматически создаются таблицы в TransactionTestCase
.
EDIT2: я также пытался подключить сигнал к post_migrate
. Это хорошо работает для тестов (так как они всегда мигрируют), но не работало в производстве. request_started
, кажется, работает для обоих.
С этим улучшением дополнительная миграция, созданная в приложении для Car
, является единственной серьезной проблемой этой стратегии.