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?

Oct 16, 2020, 16:48 by [email protected]:

> Just to add, I don't think django supports symmetrical M2M relations with 
> additional data / explicit through model without the shim I suggested. 
>
> For example, this works:
>
> from > django.db > import > models
>
>
> class > Person(models.Model):
>     name = models.CharField(> max_length> => 255> )
>     friends = models.ManyToManyField(> "self"> , > symmetrical> => True> )
>
>     > def > __str__> (> self> ):
>         > return > self> .name
>
> >>> from people.models import Person
> >>> bill = Person.objects.create(name='bill')
> >>> ted = Person.objects.create(name='ted')
> >>> bill.friends.add(ted)
> >>> bill.friends.all()
> <QuerySet [<Person: ted>]>
> >>> ted.friends.all()
> <QuerySet [<Person: bill>]>
>
>
>
>
>
>
>> On 17 Oct 2020, at 10:28, coolguy <>> [email protected]>> > 
>> wrote:
>>
>>
>> With your example, you can also find the records through>>> 
>> ted.person_set.all().
>>  
>>  
>> On Friday, October 16, 2020 at 7:05:51 PM UTC-4 David Nugent wrote:
>>
>>> This is expected with your code.  You've created an asymmetric relationship 
>>> from bill to ted, but not the reverse. This would be appropriate in a 
>>> "follow" relationship. For symmetric relationships you need to create 
>>> records in both directions. There are a few ways to do this but a helper 
>>> function on the Person model is the most direct approach, something along 
>>> the lines:
>>>
>>>     def add_friendship(self, person, symmetric=True):
>>>         friendship = Friendship.objects.get_or_create(personA=self, 
>>> personB=person)
>>>         if symmetric:
>>>             # avoid recursion
>>>             person.add_friendship(self, False)
>>>         return friendship
>>>
>>>
>>> Regards, David
>>>
>>>
>>>>
>>>>
>>>> On 17 Oct 2020, at 05:38, gjgilles via Django users <>>>> 
>>>> [email protected] <>>>>> > wrote:
>>>>
>>>>
>>>> There are no responses to the same question on stackoverflow, so hopefully 
>>>> someone here can provide a solution. 
>>>> <https://stackoverflow.com/questions/64346385/how-to-make-a-recursive-manytomany-relationship-symmetrical-with-django>
>>>>  
>>>>
>>>> I've read the docs. 
>>>> <https://docs.djangoproject.com/en/3.0/ref/models/fields/#django.db.models.ManyToManyField.symmetrical>>>>>
>>>>   >>>> I've>>>>  >>>> read this question too, 
>>>> <https://stackoverflow.com/questions/4129439/how-to-make-recursive-manytomanyfield-relationships-that-have-extra-fields-symme>>>>>
>>>>   >>>> but the following code is not working as the Django docs describe.
>>>>
>>>> If>>>>  >>>> bill>>>>  >>>> and>>>>  >>>> ted >>>> are friends,>>>>  >>>> 
>>>> bill.friends.all()>>>>  >>>> should include>>>>  >>>> ted, >>>>  >>>> 
>>>> and>>>>  >>>> ted.friends.all()>>>>  >>>> should include>>>>  >>>> 
>>>> bill>>>> . This is not what Django does.>>>>  >>>> ted>>>> 's query is 
>>>> empty, while>>>>  >>>> bill>>>> 's query includes>>>>  >>>> ted>>>> .
>>>> # people.models>>>> from>>>>  django.db >>>> import>>>>  models>>>> 
>>>> class>>>>  >>>> Person>>>> (>>>> models.Model>>>> ):>>>>     name = 
>>>> models.CharField(max_length=>>>> 255>>>> )    friends = 
>>>> models.ManyToManyField(>>>> "self">>>> ,                                   
>>>>   through=>>>> 'Friendship'>>>> ,                                     
>>>> through_fields=(>>>> 'personA'>>>> , >>>> 'personB'>>>> ),                 
>>>>                     symmetrical=>>>> True>>>> ,                            
>>>>          )    >>>> def>>>>  >>>> __str__>>>> (>>>> self>>>> ):>>>>         
>>>> >>>> return>>>>  >>>> self.name <http://self.name/>>>>> class>>>>  >>>> 
>>>> Friendship>>>> (>>>> models.Model>>>> ):>>>>     personA = 
>>>> models.ForeignKey(Person, on_delete=models.CASCADE, related_name=>>>> 
>>>> 'personA'>>>> )    personB = models.ForeignKey(Person, 
>>>> on_delete=models.CASCADE, related_name=>>>> 'personB'>>>> )    start = 
>>>> models.DateField(null=>>>> True>>>> , blank=>>>> True>>>> )    end = 
>>>> models.DateField(null=>>>> True>>>> , blank=>>>> True>>>> )    >>>> 
>>>> def>>>>  >>>> __str__>>>> (>>>> self>>>> ):>>>>         >>>> return>>>>  
>>>> >>>> ' and '>>>> .join([str(self.personA), str(self.personB)])
>>>>
>>>>
>>>>
>>>> >>> import django
>>>> >>> django.__version__
>>>> '3.1.2'>>> >>>> from>>>>  people.models >>>> import>>>>  Person, 
>>>> Friendship>>>> >>> >>>> bill = Person(name=>>>> 'bill'>>>> )>>>> >>> >>>> 
>>>> bill.save()>>>> >>> >>>> ted = Person(name=>>>> 'ted'>>>> )>>>> >>> >>>> 
>>>> ted.save()>>>> >>> >>>> bill_and_ted = Friendship(personA=bill, 
>>>> personB=ted)>>>> >>> >>>> bill_and_ted.save()>>>> >>> >>>> 
>>>> bill.friends.all()<QuerySet [<Person: ted>]>>>>> >>> >>>> 
>>>> ted.friends.all()<QuerySet []>>>>> >>> >>>> ted.refresh_from_db()>>>> >>> 
>>>> >>>> ted.friends.all()<QuerySet []>>>>> >>> >>>> ted = 
>>>> Person.objects.get(name=>>>> 'ted'>>>> )>>>> >>> >>>> 
>>>> ted.friends.all()<QuerySet []>
>>>> Can someone please show me how to make this behave as expected.
>>>>
>>>>
>>>>
>>>> -- 
>>>>  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/MJmh5Qw--3-2%40tutanota.com 
>>>> <https://groups.google.com/d/msgid/django-users/MJmh5Qw--3-2%40tutanota.com?utm_medium=email&utm_source=footer>>>>>
>>>>  .
>>>>
>>>
>>>
>>
>> --
>>  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/f1b39d06-6100-4b6c-94a5-1ec60fa45f80n%40googlegroups.com
>>  
>> <https://groups.google.com/d/msgid/django-users/f1b39d06-6100-4b6c-94a5-1ec60fa45f80n%40googlegroups.com?utm_medium=email&utm_source=footer>>>
>>  .
>>
>
>
>
>
> --
>  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/A978B24A-1041-4850-AABA-224F2EFD2E9E%40uniquode.io
>  
> <https://groups.google.com/d/msgid/django-users/A978B24A-1041-4850-AABA-224F2EFD2E9E%40uniquode.io?utm_medium=email&utm_source=footer>>
>  .
>

-- 
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/MJnvNVy--3-2%40tutanota.com.

Reply via email to