tags:

views:

111

answers:

3

I have a model called Question and another one called Answer.

class Question(models.Model):
    text = models.CharField(max_length=140)

class Answer(models.Model): 
    user = models.ForeignKey(User)
    question = models.ForeignKey(Question)
    uncertain = models.BooleanField()
    text = models.CharField(max_length=30)

Now I'd like to have a QuerySet questions which is equivalent to Question.objects.all() but including the currently logged in user's answers. I'm almost completely sure that this could be accomplished by an explicit JOIN in SQL, but I'm certain there is a better way to do this using Django's ORM.

So how would I do that?

EDIT: To clarify, I'd like to be able to loop through the QuerySet, e.g. for q in questions and then be able to see whether or not the user has answered it through something like q.answered which could be a boolean, or q.answer which could be the Answer object itself (or None).

+1  A: 

Are you looking for something like this?

[ item.question for question in Answer.objects.filter(user=request.user) ]

Or perhaps you want to do a

questions = Question.objects.all()
for question in questions:
    question.answer = question.answer_set.filter(user=request.user)

I guess I'm not understanding the question and which direction you want to go...

EDIT: Ok, based on your edit, perhaps the latter... you can see if it's an empty list or tack on a .count() and see if the value is greater than 0.

uzi
The latter is the one I'm probably looking for. I'm curious though... The first one will obviously hit the database once, but how many times will the latter one hit the database?
Deniz Dogan
Yep, the latter seems to work, perfect! However, the question still remains; would it hit the database more than once?
Deniz Dogan
Once for the initial query, plus once for each question. I wonder... there may be a more efficient way using aggregates in the forthcoming 1.1 release... I'll have to think about it.
uzi
To avoid the multiple db hits you want to use select_related() on the Question.objects query. Nothing to do with aggregation.
ozan
It's still wasteful though because you're checking the answer_set for matching user for every question. I'll write up a more efficient approach for you.
ozan
FWIW, I don't think select_related() would work, because there is no FK from Question to Answer. Answer.objects.all().select_related() on the other hand, would select all answers and their respective questions, because there *is* a FK relation from Answer from Question.
Deniz Dogan
Ahh yes you're right. Sorry I had the relation backwards in my head.
ozan
+1  A: 

I think you're overthinking things. Essentially what you want to know (as far as I can tell - correct me if I'm wrong) is which questions have been answered by a given user, and which questions haven't (the complement). So:

user_answered_questions = set([answer.question for answer in user.answer_set.all()])

Then unanswered questions are everything else. So:

user_unanswered_questions = set(Question.objects.all()) - answered_questions

The point of doing it this way rather than uzi's solution is that you should use what you know sooner rather than later. If you filter the answer_set for every question by user, your initial query will be selecting every single answer and every single question in these tables. By starting from the known data (i.e. the user) we are selecting only answers and questions related to that user.

EDIT:

So if you then want to combine the two sets into a single list, there are a bunch of ways you could do this for instance by creating a list of tuples:

questions = [(q, q in user_answered_questions) for q in Question.objects.all()]

Then you can iterate over questions, retaining the order of the queryset:

for q in questions:
    print "it is %s that this user answered the \"%s\" question" % (q[1], q[0])

It's not pretty but you get the point - you've already determined what you wanted to determine, it's just a matter of contorting it to whatever data structure you happen to need.

ozan
That's an interesting solution! However, to make this whole thing work at all, I'll have to be able to loop through the questions in the right order in the template. In other words, I will not be listing the two categories separately, but in one and the same, with a marker at answered questions.
Deniz Dogan
Well you already know which questions are answered, it's just a matter of constructing the list in whatever shape or order suits you. I've edited to show a crude example of how you might do that.
ozan
A: 

What is wrong with

Question.objects.filter(answer__user=current_user).select_related()
ironfroggy
AFAIK, select_related() only works on "forward relations", which means that it would do nothing in this case. Answer.objects.all().select_related() however, would get all answers and their questions, since there is a FK relationship from Answer to Question (just not vice versa).
Deniz Dogan
Also, question objects don't have user as an attribute. It seems we're all mixing up questions and answers... in our answers... to this question.
ozan