Thursday, June 25, 2020

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+unsubscribe@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.

No comments:

Post a Comment