向ManyToManyField添加相同对象两次
我有两个 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() 这个方法可能是用来设置某种特定的语义的,但我该怎么绕过这个限制呢?我试着去了解自定义管理器,但我不太确定这是不是正确的方法(看起来有点复杂)……
提前谢谢你们!
3 个回答
2
有一种方法可以做到:
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
4
Django使用关系型数据库来存储数据,这种数据库本身就有“集合语义”,这点是无法改变的。所以如果你想要表示一个“多重集合”,你就得用一个数字字段来记录每个项目出现的次数。ManyToManyField并不能做到这一点,因此你需要创建一个单独的模型子类,明确表示它所关联的A和B,并且要有一个整数属性来“计算出现的次数”。
8
我觉得你想做的是使用一个中间模型,通过在 ManyToManyField 中使用 through
这个关键字来建立多对多关系。这有点像上面第一个答案,但更符合 Django 的风格。
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
这个关键字时,通常的多对多操作方法就不能用了(这意味着 add
、create
、remove
或者用 =
赋值的方式都不行)。相反,你必须自己创建这个中间模型,像这样:
>>> C.objects.create(a=a1, b=b)
不过,你仍然可以在包含 ManyToManyField
的模型上使用常规的查询操作。换句话说,下面的操作仍然可以正常工作:
>>> b.a.filter(a=a1)
但也许更好的例子是这样的:
>>> B.objects.filter(a__name='Test')
只要中间模型上的外键字段没有被标记为 unique
,你就可以创建多个具有相同外键的实例。你还可以通过在 C
中添加其他字段来附加关于关系的额外信息。
关于中间模型的详细信息可以在 这里 找到。