【问题标题】:adding the same object twice to a ManyToManyField将同一对象两次添加到 ManyToManyField
【发布时间】:2009-09-13 14:04:57
【问题描述】:

我有两个 django 模型类:

class A(models.Model):
     name = models.CharField(max_length = 128)    #irrelevant

class B(models.Model):
     a = models.ManyToManyField(A)
     name = models.CharField(max_length = 128)    #irrelevant

我想做的是:

a1 = A()
a2 = A()
b = B()

b.a.add(a1)
b.a.add(a1)    #I want to have a1 twice
b.a.add(a2)

assert len(b.a.all()) == 3 #this fails; the length of all() is 2

我猜测 add() 使用了集合语义,但我该如何规避呢?我尝试研究自定义管理器,但我不确定这是否正确(似乎很复杂)...

提前致谢!

【问题讨论】:

    标签: python django django-models


    【解决方案1】:

    我认为您想要的是使用中间模型,使用 ManyToManyField 中的 through 关键字参数来形成 M2M 关系。有点像上面的第一个答案,但更多的是“Django-y”。

    class A(models.Model):
        name = models.CharField(max_length=200)
    
    class B(models.Model):
        a = models.ManyToManyField(A, through='C')
        ...
    
    class C(models.Model):
        a = models.ForeignKey(A)
        b = models.ForeignKey(B)
    

    使用through 关键字时,通常的M2M 操作方法不再可用(这意味着add、create、remove,或使用= 运算符进行赋值)。相反,您必须自己创建中间模型,如下所示:

     >>> C.objects.create(a=a1, b=b)
    

    但是,您仍然可以在包含ManyToManyField 的模型上使用通常的查询操作。换句话说,以下内容仍然有效:

     >>> b.a.filter(a=a1)
    

    但也许一个更好的例子是这样的:

    >>> B.objects.filter(a__name='Test')
    

    只要中间模型上的 FK 字段未指定为 unique,您就可以使用相同的 FK 创建多个实例。您还可以通过将您喜欢的任何其他字段添加到C 来附加有关该关系的其他信息。

    中间模型记录在here。

    【讨论】:

      【解决方案2】:

      Django 使用关系数据库作为其底层存储,并且本质上确实具有“设置语义”:无法绕过它。因此,如果您想要任何东西的“多组”,您必须用一个数字字段来表示它,该字段计算每个项目“出现”的次数。 ManyToManyField 不这样做 - 因此,相反,您需要一个单独的 Model 子类,它明确指示 A 和它相关的 B,并且有一个整数属性来“计算多少次”。

      【讨论】:

      • 我认为这并不完全正确。从 A 到 B 的关系是通过由三列组成的关系表实现的:id、a_id 和 b_id。 id 是该表的主键,因此可以在该表中有三行,其中两行分别具有相同的 a_id 和 b_id 条目。这就是我想要的,但我不知道如何告诉 Django……
      • @qolin,sos-skil 的回答为您提供了一种制作这样一个表的方法(当然不是通过 manytomany)——我的想法只是向该表添加一个计数字段,而不是依赖于重复项(这只是一种更紧凑的方式来实现 multisets aka bag)。
      • @qollin:我不确定您是否可以依赖关系表中始终存在一个 id - 这可能是一个实现细节。您可以使用“直通”表构建多对多,该表可以包含其他列,例如 Alex 建议的计数字段。
      【解决方案3】:

      一种方法:

      class A(models.Model):
          ...
      
      class B(models.Model):
          ...
      
      class C(models.Model):
          a = models.ForeignKey(A)
          b = models.ForeignKey(B)
      

      然后:

      >>> a1 = A()
      >>> a2 = A()
      >>> b = B()
      >>> c1 = C(a=a1, b=b)
      >>> c2 = C(a=a2, b=b)
      >>> c3 = C(a=a1, b=b)
      

      所以,我们只需要:

      >>> assert C.objects.filter(b=b).count == 3
      >>> for c in C.objects.filter(b=b):
      ...     # do something with c.a
      

      【讨论】:

      • 我知道,不完全是您正在寻找的东西,而是一种做类似事情的方法:/
      猜你喜欢
      • 2013-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-11
      • 2021-09-18
      • 2013-06-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多