| | 979 | Extra data on many-to-many relationships |
| | 980 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| | 981 | |
| | 982 | When you're only dealing with mixing and matching pizzas and toppings, a standard |
| | 983 | ``ManyToManyField`` works great. For many situations, however, some extra data |
| | 984 | is necessary about relationships between models. For situations like this, |
| | 985 | Django allows for the specification of an intermediary many-to-many model. To |
| | 986 | use this functionality, specify a ``through`` keyword argument onto the |
| | 987 | ``ManyToManyField``. This is best illustrated with an example:: |
| | 988 | |
| | 989 | class Person(models.Model): |
| | 990 | # ... |
| | 991 | name = models.CharField(max_length=128) |
| | 992 | |
| | 993 | def __unicode__(self): |
| | 994 | return self.name |
| | 995 | |
| | 996 | class Group(models.Model): |
| | 997 | # ... |
| | 998 | name = models.CharField(max_length=128) |
| | 999 | members = models.ManyToManyField(Person, through='Membership') |
| | 1000 | |
| | 1001 | def __unicode__(self): |
| | 1002 | return self.name |
| | 1003 | |
| | 1004 | class Membership(models.Model): |
| | 1005 | person = models.ForeignKey(Person) |
| | 1006 | group = models.ForeignKey(Group) |
| | 1007 | date_joined = models.DateTimeField() |
| | 1008 | invite_reason = models.CharField(max_length=64) |
| | 1009 | |
| | 1010 | Now that you have set up your ``ManyToManyField`` to use your intermediary |
| | 1011 | model (Membership, in this case), you're ready to use the convenience methods |
| | 1012 | provided by that ``ManyToManyField``. Here's an example of how you can query |
| | 1013 | for and use these models:: |
| | 1014 | |
| | 1015 | >>> ringo = Person.objects.create(name="Ringo Starr") |
| | 1016 | >>> paul = Person.objects.create(name="Paul McCartney") |
| | 1017 | >>> beatles = Group.objects.create(name="The Beatles") |
| | 1018 | >>> m1 = Membership.objects.create(person=ringo, group=beatles, |
| | 1019 | ... date_joined=datetime(1962, 8, 16), |
| | 1020 | ... invite_reason= "Needed a new drummer.") |
| | 1021 | >>> beatles.members.all() |
| | 1022 | [<Person: Ringo Starr>] |
| | 1023 | >>> ringo.group_set.all() |
| | 1024 | [<Group: The Beatles>] |
| | 1025 | >>> m2 = Membership.objects.create(person=paul, group=beatles, |
| | 1026 | ... date_joined=datetime(1960, 8, 1), |
| | 1027 | ... invite_reason= "Wanted to form a band.") |
| | 1028 | >>> beatles.members.all() |
| | 1029 | [<Person: Ringo Starr>, <Person: Paul McCartney>] |
| | 1030 | |
| | 1031 | As you can see, creating ``Membership`` objects automatically adds the |
| | 1032 | ``Person`` objects to the ``beatles.members`` queryset. This means that you |
| | 1033 | can do anyting that you would do on a normal queryset, like ``filter`` or |
| | 1034 | ``exclude``. |
| | 1035 | |
| | 1036 | .. note:: |
| | 1037 | |
| | 1038 | As soon as an intermediary model is specified, the ``add`` and |
| | 1039 | ``remove`` methods become unavailable on the descriptors added by the |
| | 1040 | ``ManyToManyField``. For example, something like |
| | 1041 | ``beatles.members.add(paul)`` will no longer work. |
| | 1042 | |
| | 1043 | For more examples and ideas on how to work with intermediary models, |
| | 1044 | `see the tests`_. |
| | 1045 | |
| | 1046 | .. _`see the tests`: http://code.djangoproject.com/browser/django/trunk/tests/modeltests/m2m_manual/models.py |
| | 1047 | |