【问题标题】:Django ManytoMany field duplicates, attribute error: 'ManyRelatedManager'Django ManytoManyfield 重复,attributeerror: 'ManyRelatedManager'
【发布时间】:2012-03-15 19:57:36
【问题描述】:

共有三种模型:House_Type、House_Option 和 Order

House_Type 模型有 2 个字段:id 和 name

House_Option 有 3 个字段:id、name 和 type,其中 type 是链接到 House_Type 的外键。

最后,Order 由许多字段组成,其中一个是名为“choice”的多对多字段,它链接到 House_Option

其工作方式是 House_Type 具有不同的房屋“类型”:例如,公寓、公寓、独立式房屋、半独立式房屋等。

House_Option 具有每种类型的所有可能选项:例如,对于“公寓”类型,选项 1 位于街道 X,选项 2 位于街道 Y 等等。

在订单模型中,用户必须为每个房屋“类型”选择一个“选项”。所以他们必须选择一个公寓选项,一个房子选项等等。因为这是一个多对多领域,这是可能的。但是我的问题是:例如,如何防止用户选择两个“公寓”选项。我如何限制他们只选择一个(或不选择)?

我试图在 Order 模型中创建一个 def(clean):

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

然而,这会返回一个属性错误:“ManyRelatedManager”对象没有属性“house_option_type”

有什么想法吗?

【问题讨论】:

    标签: python django django-models django-orm


    【解决方案1】:

    通过明确定义的模型管理多对多关系,其中每个订单的两个外键都是唯一的。您可以使用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 被应用为表上的数据库约束,并且不能跨表工作。所以忘记上面的方法吧。

    我仍然认为以下应该有效:

    如果您只是简单地在 Order_options 上覆盖 validate_unique 并自己实现唯一性逻辑,同时注意如何处理现有和不存在的情况,它应该可以工作。

    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)]})
        ...
    

    【讨论】:

    • 这很好。但是 OP 需要的是对 (order, option_type) 的唯一约束。请注意,option_type 不是House_OptionOrder_options 之间的外键的一部分。可以以某种方式在 Django 中实现吗?我以为 Django 不能处理复合主键。
    • 您可以尝试使用 unique_together = ('order', 'option__type') ,但我不确定 Django 是否允许以这种方式进行跨模型唯一验证。
    • 当尝试跨模型唯一验证时,这是我在同步数据库时遇到的错误:错误:一个或多个模型未验证:House_proj.order_options:“unique_together”指的是 Option__option_type,一个字段不存在。检查你的语法。
    • 尝试 validate_unique 会完全从管理页面的 Order 表中删除 manytomany 字段。我实际上什至不确定那里发生了什么
    • 从 Order_options 中删除 Meta 类,validate_unique 不应该对 Order 中的 manytomany 字段的存在产生任何影响,但 unique_together 可能(不幸的是它对您的情况没有帮助)。跨度>
    【解决方案2】:

    我认为 Django 不允许复合主键,因此(因此)也不允许复合外键约束(主键或唯一键),这将在本机解决问题。

    该功能有一张票,其中有活动:Ticket #373: Add support for multiple-column primary keys

    【讨论】:

    • 我很高兴听到使用现有功能解决此问题的方法。
    【解决方案3】:

    如果你想统计一个类型的选择个数,你可以这样做:

    if self.choice.filter(type__name = 'condo').count() > 1:
      raise ValidationError("Multiple condos selected!");
    

    【讨论】:

    • 然而,这会限制模型并阻止它变得更加动态..
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-02-06
    • 1970-01-01
    • 1970-01-01
    • 2020-11-20
    • 2019-08-10
    • 1970-01-01
    • 2016-11-09
    相关资源
    最近更新 更多