Ask and you shall receive (eventually). Another post in this list has an example using Prefetch(), perhaps that will help you:
https://groups.google.com/d/msgid/django-users/3daddb38-3260-4f7d-9559-7d0d3f17b59e%40googlegroups.com?utm_medium=email&utm_source=footer On Feb 27, 2015 2:38 AM, "James Schneider" <[email protected]> wrote: > > On Feb 27, 2015 12:51 AM, "aRkadeFR" <[email protected]> wrote: > > > > Yeah, but from my experience, your example through a new > > query. You have to (and please correct me if I'm wrong or > > there are other ways) use the self.chair_set.all() in order > > to not through a new query when you have prefetched the > > chairs. > > AFAIK, self.chair_set.all() would always spawn a second query unless > something like select_related() had been used previously to cache that > query result > > I provided several examples, some of which spawn a second query (and may > be appropriate, I can't provide an affirmative answer for the OP without > knowing how many queries are being run per page load and average query > time, etc.). You'll need to be more specific. > > I think I accidentally referred to some of the model fields as if they > were FK's, but they probably should have had .all() after all of the > references since everything was an M2M relationship. > > > > > To be simple and answer the problem: my only solution I > > have in mind is to prefetched all the objects (chairs), and > > then filter it in python with properties like I said. But as you > > said it will load too much objects... > > I think there was some miscommunication here. The OP stated that loading > all of the chairs was infeasible, and I would tend to agree. > > My only clarification would be that loading all of the chairs via > something like Chair.objects.all() would be a bad idea, since you have no > idea how many chairs you may have in the entire database. > > Loading >10k Chair objects into memory and having Django coerce those into > model objects, and then performing post processing in some custom app code > to filter that list back down to something reasonable will give you a bad > time, every time. Your users will be unhappy with pages that take seconds > to load, and your server processes will be unhappy assuming you have plenty > of RAM/CPU to handle such a request. Now multiply that load by X number of > users...and you're quickly hitting CPU and process limits for RAM > allocation. > > However, "loading all of the chairs" via a M2M or FK relationship such as > desk_obj.nearby_chairs.all() (assuming the OP reconfigured the model fields > as I suggested before, or even using the existing Chair M2M to Desk) would > likely be perfectly valid and would probably be a small subset of the total > chairs in the system (or maybe None or all of them). > > > > > Still watching the thread cause I have couple of problems > > like this one :) > > > > I don't necessarily run in to this specific problem, so I'm not sure how > much more I can contribute. A lot of the model changes I suggested were > educated guesses and may not be valid at all given other requirements or > design considerations in the project. > > > aRkadeFR > > > > > > On 02/26/2015 08:27 PM, James Schneider wrote: > >> > >> 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 > . > >>>>>>>>>>>> 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 > . > >>>>>>>>>>> > >>>>>>>>>>> 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 > . > >>>>>>>> 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 > . > >>>>>>> > >>>>>>> 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 > . > >>>>>> 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 > . > >>>>> > >>>>> 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. > > > > > > -- > > 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/54F0300A.9000204%40arkade.info > . > > > > 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%2BciVGNVZ0hkACb7MWjeVWUAXc6z6zrmhBTNCc8s-gJSRFVA%40mail.gmail.com. For more options, visit https://groups.google.com/d/optout.

