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).
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.
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.
What is wrong with
Question.objects.filter(answer__user=current_user).select_related()
Related
Since each queryset is unique, what is the best way of subsetting a large queryset without generate too many sql queries in a loop?
For instance, I need to produce a report which requires me to loop through all of the data in a database:
for user in users:
notes = Note.objects.filter(owner=user.id)
for note in notes:
answers = Answer.filter(note_id = note.id)
for answer in answers:
#do something
As you can already see how bad this loop will be since each filter statement creates a queryset which hits the database.
What am I suppose to do in this situation to avoid calling the database thousands of times?
Thanks!
If users is QuerySet, then
answers = Answer.objects.filter(note__owner__in=users)
UPDATE: If you want to do something with Answer's note and owner, then
answers = Answer.objects.filter(note__owner__in=users).select_related('note', 'note__owner')
It requires one query.
Try this
answer = Answer.objects.filter(note__owner__in = user_ids)
I have long looked for an answer to this question, and have yet to find one. Hopefully s/o can help me out.
I have two models:
class UsedEverywhere(models.Model):
fk_OtherThing = models.ForeignKey(OtherThing)
...(many more fks)
class NewThing(models.Model):
fk_UsedEverywhere(UsedEverywhere)
newThingMember = models.IntegerField()
I simply want to join those two tables. It would be quite easy if I could just do something like
x = NewThing.objects.get()
x.fk_UsedEverwhere.fk_OtherThing ...
the problem is, the UsedEverywhere models is already used in many many places throughout my code base, and if i want to put 'NewThing' as the 'parent' of UsedEverywhere, I will have to update lots and lots of code so existing code that has something like:
x = x.filter(fk_OtherThing=val)
would have to change to
x = x.filter(fk_UsedEverywhere.fk_OtherThing=val)
and I am trying to avoid that. My SQL relationship are all set up exactly how I want them, i do not want to give UsedEverywhere a column for NewThing.
I want to do something like:
x = UsedEverywhere.objects.join(NewThing.fk_UsedEverywhere)
member = x.newThingMember
I found the solution:
Use a OneToOneField not a foreign key field https://docs.djangoproject.com/en/1.4/topics/db/examples/one_to_one/
As you can see in their examples, you can do something like
x = UsedEverywhere.objects.get(pk=1)
x.NewThing...
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
In order to avoid changing a lot of existing code, I try to add a self foreign key to a model, then the operations(filter,get,update,save,...) on the record will automatically performed on the "real record" if it has the self foreign key value.what need I do?
class Person(models.Model):
name = models.CharField(null=True,blank=True,max_length=50)
real_one = models.ForeignKey('self',null=True,blank=True)
I am expecting:
tom = Person.objects.create(name='Tom') # id=1
Person.objects.create(name='someone', real_one=tom) # id=2
someone = Person.objects.filter(name='someone')[0]
print someone.id # 1
print someone.name # Tom
someone.name = 'Jack'
someone.save()
tom = Person.objects.filter(name='Tom')[0] # sorry, this line added late
print tom.name # 'Jack'
IMHO, you're trying to do this in a denormalized fashion and it probably isn't going to work for you. One thing you could do is:
class Person(models.Model):
name = models.CharField(null=True,blank=True,max_length=50)
real_one = models.ForeignKey('self',null=True,blank=True)
#property
def real_name(self):
if(self.real_one):
return self.real_one.real_name
else:
return self.real_name
#real_name.setter
def real_name_setter(self, val):
if(self.real_one):
self.real_one.real_name = val
self.real_one.save()
else:
self.real_name = val
self.save()
but this creates a lot of database transactions. Note also the required side effect of the setter, since it has to save the record immediately lest it lose track of which record was changed. And this still has a problem in that the following case won't work:
someone.name = 'Jack'
someone.save()
print tom.name # 'Tom' <-- The 'tom' object now has stale data!
One object is being changed, yet (as far as Python is concerned) an entirely different object is really being changed. There's no reliable way to do this without changing Django and creating a mess (Django would have to internally track every object that referred to a given record, which plays havoc with the garbage collector, Django's queryset lazy-evaluation generators, and maybe even transactions).
Your best bet is to try a different approach, or live with the fact that your example isn't going to work. If you stick with this schema, every filter and exclude queryset call, and probably others, is going to need to know about this aliasing.
IMHO, you're much, much better off changing the schema: one table with names and one table with the "real" info for each person. The name table has foreign keys to the info table.
If you're trying to do something else, then you need to explain what a lot clearer in the question.
I have two models
class Subject(models.Model):
name = models.CharField(max_length=100,choices=COURSE_CHOICES)
created = models.DateTimeField('created', auto_now_add=True)
modified = models.DateTimeField('modified', auto_now=True)
syllabus = models.FileField(upload_to='syllabus')
def __unicode__(self):
return self.name
and
class Pastquestion(models.Model):
subject=models.ForeignKey(Subject)
year =models.PositiveIntegerField()
questions = models.FileField(upload_to='pastquestions')
def __unicode__(self):
return str(self.year)
Each Subject can have one or more past questions but a past question can have only one subject. I want to get a subject, and get its related past questions of a particular year. I was thinking of fetching a subject and getting its related past question.
Currently am implementing my code such that I rather get the past question whose subject and year correspond to any specified subject like
this_subject=Subject.objects.get(name=the_subject)
thepastQ=Pastquestion.objects.get(year=2000,subject=this_subject)
I was thinking there is a better way to do this. Or is this already a better way? Please Do tell ?
I think what you want is the related_name property of the ForeignKey field. This creates a link back to the Subject object and provides a manager you can use to query the set.
So to use this functionality, change the foreignkey line to:
subject=models.ForeignKey(Subject, related_name='questions')
Then with an instance of Subject we'll call subj, you can:
subj.questions.filter(year=2000)
I don't think this performs much differently to the technique you have used. Roughly speaking, SQL performance boils down a) whether there's an index and b) how many queries you're issuing. So you need to think about both. One way to find out what SQL your model usage is generating is to use SqlLogMiddleware - and alternatively play with the options in How to show the SQL Django is running It can be tempting when you get going to start issuing queries across relationships - e.g. q = Question.objects.get(year=2000, subject__name=SUBJ_MATHS) but unless you keep a close eye on these types of queries, you can and will kill your app's performance, badly.
Django's query syntax allows you to 'reach into' related objects.
past_questions = Pastquestion.objects.filter(year=2000, subject__name=subject_name)
I have the following Django model:
class Make:
name = models.CharField(max_length=200)
class MakeContent:
make = models.ForeignKey(Make)
published = models.BooleanField()
I'd like to know if it's possible (without writing SQL directly) for me to generate a queryset that contains all Makes and each one's related MakeContents where published = True.
Yes, I think you want
make = Make.objects.get(pk=1)
make.make_content_set.filter(published=True)
or maybe
make_ids = MakeContent.objects.filter(published=True).values_list('make_id', flat=True)
makes = Make.objects.filter(id__in=make_ids)
I know this is very old question, but I am answering. As I think my answer can help others. I have changed the model a bit as follows. I have used Django 1.8.
class Make(models.Model):
name = models.CharField(max_length=200)
class MakeContent(models.Model):
make = models.ForeignKey(Make, related_name='makecontent')
published = models.BooleanField()
I have used the following queryset.
Make.objects.filter(makecontent__published=True)
You should use distinct() to avoid the duplicate result.
Make.objects.filter(makecontent__published=True).distinct()
I hope it will help.
Django doesn't support the select_related() method for reverse foreign key lookups, so the best you can do without leaving Python is two database queries. The first is to grab all the Makes that contain MakeContents where published = True, and the second is to grab all the MakeContents where published = True. You then have to loop through and arrange the data how you want it. Here's a good article about how to do this:
http://blog.roseman.org.uk/2010/01/11/django-patterns-part-2-efficient-reverse-lookups/
Let me translate Spike's worded answer into codes for future viewers. Please note that each 'Make' can have zero to multiple 'MakeContent'
If the asker means to query 'Make' with AT LEAST ONE 'MakeContent' whose published=True, then Jason Christa's 2nd snippet answers the question.
The snippet is equivalent to
makes = Make.objects.select_related().filter(makecontent__published=True).distinct()
But if the asker means to query 'Make' with ALL 'MakeContent' whose published=True, then following the 'makes' above,
import operator
make_ids = [m.id for m in makes if
reduce(operator.and_, [c.published for c in m.makecontent_set.all()] )
]
makes_query = Make.objects.filter(id__in=make_ids)
contains the desired query.
One more time, it is not clear what was the question, but if it was desired that all related MakeContent objects must have been published this can work:
Make.objects.exclude(MakeContent_set__published=False)
And if at least one of them (as it was in other answers):
Make.objects.filter(MakeContent_set__published=True)