38

Basically I need a graceful way to do the following:-

obj1 = Model1.objects.select_related('model2').get(attribute1=value1)
obj2 = Model1.objects.select_related('model2').get(attribute2=value2)
model2_qs = QuerySet(model=Model2, qs_items=[obj1.model2,obj2.model2])

I may not be thinking right, but doing something like the following seems infinitely stupid to me.: -

obj1 = Model1.objects.select_related('model2').get(attribute1=value1)
model2_qs = Model2.objects.filter(pk=obj1.model2.pk)

Yes, I need to end up with a QuerySet of Model2 for later use (specifically to pass to a Django form).

In the first code block above,even if I use filter instead of get I will obviously have a QuerySet of Model1. Reverse lookups may not always be possible in my case.

chefsmart
  • 6,873
  • 9
  • 42
  • 47
  • 1
    Sorry, it's not clear what you are actually trying to do. Please show what you want to start and end with. – Daniel Roseman Aug 03 '10 at 14:31
  • What I'm trying to say is, if I can already refer to Model2 objects by using obj1.model2, it would be great to have a way to add these objects to a QuerySet of Model2 objects. – chefsmart Aug 03 '10 at 18:05
  • I also cannot follow what you're after. If you can post the relevant code for your models and add a sentence or two of general description of what you're after we might be able to help. – Brian Luft Aug 03 '10 at 21:04
  • Well, my question is "Is it possible to manually add objects to a QuerySet?" – chefsmart Aug 05 '10 at 04:43
  • The problem is that a queryset (as it's name implies) represents a database query - both before and after the query is actually made (they are lazily evaluated). You have got two queries - and therefore two querysets. Whilst you can fake something that might behave in many ways like a queryset - there will be edge cases where it might fail as it isn't really a queryset. You could create a single query that combines your two queries and use that but it's unclear what that would achieve. I would repeat the 'why' of other responders. This smells a little like premature optimization. – Andy Baker Jun 15 '14 at 16:41

3 Answers3

48

If you're simply looking to create a queryset of items that you choose through some complicated process not representable in SQL you could always use the __in operator.

wanted_items = set()
for item in model1.objects.all():
    if check_want_item(item):
        wanted_items.add(item.pk)

return model1.objects.filter(pk__in = wanted_items)

You'll obviously have to adapt this to your situation but it should at least give you a starting point.

JudoWill
  • 4,741
  • 2
  • 36
  • 48
  • Thank you. Can you explain if this method has any drawbacks? For example, let's say the check is done is a simple check such as if a field is smaller or greater than a number. What is the difference between using your solution and `__gt`? My other question is, is there a method to supply a function (lambda or other) to the `filter`? – MSH Jun 23 '23 at 13:49
17

To manually add objects to a QuerySet, try _result_cache:

objs = ObjModel.objects.filter(...)
len(objs) #or anything that will evaluate and hit the db
objs._result_cache.append(yourObj)

PS: I did not understand (or tried to) chefsmart's question but I believe this answers to the question in title.

Arthur
  • 1,974
  • 2
  • 21
  • 28
  • 9
    This uses the internal implementation of the API, that may change in any upgrade. – anizzomc Mar 12 '15 at 13:59
  • 1
    It should be obvious, but I'll note to anyone using this that executing additional commands such as fliters, orders, aggregates, etc, will clear anything you've appended as it accesses the database again rather than using the cache. – Joseph Coco Nov 20 '20 at 17:43
6

You can't manually add objects to a QuerySet. But why don't you put them in a list ?

obj1 = Model1.objects.select_related('model2').get(attribute1=value1)
obj2 = Model1.objects.select_related('model2').get(attribute2=value2)
model2 = list(obj1, obj2)
Nicu Surdu
  • 8,172
  • 9
  • 68
  • 108