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.
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...
Still watching the thread cause I have couple of problems
like this one :)
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
<jrschneide...@gmail.com <mailto:jrschneide...@gmail.com>> 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
<jrschneide...@gmail.com <mailto:jrschneide...@gmail.com>> 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 <cont...@arkade.info
<mailto:cont...@arkade.info>> 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
<cont...@arkade.info <mailto:cont...@arkade.info>> 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"
<r...@rachum.com> 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 <jrschn...@gmail.com> 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 <ram.r...@gmail.com> 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
django-users...@googlegroups.com.
To post to this group, send email to
django...@googlegroups.com.
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
django-users...@googlegroups.com.
To post to this group, send email to
django...@googlegroups.com.
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
django-users+unsubscr...@googlegroups.com
<mailto:django-users+unsubscr...@googlegroups.com>.
To post to this group, send email to
django-users@googlegroups.com
<mailto:django-users@googlegroups.com>.
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
django-users+unsubscr...@googlegroups.com
<mailto:django-users+unsubscr...@googlegroups.com>.
To post to this group, send email to
django-users@googlegroups.com
<mailto:django-users@googlegroups.com>.
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
django-users+unsubscr...@googlegroups.com
<mailto:django-users+unsubscr...@googlegroups.com>.
To post to this group, send email to
django-users@googlegroups.com
<mailto:django-users@googlegroups.com>.
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
django-users+unsubscr...@googlegroups.com
<mailto:django-users+unsubscr...@googlegroups.com>.
To post to this group, send email to
django-users@googlegroups.com
<mailto:django-users@googlegroups.com>.
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 django-users+unsubscr...@googlegroups.com
<mailto:django-users+unsubscr...@googlegroups.com>.
To post to this group, send email to django-users@googlegroups.com
<mailto:django-users@googlegroups.com>.
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
<https://groups.google.com/d/msgid/django-users/CA%2Be%2BciU2Tnn6oX42%2Bn6Wpg8GRErcaoG9jp_2XCvYmUxDZaKRdw%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 django-users+unsubscr...@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
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.