Re: Symmetrical, Self-referencing ManyToManyField with Through Table
Nice solution. Thanks! On Thursday, June 25, 2020 at 5:49:23 PM UTC-4, Dan Madere wrote: > > I don't know of a way to configure the admin do that, but one solution > would be to use signals to notice when one-way records are created, then > automatically create the record for reverse relationship. We need to notice > when records are deleted as well. This approach would allow showing one > inline that shows the combined results. I was able to get it working with > this: https://gist.github.com/dgmdan/e7888a73c14446dccd7ad9aaf5055b10 > Hope this helps! > -- 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 django-users+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/d9cd8467-74c5-4915-a452-15f4fc2d03bfo%40googlegroups.com.
Re: Symmetrical, Self-referencing ManyToManyField with Through Table
I don't know of a way to configure the admin do that, but one solution would be to use signals to notice when one-way records are created, then automatically create the record for reverse relationship. We need to notice when records are deleted as well. This approach would allow showing one inline that shows the combined results. I was able to get it working with this: https://gist.github.com/dgmdan/e7888a73c14446dccd7ad9aaf5055b10 Hope this helps! On Thursday, June 25, 2020 at 1:53:45 PM UTC-4 jes...@gmail.com wrote: > After reviewing the tests, I think I now understand what is going on. > > Basically, for symmetric ManyToManyField, Django creates entries for both > directions automatically if the correct interface is used. From the test > models: > > class PersonSelfRefM2M(models.Model): > name = models.CharField(max_length=5) > sym_friends = models.ManyToManyField('self', > through='SymmetricalFriendship', symmetrical=True) > > > class SymmetricalFriendship(models.Model): > first = models.ForeignKey(PersonSelfRefM2M, models.CASCADE) > second = models.ForeignKey(PersonSelfRefM2M, models.CASCADE, > related_name='+') > date_friended = models.DateField() > > > And the tests: > > def test_self_referential_symmetrical(self): > tony = PersonSelfRefM2M.objects.create(name='Tony') > chris = PersonSelfRefM2M.objects.create(name='Chris') > SymmetricalFriendship.objects.create( > first=tony, second=chris, date_friended=date.today(), > ) > self.assertSequenceEqual(tony.sym_friends.all(), [chris]) > # Manually created symmetrical m2m relation doesn't add mirror entry > # automatically. > self.assertSequenceEqual(chris.sym_friends.all(), []) > SymmetricalFriendship.objects.create( > first=chris, second=tony, date_friended=date.today() > ) > self.assertSequenceEqual(chris.sym_friends.all(), [tony]) > > def test_add_on_symmetrical_m2m_with_intermediate_model(self): > tony = PersonSelfRefM2M.objects.create(name='Tony') > chris = PersonSelfRefM2M.objects.create(name='Chris') > date_friended = date(2017, 1, 3) > tony.sym_friends.add(chris, through_defaults={'date_friended': > date_friended}) > self.assertSequenceEqual(tony.sym_friends.all(), [chris]) > self.assertSequenceEqual(chris.sym_friends.all(), [tony]) > friendship = tony.symmetricalfriendship_set.get() > self.assertEqual(friendship.date_friended, date_friended) > > > So the tests show that the add() method needs to be used to create both > sides of the relationship. I assume that the Admin page does not use the > add method, but creates the intermediate entries which would require both > the be added for each entry. > > Is it possible to configure the Admin models to use add() or to create > both directions automatically? > > Thanks! > > > -- 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 django-users+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/b8e0927d-6079-4a0a-91db-044d6f393998n%40googlegroups.com.
Re: Symmetrical, Self-referencing ManyToManyField with Through Table
After reviewing the tests, I think I now understand what is going on. Basically, for symmetric ManyToManyField, Django creates entries for both directions automatically if the correct interface is used. From the test models: class PersonSelfRefM2M(models.Model): name = models.CharField(max_length=5) sym_friends = models.ManyToManyField('self', through='SymmetricalFriendship', symmetrical=True) class SymmetricalFriendship(models.Model): first = models.ForeignKey(PersonSelfRefM2M, models.CASCADE) second = models.ForeignKey(PersonSelfRefM2M, models.CASCADE, related_name='+') date_friended = models.DateField() And the tests: def test_self_referential_symmetrical(self): tony = PersonSelfRefM2M.objects.create(name='Tony') chris = PersonSelfRefM2M.objects.create(name='Chris') SymmetricalFriendship.objects.create( first=tony, second=chris, date_friended=date.today(), ) self.assertSequenceEqual(tony.sym_friends.all(), [chris]) # Manually created symmetrical m2m relation doesn't add mirror entry # automatically. self.assertSequenceEqual(chris.sym_friends.all(), []) SymmetricalFriendship.objects.create( first=chris, second=tony, date_friended=date.today() ) self.assertSequenceEqual(chris.sym_friends.all(), [tony]) def test_add_on_symmetrical_m2m_with_intermediate_model(self): tony = PersonSelfRefM2M.objects.create(name='Tony') chris = PersonSelfRefM2M.objects.create(name='Chris') date_friended = date(2017, 1, 3) tony.sym_friends.add(chris, through_defaults={'date_friended': date_friended}) self.assertSequenceEqual(tony.sym_friends.all(), [chris]) self.assertSequenceEqual(chris.sym_friends.all(), [tony]) friendship = tony.symmetricalfriendship_set.get() self.assertEqual(friendship.date_friended, date_friended) So the tests show that the add() method needs to be used to create both sides of the relationship. I assume that the Admin page does not use the add method, but creates the intermediate entries which would require both the be added for each entry. Is it possible to configure the Admin models to use add() or to create both directions automatically? Thanks! -- 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 django-users+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/3ad9f7f2-539f-4b66-9717-863d5f412ef4o%40googlegroups.com.
Re: Symmetrical, Self-referencing ManyToManyField with Through Table
On Thursday, June 25, 2020 at 10:26:25 AM UTC-4, Dan Madere wrote: > > I tried out the example code, and can replicate this. What's interesting > is that if I try removing the ContactConnection model, and the "through" > attribute, this allows Django to create the intermediate table on its own, > and then your get_connections() method works as expected! It seems like > using a custom through table is causing this somehow. I haven't found a fix > but will keep looking. Dan, thanks for looking into this issue. I found the unit tests for this capability in https://github.com/django/django/tree/master/tests/m2m_through which seems to indicate that it does work. I updated my version of Django to 3.1.b1 and copied the models and tests into my code. Neither the admin nor queries on the ManyToManyField return the full "symmetric" results. Any other ideas? Thanks! -- 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 django-users+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/05819cd1-7c67-4c64-bfce-69934315782eo%40googlegroups.com.
Re: Symmetrical, Self-referencing ManyToManyField with Through Table
I tried out the example code, and can replicate this. What's interesting is that if I try removing the ContactConnection model, and the "through" attribute, this allows Django to create the intermediate table on its own, and then your get_connections() method works as expected! It seems like using a custom through table is causing this somehow. I haven't found a fix but will keep looking. On Thursday, June 25, 2020 at 8:52:47 AM UTC-4 jes...@gmail.com wrote: > Mike, Thanks for your suggestions. > > Just a stab in the dark - have you tried giving from_contact a >> related_name? >> > > Yes, I have tried a few different combinations of providing a single > related_name and various naming conventions when providing related_name on > both ForeignKey fields without success. > > >> Another stab ... maybe you could just display the >> ContactConnectionAdmin(admin.ModelAdmin) with model=ContactConnection? >> >> > Yes, adding a ModelAdmin for the through table will show all of the > relationships, but it still requires searching both ForeignKey fields to > capture all of the relationships, regardless of which Contact hey were > created on. > > Workarounds are to add two Inlines, one for each through table foreign > key, or perform a compound query to combine the two results. I just figured > that since the capability was added in Django 3.0, that the symmetric > queries were included as well. My guess is that it is possible with the > correct configuration / naming conventions. I'll dive into the code to see > if anything pops up. > > Thanks! > -- 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 django-users+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/7ee482c1-60ea-482f-8ffb-5f71ff92a1edn%40googlegroups.com.
Re: Symmetrical, Self-referencing ManyToManyField with Through Table
Mike, Thanks for your suggestions. Just a stab in the dark - have you tried giving from_contact a > related_name? > Yes, I have tried a few different combinations of providing a single related_name and various naming conventions when providing related_name on both ForeignKey fields without success. > Another stab ... maybe you could just display the > ContactConnectionAdmin(admin.ModelAdmin) with model=ContactConnection? > > Yes, adding a ModelAdmin for the through table will show all of the relationships, but it still requires searching both ForeignKey fields to capture all of the relationships, regardless of which Contact hey were created on. Workarounds are to add two Inlines, one for each through table foreign key, or perform a compound query to combine the two results. I just figured that since the capability was added in Django 3.0, that the symmetric queries were included as well. My guess is that it is possible with the correct configuration / naming conventions. I'll dive into the code to see if anything pops up. Thanks! -- 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 django-users+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/1f730af3-11cb-49c6-a62f-e147107233a0o%40googlegroups.com.
Re: Symmetrical, Self-referencing ManyToManyField with Through Table
On 25/06/2020 8:29 am, Jim Shepherd wrote: I am unable to get a symmetrical, self-referencing ManyToManyField with a through table to actually be symmetrical, at least through the Admin interface. I am using Django 3.0 so it looks like the capability is supported. Models: class Contact(models.Model): last_name = models.TextField(default='', blank=True) connections = models.ManyToManyField('self', through='ContactConnection', symmetrical=True, blank=True) def get_connections(self): return ', '.join([str(p.last_name)for pin self.connections.all()]) class ContactConnection(models.Model): to_contact = models.ForeignKey(Contact, related_name='contacts', on_delete=models.CASCADE, null=False) from_contact = models.ForeignKey(Contact, on_delete=models.CASCADE, null=False) comments = models.TextField(default='', blank=True) Just a stab in the dark - have you tried giving from_contact a related_name? Admin: class ContactConnectionInline(admin.TabularInline): model = Contact.connections.through fk_name ='from_contact' extra =0 @admin.register(Contact) class ContactAdmin(admin.ModelAdmin): ordering = ('last_name',) list_display = ['last_name', 'get_connections'] inlines = (ContactConnectionInline) From the Admin page, when viewing a Contact, only one-direction of the ContactConnection relationship is shown which makes sense due to the fk_name='from_contact attribute fixing the direction. But it seems that at least the get_connections() method would get both sides. Is there a way either through a query or preferably an Inline to show the Contacts on both sides of the ContactConnection? Another stab ... maybe you could just display the ContactConnectionAdmin(admin.ModelAdmin) with model=ContactConnection? I haven't advanced to 3.x so I don't know what it is capable of. Thanks, Jim -- 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 django-users+unsubscr...@googlegroups.com <mailto:django-users+unsubscr...@googlegroups.com>. To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/91dad4bc-7eba-461d-89f1-aaf173e80831o%40googlegroups.com <https://groups.google.com/d/msgid/django-users/91dad4bc-7eba-461d-89f1-aaf173e80831o%40googlegroups.com?utm_medium=email_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 django-users+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/174d1c37-cc87-4015-89c0-83f325f8d8c1%40dewhirst.com.au.
Symmetrical, Self-referencing ManyToManyField with Through Table
I am unable to get a symmetrical, self-referencing ManyToManyField with a through table to actually be symmetrical, at least through the Admin interface. I am using Django 3.0 so it looks like the capability is supported. Models: class Contact(models.Model): last_name = models.TextField(default='', blank=True) connections = models.ManyToManyField('self', through='ContactConnection', symmetrical=True, blank=True) def get_connections(self): return ', '.join([str(p.last_name) for p in self.connections.all()]) class ContactConnection(models.Model): to_contact = models.ForeignKey(Contact, related_name='contacts', on_delete=models.CASCADE, null=False) from_contact = models.ForeignKey(Contact, on_delete=models.CASCADE, null=False) comments = models.TextField(default='', blank=True) Admin: class ContactConnectionInline(admin.TabularInline): model = Contact.connections.through fk_name = 'from_contact' extra = 0 @admin.register(Contact) class ContactAdmin(admin.ModelAdmin): ordering = ('last_name',) list_display = ['last_name', 'get_connections'] inlines = (ContactConnectionInline) >From the Admin page, when viewing a Contact, only one-direction of the ContactConnection relationship is shown which makes sense due to the fk_name='from_contact attribute fixing the direction. But it seems that at least the get_connections() method would get both sides. Is there a way either through a query or preferably an Inline to show the Contacts on both sides of the ContactConnection? Thanks, Jim -- 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 django-users+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/91dad4bc-7eba-461d-89f1-aaf173e80831o%40googlegroups.com.