Many to many lookups in Django

I had a bit of an interesting lookup I was trying to do in Django the other day which was confusing me until I sat down and actually worked my way through what I was trying to do. Turns out, django makes even somewhat complex relationship lookups fairly easy, but I thought I’d post about it as it’s not blindingly obvious at first glance.

Imagine the following models:


class Document(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(User, blank=True)
    content = models.TextField(blank=True)
    private = models.BooleanField(default=False)
class UserProfile(models.Model):
    user = models.ForeignKey(User, unique=True)
    friends = models.ManyToManyField(User, symmetrical=False,
                 related_name='user_friends')
    ignored = models.ManyToManyField(User, symmetrical=False,
                 related_name='user_ignored')

Imaginging the following Users:

  • Alice has 3 documents, 1 of which is private (meaning only friends can see it). She is friends with Bob, is ignoring Mallory and is apathetic towards Eve (meaning no stored relationship).
  • Mallory has 2 documents, both public and is apathetic towards everyone.
  • Bob has 1 document which is public and is also apathetic towards everyone.
  • Eve is ignoring Alice and is apathetic to Mallory and Bob

Users searching for documents should produce the following:

  • Bob searching for documents should see 6, as Alice has made him a friend and he can view her private documents.
  • Alice searching for documents should see 4, Bobs 1 and her 3. She doesn’t see Mallory’s public documents as Alice is ignoring Mallory.
  • Mallory searching for documents sees 5 – Alice’s public ones, her own 2 and Bobs 1. Alice ignoring her has no bearing on what Mallory can see, just that Alice doesn’t see Mallory’s docs.
  • Eve searching for documents sees 3 – Mallory and Bob’s public documents as she has ignored Alice.

First off, we want to get a list of people who have friended me which is a reverse lookup through the Many to Many relationship:
friendly_authors=self.user.user_friends.all()
Get all the people I’ve ignored:
my_ignored=UserProfile.objects.get(user=self.user).ignored.all()

Get a list of docs I can view – docs which are viewable, mine, or written by people who have friended me but whom I haven’t ignored:

docs=Document.objects.filter(
(Q(viewable=True) | Q(author=self.user) | Q(author__in=friendly_authors))
& ~Q(author__in=my_ignored)
)

Category: Web Development | Tags: , , Comment »


Leave a Reply



Back to top