from datetime import datetime from django.contrib.auth.models import User from django.core import management from django.db import models # Forward declared intermediate model class Membership(models.Model): person = models.ForeignKey('Person') group = models.ForeignKey('Group') price = models.IntegerField(default=100) def __unicode__(self): return "%s is a member of %s" % (self.person.name, self.group.name) class UserMembership(models.Model): user = models.ForeignKey(User) group = models.ForeignKey('Group') price = models.IntegerField(default=100) def __unicode__(self): return "%s is a user and member of %s" % (self.user.username, self.group.name) class Person(models.Model): name = models.CharField(max_length=128) def __unicode__(self): return self.name class Group(models.Model): name = models.CharField(max_length=128) # Membership object defined as a class members = models.ManyToManyField(Person, through=Membership) user_members = models.ManyToManyField(User, through='UserMembership') def __unicode__(self): return self.name __test__ = {'API_TESTS':""" # Create some dummy data >>> bob = Person.objects.create(name='Bob') >>> jim = Person.objects.create(name='Jim') >>> rock = Group.objects.create(name='Rock') >>> roll = Group.objects.create(name='Roll') >>> frank = User.objects.create_user('frank','frank@example.com','password') >>> jane = User.objects.create_user('jane','jane@example.com','password') # Now test that the forward declared Membership works >>> Membership.objects.create(person=bob, group=rock) >>> Membership.objects.create(person=bob, group=roll) >>> Membership.objects.create(person=jim, group=rock) >>> bob.group_set.all() [, ] >>> roll.members.all() [] # Error messages use the model name, not repr of the class name >>> bob.group_set = [] Traceback (most recent call last): ... AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead. >>> roll.members = [] Traceback (most recent call last): ... AttributeError: Cannot set values on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead. >>> rock.members.create(name='Anne') Traceback (most recent call last): ... AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead. >>> bob.group_set.create(name='Funk') Traceback (most recent call last): ... AttributeError: Cannot use create() on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead. # Now test that the intermediate with a relationship outside # the current app (i.e., UserMembership) workds >>> UserMembership.objects.create(user=frank, group=rock) >>> UserMembership.objects.create(user=frank, group=roll) >>> UserMembership.objects.create(user=jane, group=rock) >>> frank.group_set.all() [, ] >>> roll.user_members.all() [] # Regression test for #8134 -- # m2m-through models shouldn't be serialized as m2m fields on the model. # Dump the current contents of the database as a JSON fixture >>> management.call_command('dumpdata', 'm2m_through_regress', format='json', indent=2) [ { "pk": 1, "model": "m2m_through_regress.membership", "fields": { "person": 1, "price": 100, "group": 1 } }, { "pk": 2, "model": "m2m_through_regress.membership", "fields": { "person": 1, "price": 100, "group": 2 } }, { "pk": 3, "model": "m2m_through_regress.membership", "fields": { "person": 2, "price": 100, "group": 1 } }, { "pk": 1, "model": "m2m_through_regress.usermembership", "fields": { "price": 100, "group": 1, "user": 1 } }, { "pk": 2, "model": "m2m_through_regress.usermembership", "fields": { "price": 100, "group": 2, "user": 1 } }, { "pk": 3, "model": "m2m_through_regress.usermembership", "fields": { "price": 100, "group": 1, "user": 2 } }, { "pk": 1, "model": "m2m_through_regress.person", "fields": { "name": "Bob" } }, { "pk": 2, "model": "m2m_through_regress.person", "fields": { "name": "Jim" } }, { "pk": 1, "model": "m2m_through_regress.group", "fields": { "name": "Rock" } }, { "pk": 2, "model": "m2m_through_regress.group", "fields": { "name": "Roll" } } ] # Check the XML serializer too, since it doesn't use the common implementation >>> management.call_command('dumpdata', 'm2m_through_regress', format='xml', indent=2) 1 1 100 1 2 100 2 1 100 1 1 100 1 2 100 2 1 100 Bob Jim Rock Roll """}