maxmoriss
5/22/2016 - 11:23 PM

An example of using many-to-many "through" to augment m2m relationships. See http://www.quora.com/How-do-you-query-with-a-condition-on-a-Man

An example of using many-to-many "through" to augment m2m relationships. See http://www.quora.com/How-do-you-query-with-a-condition-on-a-ManyToMany-model-in-Django for context.

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=200)
    groups = models.ManyToManyField('Group', through='GroupMember', related_name='people')
    
    class Meta:
        ordering = ['name']
    
    def __unicode__(self):
        return self.name
    
class Group(models.Model):
    name = models.CharField(max_length=200)
    
    class Meta:
        ordering = ['name']
    
    def __unicode__(self):
        return self.name
    
class GroupMember(models.Model):
    person = models.ForeignKey(Person, related_name='membership')
    group = models.ForeignKey(Group, related_name='membership')
    type = models.CharField(max_length=100)
    
    def __unicode__(self):
        return "%s is in group %s (as %s)" % (self.person, self.group, self.type)
        
        
        
        
from django.test import TestCase
from .models import Person, Group, GroupMember

class M2MThroughTest(TestCase):
    
    def setUp(self):
        # Create three people:
        self.joe = Person.objects.create(name='Joe')
        self.jim = Person.objects.create(name='Jim')
        self.bob = Person.objects.create(name='Bob')
            
        # And three groups:
        self.jocks = Group.objects.create(name='Jocks')
        self.nerds = Group.objects.create(name='Nerds')
        self.skaters = Group.objects.create(name='Skaters')
        
        # Every person is a member of every group, but
        # Joe admins jocks, Jim admins nerds, and Bob admins skaters.
        GroupMember.objects.create(person=self.joe, group=self.jocks, type="admin")
        GroupMember.objects.create(person=self.jim, group=self.jocks, type="member")
        GroupMember.objects.create(person=self.bob, group=self.jocks, type="member")
        
        GroupMember.objects.create(person=self.joe, group=self.nerds, type="member")
        GroupMember.objects.create(person=self.jim, group=self.nerds, type="admin")
        GroupMember.objects.create(person=self.bob, group=self.nerds, type="member")
        
        GroupMember.objects.create(person=self.joe, group=self.skaters, type="member")
        GroupMember.objects.create(person=self.jim, group=self.skaters, type="member")
        GroupMember.objects.create(person=self.bob, group=self.skaters, type="admin")
    
    def test_unfiltered_membership(self):
        # Which groups is Jim in?
        jims_groups = Group.objects.filter(people=self.jim)
        self.assertEqual(list(jims_groups), [self.jocks, self.nerds, self.skaters])
    
    def test_admin_groups(self):
        # But which groups does Jim admin?
        jim_admins = Group.objects.filter(people=self.jim, membership__type='admin')
        self.assertEqual(list(jim_admins), [self.nerds])
    
    def test_member_groups(self):
        # And which groups is Bob just a member of?
        bob_membership = Group.objects.filter(people=self.bob, membership__type='member')
        self.assertEqual(list(bob_membership), [self.jocks, self.nerds])