After playing with this to answer your question and to correct my previous
response, I found that it does work as documented when using a "through" model
without using "through_fields".
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=255)
friends = models.ManyToManyField("self", through='Friendship',
symmetrical=True)
def __str__(self):
return self.name
class Friendship(models.Model):
person_a = models.ForeignKey(Person, on_delete=models.CASCADE,
related_name='a')
person_b = models.ForeignKey(Person, on_delete=models.CASCADE,
related_name='b')
start = models.DateField(auto_now_add=True)
def __str__(self):
return f'{self.person_a.name} => {self.person_b.name}: {self.start}'
>>> from people.models import Person, Friendship
>>> bill = Person.objects.create(name='bill')
>>> rufus = Person.objects.create(name='rufus')
>>> bill.friends.add(ted)
>>> bill.friends.add(rufus)
>>> rufus.friends.add(bill)
>>> rufus.friends.add(ted)
>>> bill.friends.all()
<QuerySet [<Person: ted>, <Person: rufus>]>
>>> ted.friends.all()
<QuerySet [<Person: bill>, <Person: rufus>]>
>>> rufus.friends.all()
<QuerySet [<Person: bill>, <Person: ted>]>
>>> Friendship.objects.all()
<QuerySet [
<Friendship: bill => ted: 2020-10-17>,
<Friendship: ted => bill: 2020-10-17>,
<Friendship: bill => rufus: 2020-10-17>,
<Friendship: rufus => bill: 2020-10-17>,
<Friendship: rufus => ted: 2020-10-17>,
<Friendship: ted => rufus: 2020-10-17>
]>
In your case, naming the related_name the same as the field name may be an
issue. Since related_name becomes a pseudo field on the model in which it is
defined, so there is a potential clash in the namespace?
On 17 Oct 2020, at 11:20, gjgilles via Django users
<[email protected]<mailto:[email protected]>> wrote:
Thanks for all for the replies!
@David, the helper function works as expected.
>>> from people.models import Person, Friendship
>>> bill = Person(name='bill')
>>> bill.save()
>>> ted = Person(name='ted')
>>> ted.save()
>>> bill.add_friendship(ted, True)
(<Friendship: bill and ted>, True)
>>> bill.friends.all()
<QuersySet [<Person: ted>]>
>>> ted.friends.all()
<QuerySet [<Person: bill>]>
Also, @coolguy for my code, the correct call is >>> ted.personB.all() without
the helper function. ted.personA.all() returns an empty queryset without the
helper function.
While I'm here, the Django docs imply that
<https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.ManyToManyField.through>
intermediate models can be recursive and
symmetrical<https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.ManyToManyField.through>:
"Recursive relationships using an intermediary model and defined as symmetrical
(that is, with symmetrical=True, which is default) can't determine the
accessory names, as they would be the same. You need to set a related_name to
at least one of them. If you'd prefer Django not to create a backwards
relation, set related_name to '+'."
This implies Django makes the reverse relation by default. Am I
misunderstanding something?
--
You received this message because you are subscribed to the Google Groups
"Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-users/0B233FC5-01A3-469D-8F85-EFFE0C8F13EC%40uniquode.io.