Many-To-One Magic
Django provides convenience methods when any model has a models.ForeignKey
field. See Many-To-One Relationships for more details. Check out this example to find what percentage of an answer each gender voted for?
Given a particular Answer
as answer
:
>>> answer.response_set.filter(user__profile__gender='M')
[<Repsonse 1>, <Repsonse 2>, <Repsonse 3>, ... ]
That will give you all of the responses where the user who answered was a male.
If you only want to know just the statistics and don't care about the actual Response objects returned, you can simply use the .count()
method like this:
>>> queryset = answer.response_set.all()
>>> total_responses_for_answer = queryset.count()
>>> number_of_males = queryset.filter(user__profile__gender='M').count()
>>> print 'Percent', (number_of_males / float(total_responses_for_answer)) * 100
98.1
Let me try to explain what's going on.
response_set.filter()
When model B
has a models.ForeignKey
field that points to model A
, Django uses the related_name
keyword argument to create a special method on model A
that gets all relations in model B
that point to the model A
instance.
If the related_name
keyword isn't specified, Django defaults the method name to <relationship>_set
.
In this case, the answer
field on the Response
model did not specify the related_name
keyword argument, so Django added the convenience method response_set
on to the Answer
model.
To see all Responses
that chose answer
, use the simple query:
>>> answer.response_set.all()
If you wanted, you could specify the related_name
to create your own relationship method. For example:
class Response(models.Model):
...
answer = models.ForeignKey(Answer, related_name='responses')
...
# and use the custom method name like so:
>>> answer = Answer.objects.get(pk=pk)
>>> answer.responses.all()
filter(user_profile_gender='M')
The second bit of magic happening is the JOINs
that you're looking for. The response_set
returns a queryset of Response
objects. The Response
model has a field called user
. The Profile
model has a relationship to User
via the models.OneToOne
field user
and the Profile
model also contains that user's gender (and other fun stuff). The fancy double underscore part user__profile__gender='M'
is simply saying:
Find all Response instances WHERE the Response's user's __ profile's __ gender equals 'M'