views:

336

answers:

2

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:

>>>MyGroup.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:

>>>MyGroup.members.person.name

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

A: 

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

Hope that helps a little :)

Monika Sulik
What you are saying makes perfect sense and fit how I understand models in django. What confused me was django docs about intemedaite model at http://docs.djangoproject.com/en/1.0/topics/db/models/#extra-fields-on-many-to-many-relationshipsThey show this example with the same models above:# Find all the groups with a member whose name starts with 'Paul'>>> Group.objects.filter(members__name__startswith='Paul')[<Group: The Beatles>]I would expect it to be:>>> Group.objects.filter(members__person__name__startswith='Paul')
pablo
It's "Group.objects.filter(members__name__startswith='Paul')" because members is a Manager. You have to make sure you understand that members is not a Membership object, but a ManyRelatedManager object. I think it might help you if you read more on what Managers are exactly. P.S. If you type "python manage.py shell" in a project that has those models, you can try putting in "Group.objects.filter(members__person__name__startswith='Paul')" and see what happens - maybe that will help you?
Monika Sulik
I understand that members is a manager and I read about managers.I don't understand how members decides to use the name for Person and not for Group. Both have this field. It is also possible that membership will have a field name. I would I filter this?
pablo
Every Manager works on specific kind of objects. The Manager in the members field works on Person objects and not on Membership objects. I've only accessed data in the intermediate table (how to do that is in the answer), I've never needed to filter it so far. But I suggest you try it out yourself in the python shell ("manage.py shell" command). I think you can use "Group.objects.filter(membership__date_joined='1962-02-12')". If Membership had a name field then "Group.objects.filter(membership__name='abc')" as opposed to "Group.objects.filter(members__name='Paul')".
Monika Sulik
For names of people "Group.objects.filter(membership__person__name='Paul')" would probably also work. But I'm not sure - I suggest you try it out yourself :) Mostly, Django's error messages are quite useful. Only occasionally have I been stumped by them.
Monika Sulik
got it. thanks 1
pablo
A: 

Use the manager of the membership class:

MyGroup.membership_set.all()

instead of:

MyGroup.members.all()

Alain Maringoni