Access fields in Django intermediate model

Viewed 18217

I'm creating a Person Group and Membership as described in Django docs for intermediate model.

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)
  members = models.ManyToManyField(Person, through='Membership')

  def __unicode__(self):
    return self.name

class Membership(models.Model):
  person = models.ForeignKey(Person)
  group = models.ForeignKey(Group)
  date_joined = models.DateField()
  invite_reason = models.CharField(max_length=64)

It is possible to access the Person from a Group object with:

>>>Group.members.name

Does Django creates another query to fetch the Person? Can I access the date_joined field from a Group object?

The thing that confuses me is that I would expect to get the Person name field with:

>>>Group.members.person.name

What happens if a Person has a field 'name' and also the intermediate model have a field 'name'.

2 Answers

The members field in your example is a ManyToManyField, so it's a way to access many people rather than one person. The object that is under the members field is actually a special type of Manager, not a Person:

>>> print my_group.members
<django.db.models.fields.related.ManyRelatedManager object at 0x181f7d0>

To understand better what a Manager is, see the documentation.

To access a person's name you would do for example:

>>> for person in my_group.members.all():
>>>     print person.name

You cannot access the fields in your Membership model via the Manager in the members field. To access any of the fields in it you would do:

>>> for membership in my_group.membership_set.all():
>>>     print membership.date_joined

And so if you had a field called name in your Membership model, you would access it like this:

>>> for membership in my_group.membership_set.all():
>>>     print membership.name

A second way to access a Person's name would be:

>>> for membership in my_group.membership_set.all():
>>>     print membership.person.name

Note that membership_set is a default name for the manager pointing towards the membership, but it can be changed by specifying related_name in the corresponding foreign key. For example if the foreign key from the Membership to the Group would be defined like such:

    group = models.ForeignKey(Group, related_name="group_members")

Then you would access the manager using group_members:

>>> for membership in my_group.group_members.all():
>>>     print membership.name

Hope that helps a little :)

Related