Heh, I just realized that aRkadeFR had replied with a similar idea to use a
property. At least I know I'm not too far off on my thinking. :-D

-James

On Thu, Feb 26, 2015 at 11:02 AM, James Schneider <[email protected]>
wrote:

> Whoops, accidentally sent that last one too early, here's the continuation:
>
> However, that probably doesn't buy you much since you are still doing an
> extra query for every Desk you pull from your original query.
>
> Funny enough, I was googling around for an answer here, and stumbled
> across this:
>
>
> https://docs.djangoproject.com/en/1.7/ref/models/queries/#django.db.models.Prefetch
>
> which I think is what you were referring to initially in your OP. I wasn't
> even aware of its existence. Prefetch() is a helper class for
> prefetch_related(). Taking a quick glance through the source code, I would
> imagine that it probably won't help you much, since the functionality of
> that class only controls the action of prefetch_related().
>
>
> Zooming out a bit, the crux of your problem is this: An attribute you wish
> to populate is not an FK or M2M field, it is an entirely separate Queryset
> with some moderately complex filters. The built-in ORM functionality for
> pre-loading via prefetch/select_related() is expecting a FK or M2M
> relationship and can't  use another queryset AFAIK. The high-level
> functionality of prefetch_related() is probably close to what you want,
> which is to run a single second query to collect all of
> the favorite_or_nearby_chairs for all of the Desks in your original query,
> and then glue everything together behind the scenes in Python to make the
> desk_obj.favorite_or_nearby_chairs available seamlessly.
>
> I would then wonder if there is another way to organize this data to make
> it easier to work with? How about adding a 'favorite_chairs' field to the
> Desk model that has an M2M to Chair? I would also update your
> 'nearby_desks' model field to use 'nearby_chairs' as the related_field.
>
> Then you could do something like the following:
>
> desks = Desk.objects.filter(<filter here>).select_related('nearby_chairs',
> 'favorite_chairs')
>
> Then, you can modify your model with a property that will return the
> concatenation of nearby_chairs and favorite_chairs:
>
> class Desk(models.Model):
>     @property
>     def favorite_or_nearby_chairs(self):
>         return self.nearby_chairs.all() + self.favorite_chairs
>
> I don't believe this will spawn another query, since select_related() will
> have already run and have the results cached. You may also want to consider
> moving 'nearby_desks' out of Chair and renaming it to 'nearby_chairs' in
> Desk, and using a related_name of 'nearby_desks' instead. Then you can
> remove the .all() from the property definition from above and it definitely
> won't spawn a query. Obviously you'll need to create other processes that
> will populate desk.favorite_chairs, which may or may not be feasible.
>
> TL;DR; I don't believe you can pre-fetch anything because of the extra SQL
> logic needed to calculate the favorite_or_nearby_chairs attribute. It might
> be possible via raw SQL though. Reformatting your data models may lead to
> an easier time since you can then take advantage of the some of the
> optimizations Django offers.
>
> I'm slightly out in right field on this one, so YMMV, but taking a hard
> look at the current model design would be where I would start to try and
> eliminate the need for that custom queryset.
>
> Again, the django-debug-toolbar is your friend in these cases, but
> obviously a high number of even relatively fast queries can have a
> detrimental effect on your load times. Also ensure that the fields you are
> using to filter contain indexes, if appropriate/available.
>
> -James
>
>
>
>
> On Thu, Feb 26, 2015 at 10:17 AM, James Schneider <[email protected]
> > wrote:
>
>> Yep, looks like I misunderstood.
>>
>> So, you want to have something like this pseudo code:
>>
>> desks = Desk.objects.filter(<some filter>)
>> for desk in desks:
>>     print desk.favorite_or_nearby_chairs
>>
>> And have favorite_or_nearby_chairs be pre-populated with your Chair
>> queryset mentioned earlier?
>>
>> Due to the nature of the custom Chair queryset, I doubt you can do any
>> sort of pre-fetching that would reduce the number of queries. You can
>> probably simulate the effect of prefetch_related() though by overriding the
>> __init__() method of your Desk model, and having the Desk model
>> populate favorite_or_nearby_chairs whenever a Desk object is created.
>>
>> class Desk(models.Model)
>>     def __init__(self):
>>         # using list() to force the queryset to be evaluated
>>         self.favorite_or_nearby_chairs = list(<custom Chair queryset>)
>>
>>
>> However, that probably doesn't buy you much since you are still doing an
>> extra query for every Desk you pull from your original query.
>>
>> Funny enough, I was googling around for an answer here, and stumbled
>> across this:
>>
>>
>>
>>
>> On Thu, Feb 26, 2015 at 4:19 AM, aRkadeFR <[email protected]> wrote:
>>
>>>  got it, so you want to prefetch but not all chairs.
>>>
>>> I will def follow this thread to see the possibilities of Prefetch :)
>>>
>>>
>>> On 02/26/2015 12:52 PM, Ram Rachum wrote:
>>>
>>> There may be a big number of chairs, and I don't want all the chairs
>>> prefetched. I want to have the database filter them according to the
>>> queryset I specified in a single call, I don't want to filter them in
>>> Python or make a new call to filter them.
>>>
>>>  Thanks,
>>> Ram.
>>>
>>> On Thu, Feb 26, 2015 at 1:48 PM, aRkadeFR <[email protected]> wrote:
>>>
>>>>  I may not have completely understand your problem, but
>>>> why not prefetching all the chairs? and then with the (new)
>>>> attribute favorite_or_nearby_chairs loading only the favorite
>>>> or nearby one?
>>>>
>>>> like:
>>>> @property
>>>> def favorite_or_nearby_chairs(self):
>>>>     for chair in self.chair_set.all():
>>>>           #filter...
>>>>           ans += ...
>>>>     return ans
>>>>
>>>> It will only hit the DB once thanks to the first join of desk
>>>> <-> chair.
>>>>
>>>>
>>>> On 02/26/2015 11:28 AM, cool-RR wrote:
>>>>
>>>>  James, you misunderstood me.
>>>>
>>>>  There isn't supposed to be a `favorite_or_nearby_chairs` attribute.
>>>> That's the new attribute I want the prefetching to add to the `Desk`
>>>> queryset that I need. Also, I don't understand why you'd tell me to add a `
>>>> .select_related('nearby_desks')` to my query. Are you talking about
>>>> the query that starts with `Chair.objects`? I'm not looking to get a
>>>> `Chair` queryset. I'm looking to get a `Desk` queryset, which has a
>>>> prefetched attribute `favorite_or_nearby_chairs` which contains the
>>>> `Chair` queryset I wrote down.
>>>>
>>>>
>>>>  Thanks,
>>>> Ram.
>>>>
>>>> On Thursday, February 26, 2015 at 6:02:15 AM UTC+2, James Schneider
>>>> wrote:
>>>>>
>>>>> Well, the Desk model you provided is blank, but I'll believe you that
>>>>> there's a favorite_or_nearby_chairs attribute. ;-)
>>>>>
>>>>> Should be relatively simple. Just add a
>>>>> .select_related('nearby_desks') to your existing query and that should 
>>>>> pull
>>>>> in the associated Desk object in a single query. You can also substitute 
>>>>> in
>>>>> prefetch_related(), although you'll still have two queries at that point.
>>>>>
>>>>> If you are trying to profile your site, I would recommend the
>>>>> Django-debug-toolbar. That should tell you whether or not that query set 
>>>>> is
>>>>> the culprit.
>>>>>
>>>>> -James
>>>>> On Feb 25, 2015 1:28 PM, "Ram Rachum" <[email protected]> wrote:
>>>>>
>>>>>> Hi James,
>>>>>>
>>>>>>  I've read the docs but I still couldn't figure it out. My queryset
>>>>>> works great in production, I'm trying to optimize it because our 
>>>>>> pageloads
>>>>>> are too slow. I know how to use querysets in Django pretty well, I just
>>>>>> don't know how to use `Prefetch`.
>>>>>>
>>>>>>  Can you give me the solution for the simplified example I gave?
>>>>>> This might help me figure out what I'm not understanding. One thing that
>>>>>> might be unclear with the example I gave, is that I meant I want to get a
>>>>>> queryset for `Desk` where every desk has an attribute names 
>>>>>> `favorite_or_nearby_chairs`
>>>>>> which contains the queryset of chairs that I desrcibed, prefetched.
>>>>>>
>>>>>>
>>>>>>  Thanks,
>>>>>> Ram.
>>>>>>
>>>>>> On Wed, Feb 25, 2015 at 11:18 PM, James Schneider <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>>  I assume that you are talking about the select_related() and
>>>>>>> prefetch_related() queryset methods?
>>>>>>>
>>>>>>>
>>>>>>> https://docs.djangoproject.com/en/1.7/ref/models/querysets/#select-related
>>>>>>>
>>>>>>> https://docs.djangoproject.com/en/1.7/ref/models/querysets/#prefetch-related
>>>>>>>
>>>>>>>  Both of those sections have excellent examples, and detail what
>>>>>>> the differences are (primarily joins vs. separate queries, 
>>>>>>> respectively).
>>>>>>>
>>>>>>>  For better help, you'll need to go into more detail about the
>>>>>>> queries you are trying to make, what you've tried (with code examples if
>>>>>>> possible), and the results/errors you are seeing.
>>>>>>>
>>>>>>>  In general, I would try to get an initial queryset working and
>>>>>>> gathering the correct results first before looking at optimizations 
>>>>>>> such as
>>>>>>> select_related(). Any sort of pre-fetching will only confuse the 
>>>>>>> situation
>>>>>>> if the base queryset is incorrect.
>>>>>>>
>>>>>>>  -James
>>>>>>>
>>>>>>> On Wed, Feb 25, 2015 at 12:05 PM, cool-RR <[email protected]>
>>>>>>> wrote:
>>>>>>>
>>>>>>>> Hi guys,
>>>>>>>>
>>>>>>>>  I'm trying to solve a problem using the new `Prefetch` but I
>>>>>>>> can't figure out how to use it.
>>>>>>>>
>>>>>>>>  I have these models:
>>>>>>>>
>>>>>>>>       class Desk(django.db.models.Model):
>>>>>>>>         pass
>>>>>>>>
>>>>>>>>     class Chair(django.db.models.Model):
>>>>>>>>         desk = django.db.models.Foreignkey('Desk',
>>>>>>>> related_name='chair',)
>>>>>>>>         nearby_desks = django.db.models.ManyToManyField(
>>>>>>>>             'Desk',
>>>>>>>>             blank=True,
>>>>>>>>         )
>>>>>>>>
>>>>>>>>  I want to get a queryset for `Desk`, but it should also include a
>>>>>>>> prefetched attribute `favorite_or_nearby_chairs`, whose value should be
>>>>>>>> equal to:
>>>>>>>>
>>>>>>>>      Chair.objects.filter(
>>>>>>>>         (django.db.models.Q(nearby_desks=desk) |
>>>>>>>> django.db.models.Q(desk=desk)),
>>>>>>>>         some_other_lookup=whatever,
>>>>>>>>     )
>>>>>>>>
>>>>>>>>  Is this possible with `Prefetch`? I couldn't figure out how to
>>>>>>>> use the arguments.
>>>>>>>>
>>>>>>>>
>>>>>>>>  Thanks,
>>>>>>>> Ram.
>>>>>>>>  --
>>>>>>>> You received this message because you are subscribed to the Google
>>>>>>>> Groups "Django users" group.
>>>>>>>> To unsubscribe from this group and stop receiving emails from it,
>>>>>>>> send an email to [email protected].
>>>>>>>> To post to this group, send email to [email protected].
>>>>>>>> Visit this group at http://groups.google.com/group/django-users.
>>>>>>>> To view this discussion on the web visit
>>>>>>>> https://groups.google.com/d/msgid/django-users/46d9fdb7-c008-4496-acda-ac7cb30b4a89%40googlegroups.com
>>>>>>>> <https://groups.google.com/d/msgid/django-users/46d9fdb7-c008-4496-acda-ac7cb30b4a89%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>>>>> .
>>>>>>>> For more options, visit https://groups.google.com/d/optout.
>>>>>>>>
>>>>>>>
>>>>>>>  --
>>>>>>> You received this message because you are subscribed to a topic in
>>>>>>> the Google Groups "Django users" group.
>>>>>>> To unsubscribe from this topic, visit
>>>>>>> https://groups.google.com/d/topic/django-users/EuPduHjSNos/unsubscribe
>>>>>>> .
>>>>>>> To unsubscribe from this group and all its topics, send an email to
>>>>>>> [email protected].
>>>>>>> To post to this group, send email to [email protected].
>>>>>>> Visit this group at http://groups.google.com/group/django-users.
>>>>>>>  To view this discussion on the web visit
>>>>>>> https://groups.google.com/d/msgid/django-users/CA%2Be%2BciVk7_6VBDoBE-qjLBwrBxiNeVdP6-fwwnOXV%3DvSA3HnCw%40mail.gmail.com
>>>>>>> <https://groups.google.com/d/msgid/django-users/CA%2Be%2BciVk7_6VBDoBE-qjLBwrBxiNeVdP6-fwwnOXV%3DvSA3HnCw%40mail.gmail.com?utm_medium=email&utm_source=footer>.
>>>>>>>
>>>>>>>
>>>>>>> For more options, visit https://groups.google.com/d/optout.
>>>>>>>
>>>>>>
>>>>>>    --
>>>> You received this message because you are subscribed to the Google
>>>> Groups "Django users" group.
>>>>  To unsubscribe from this group and stop receiving emails from it,
>>>> send an email to [email protected].
>>>> To post to this group, send email to [email protected].
>>>> Visit this group at http://groups.google.com/group/django-users.
>>>>  To view this discussion on the web visit
>>>> https://groups.google.com/d/msgid/django-users/fc6b1237-7bd0-44a7-a91e-c12301fe0e05%40googlegroups.com
>>>> <https://groups.google.com/d/msgid/django-users/fc6b1237-7bd0-44a7-a91e-c12301fe0e05%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>> .
>>>> For more options, visit https://groups.google.com/d/optout.
>>>>
>>>>
>>>>  --
>>>> You received this message because you are subscribed to a topic in the
>>>> Google Groups "Django users" group.
>>>> To unsubscribe from this topic, visit
>>>> https://groups.google.com/d/topic/django-users/EuPduHjSNos/unsubscribe.
>>>>  To unsubscribe from this group and all its topics, send an email to
>>>> [email protected].
>>>> To post to this group, send email to [email protected].
>>>> Visit this group at http://groups.google.com/group/django-users.
>>>>  To view this discussion on the web visit
>>>> https://groups.google.com/d/msgid/django-users/54EF0808.9090009%40arkade.info
>>>> <https://groups.google.com/d/msgid/django-users/54EF0808.9090009%40arkade.info?utm_medium=email&utm_source=footer>.
>>>>
>>>>
>>>> For more options, visit https://groups.google.com/d/optout.
>>>>
>>>
>>>   --
>>> You received this message because you are subscribed to the Google
>>> Groups "Django users" group.
>>> To unsubscribe from this group and stop receiving emails from it, send
>>> an email to [email protected].
>>> To post to this group, send email to [email protected].
>>> Visit this group at http://groups.google.com/group/django-users.
>>> To view this discussion on the web visit
>>> https://groups.google.com/d/msgid/django-users/CANXboVa6%2BtSa5gmEww2-gm3SGHvGgn2VjOBTbYQA%3DWTt6CaufA%40mail.gmail.com
>>> <https://groups.google.com/d/msgid/django-users/CANXboVa6%2BtSa5gmEww2-gm3SGHvGgn2VjOBTbYQA%3DWTt6CaufA%40mail.gmail.com?utm_medium=email&utm_source=footer>
>>> .
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>>
>>>  --
>>> You received this message because you are subscribed to the Google
>>> Groups "Django users" group.
>>> To unsubscribe from this group and stop receiving emails from it, send
>>> an email to [email protected].
>>> To post to this group, send email to [email protected].
>>> Visit this group at http://groups.google.com/group/django-users.
>>> To view this discussion on the web visit
>>> https://groups.google.com/d/msgid/django-users/54EF0F3A.8000703%40arkade.info
>>> <https://groups.google.com/d/msgid/django-users/54EF0F3A.8000703%40arkade.info?utm_medium=email&utm_source=footer>
>>> .
>>>
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>
>>
>

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-users/CA%2Be%2BciU2Tnn6oX42%2Bn6Wpg8GRErcaoG9jp_2XCvYmUxDZaKRdw%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to