I have two django model classes:
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
What I want to do is the following:
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
I am guessing that add() uses a set semantic, but how can I circumvent that? I tried looking into custom managers, but I am not sure if this the right way (seems complicated)...
Thanks in advance!
I think what you want is to use an intermediary model to form the M2M relationship using the through
keyword argument in the ManyToManyField. Sort of like the first answer above, but more "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)
When using the through
keyword, the usual M2M manipulation methods are no longer available (this means add
, create
, remove
, or assignment with =
operator). Instead you must create the intermediary model itself, like so:
>>> C.objects.create(a=a1, b=b)
However, you will still be able to use the usual querying operations on the model containing the ManyToManyField
. In other words the following will still work:
>>> b.a.filter(a=a1)
But maybe a better example is something like this:
>>> B.objects.filter(a__name='Test')
As long as the FK fields on the intermediary model are not designated as unique
you will be able to create multiple instances with the same FKs. You can also attach additional information about the relationship by adding any other fields you like to C
.
Intermediary models are documented here.
Django uses a relational DB for its underlying storage, and that intrinsically DOES have "set semantics": no way to circumvent THAT. So if you want a "multi-set" of anything, you have to represent it with a numeric field that counts how many times each item "occurs". ManyToManyField doesn't do that -- so, instead, you'll need a separate Model subclass which explicitly indicates the A and the B it's relating, AND has an integer property to "count how many times".
One way to do it:
class A(models.Model):
...
class B(models.Model):
...
class C(models.Model):
a = models.ForeignKey(A)
b = models.ForeignKey(B)
Then:
>>> a1 = A()
>>> a2 = A()
>>> b = B()
>>> c1 = C(a=a1, b=b)
>>> c2 = C(a=a2, b=b)
>>> c3 = C(a=a1, b=b)
So, we simply have:
>>> assert C.objects.filter(b=b).count == 3
>>> for c in C.objects.filter(b=b):
... # do something with c.a
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With