Отключить ограничение уникальности для ManyToManyField в Django

#python #django

#python #django

Вопрос:

От документация Django для ManyToManyField :

Если вы не хотите, чтобы между одними и теми же экземплярами было несколько ассоциаций, добавьте UniqueConstraint, включая поля from и to . Автоматически сгенерированные таблицы «многие ко многим» в Django включают такое ограничение.

Как мне отключить это ограничение? Это единственный способ предоставить явную through таблицу? Или есть способ сообщить Django, чтобы он не добавлял UniqueConstraint в сгенерированную таблицу «многие ко многим»?

Комментарии:

1. В значительной степени. Вы можете обезьянничать с исправлением django.db.models.fields.related.create_many_to_many_intermediary_model , но это больше работы, чем установка сквозной модели. Я не вижу поддерживаемого способа сброса ограничения.

2. @Melvyn Я был бы признателен, если бы это было опубликовано в качестве ответа, пожалуйста.

Ответ №1:

таблицы m2m связаны промежуточной таблицей, которая управляет отношениями и имеет unique_together ограничение. Эта таблица создается автоматически, но вы можете использовать свою собственную таблицу, как указано в документе с through аргументом. Вы можете прочитать об этом в документации. В вашем случае вам нужно отключить unique_together ограничение, определив свою собственную промежуточную таблицу.

Другие варианты:

  1. Расширьте значение по умолчанию ManyToManyField и переопределите contribute_to_class метод.
  2. Отредактируйте значение по умолчанию ManyToManyField напрямую, как упоминал @Melvyn.

Комментарии:

1. Это не то уникальное, что имеет значение. 🙂 unique_together Метакласс in модели, которую создает Django, является соответствующей частью. уникальность на уровне поля по умолчанию равна False. Подробнее см. Метод, о котором я упоминал выше. Эта функция вызывается из ManyToManyField.contribute_to_class() .

2. Из моего вопроса: «Это единственный способ предоставить явную сквозную таблицу?» Похоже, ваш ответ «да, это единственный способ отключить UniqueConstraint по умолчанию». Это правильно?

3. Вы также можете расширить значение по умолчанию ManyToManyField и переопределить contribute_to_class методы. Другой способ — комментарий @Melvyn к вашему основному сообщению.

4. @Jonatan Спасибо за информацию. Мне кажется, что создать класс для through kwarg намного проще, чем любой из других предложенных до сих пор вариантов.

Ответ №2:

Вы можете обезьянить patch django.db.models.fields.related.create_many_to_many_intermediary_model , который создает промежуточную модель, и это самое близкое, что вы можете получить от него. Он вызывается из contribute_to_class метода в ManyToManyField. Конечно, это больше работы, чем указание сквозной модели, но если у вас есть вариант использования, когда все (или большинство) ваших посредников не требуют ограничений, тогда это стоит изучить.

Я не вижу поддерживаемого способа переопределить только это ограничение.

И да, вы можете создать подкласс ManyToManyField и установить там сквозную модель с помощью служебной функции, идентичной вышеупомянутой, исключая ограничение, а затем вызвать super() . Это позволит избежать ограничения, поскольку contribute_to_class не вызывает указанный выше метод, когда сквозная модель уже установлена:

         if not cls._meta.abstract:
            if self.remote_field.through:
                def resolve_through_model(_, model, field):
                    field.remote_field.through = model
                lazy_related_operation(resolve_through_model, cls, self.remote_field.through, field=self)
            elif not cls._meta.swapped:
                self.remote_field.through = create_many_to_many_intermediary_model(self, cls)
  

Оба метода имеют одинаковое предостережение: вам необходимо вручную синхронизировать вашу альтернативную утилиту с Django при обновлении Django, и все это с целью удаления одной строки :
'unique_together': (from_, to), .

Комментарии:

1. Переопределение contribute_to_class требует меньше работы, чем редактирование библиотеки Django.

2. Исправление обезьяны отличается от «физического исправления библиотеки Django». Вы перенаправляете функцию / метод в классе, который не принадлежит вам, на что-то другое. unittest.mock Библиотека делает это широко, используя patch свои функциональные возможности.

3. Плохая практика .

4. То же самое можно сказать и о том, что Django не придерживается мантры «делай одно». Утилита могла бы делегировать get_intermediairy_meta_klass , которая будет иметь элементы по умолчанию и будет принимать kwargs . Эти «золотые правила» и BCP существуют для общего случая. К счастью, мир пока не черно-белый, и все еще есть люди, которые игнорируют их, когда это уместно 🙂

5. Как вы говорите в своем первоначальном комментарии к моему вопросу, это намного больше работы, чем просто создание класса и указание through kwarg to ManyToManyField .