Дублирование поля Django ManytoMany, ошибка атрибута: «ManyRelatedManager»

Есть три модели: House_Type, House_Option и Order.

Модель House_Type имеет 2 поля: id и name

House_Option имеет 3 поля: идентификатор, имя и тип, где тип — это внешний ключ, связанный с House_Type.

и, наконец, Order состоит из множества полей, одно из которых — это поле ManytoMany, называемое «выбор», которое ссылается на House_Option.

Это работает так, что House_Type имеет разные «типы» домов: например, квартира, квартира, отдельный дом, двухквартирный дом и т. д.

House_Option имеет все возможные варианты для каждого типа: так, например, для типа «квартира» у вас есть вариант 1, расположенный на улице X, вариант 2, расположенный на улице Y и т. д..

В модели Order пользователь должен выбрать один «вариант» каждого «типа» дома. Таким образом, они должны выбрать один вариант квартиры, один вариант дома и т. д. Поскольку это поле ManytoMany, это возможно. Однако мой вопрос: как мне запретить пользователю выбирать, например, ДВА варианта «квартиры». Как мне ограничить их выбором только одного (или ни одного) из каждого?

Я пытался создать определение (чистое) в модели Order:

        def clean(self):
            if self.choice.house_option_type.count() > 1:
                    raise ValidationError('Custom Error Message')

Однако это возвращает ошибку атрибута: объект «ManyRelatedManager» не имеет атрибута «house_option_type».

Любые идеи?


person JohnnyCash    schedule 25.02.2012    source источник


Ответы (3)


Управляйте отношениями ManyToMany с помощью явно определенной модели, в которой оба внешних ключа уникальны для каждого заказа. Вы можете сделать это, используя unique_together, чтобы наложить ограничение уникальности на отношения многие ко многим для одних и тех же типов.

 class House_Type(models.Model):
      name = models.CharField(...)


 class House_Option(models.Model):
      name = models.CharField(...)
      type = models.ForeignKey(House_Type)

 class Order(models.Model):
      ...
      choices = models.ManyToManyField(House_Option, through='Order_options')
      ...

 class Order_options(models.Model):
      class Meta:
          unique_together = ('order', 'option__type')

      ...
      order = models.ForeignKey(Order)
      option = models.ForeignKey(House_Option)
      ...

Редактировать, обновлять синтаксис и исправлять.

Да, похоже, что unique_together применяется как ограничение БД к таблице и не будет работать между таблицами. Так что забудьте о вышеупомянутом подходе.

Тем не менее я думаю, что следующее должно работать:

Если вы просто переопределите validate_unique на Order_options и реализовать логику уникальности самостоятельно, при этом внимательно следя за тем, как обрабатывать существующие и несуществующие случаи, которые должны работать.

from django.core.exceptions import ValidationError, NON_FIELD_ERRORS

class Order_options(models.Model):
    ...
    def validate_unique(self, exclude = None):
        super(Order_options, self).validate_unique(exclude)

        options = { 'order__id' : self.order.id, 'option__type' : 'self.option.type' }
        objs = Order_options.objects.exclude(id=self.id) if self.id else Order_options.objects
        if objs.filter(**options).exists():
            raise ValidationError({NON_FIELD_ERRORS: ['Error: {0} option type already exists'.format(self.option.type)]})
    ...
person astevanovic    schedule 25.02.2012
comment
Это хорошо. Но то, что нужно OP, - это уникальное ограничение для (order, option_type). Обратите внимание, что option_type не является частью внешнего ключа между House_Option и Order_options. Можно ли это как-то реализовать в Django? Я думал, что Django не может обрабатывать составные первичные ключи. - person ypercubeᵀᴹ; 25.02.2012
comment
Вы можете попробовать с unique_together = ('order', 'option__type') как изменено выше, но я не уверен, что Django разрешит таким образом уникальную проверку кросс-модели. - person astevanovic; 25.02.2012
comment
При попытке перекрестной уникальной проверки модели это ошибка, которую я получаю при синхронизации базы данных: Ошибка: одна или несколько моделей не прошли проверку: House_proj.order_options: unique_together ссылается на Option__option_type, поле, которое не существует. Проверьте свой синтаксис. - person JohnnyCash; 25.02.2012
comment
попытка validate_unique полностью удаляет поле manytomany из таблицы Order на странице администратора. На самом деле я даже не уверен, что там произошло - person JohnnyCash; 26.02.2012
comment
Удалите класс Meta из Order_options, validate_unique не должен иметь никакого влияния на существование многих полей в Order, но unique_together может (и, к сожалению, это не поможет вам в вашем случае). - person astevanovic; 26.02.2012

Если вы хотите подсчитать количество вариантов выбора типа, вы можете сделать это:

if self.choice.filter(type__name = 'condo').count() > 1:
  raise ValidationError("Multiple condos selected!");
person kdt    schedule 25.02.2012
comment
однако это ограничило бы модель и не позволило бы ей быть немного более динамичной. - person JohnnyCash; 25.02.2012

Я думаю, что Django не допускает составных первичных ключей и (таким образом) ни составных ограничений внешнего ключа (либо для первичного, либо для уникального ключа), которые решили бы проблему изначально.

Для этой функции есть заявка с активностью: Заявка №373: Добавить поддержку первичных ключей с несколькими столбцами< /а>

person ypercubeᵀᴹ    schedule 25.02.2012
comment
Я был бы рад услышать способ решения этой проблемы с существующими функциями. - person ypercubeᵀᴹ; 25.02.2012