Goal
Querying for all products, slicing them, returning subset of those products with an added key:value
, in other words, enriched.
Code that works but I can't use
I can't use this code because I use a paginator, the paginator accesses the count
of the QuerySet
. If I pass the sliced QuerySet then that count is just for that sliced part, not the overall QuerySet
, hence why I can't use it.
products_qs = final_qs[paginator.get_offset(request):
paginator.get_offset(request) + paginator.get_limit(request)]
for product in products_qs:
product.raw['super_cool_new_key'] = ms_response.get('results').get(product.id)
This works great, when I print the data I can see that super_cool_new_key
enrichment in every product. Awesome. Problem? Well, I have had to slice it and now the count method is no longer true. Of course, I can do something like:
products_qs.count = final_qs.count
and move on with my life, but it feels... hacky, or maybe not?
Code I would like for it to work, but doesn't
for i in range(paginator.get_offset(request),
paginator.get_offset(request) + paginator.get_limit(request)):
product = final_qs[i]
product.raw['super_cool_new_key'] = ms_response.get('results').get(product.id)
When I see the output of the data, the super_cool_new_key
is not there. I can't wrap my head around as to why?
Maybe I am having a thick day and I don't understand accessing by reference, so I remove the middlemonkey.
final_qs = final_qs.all()
for i in range(paginator.get_offset(request),
paginator.get_offset(request) + paginator.get_limit(request)):
final_qs[i].raw['super_cool_new_key'] = ms_response.get('results').get(final_qs[i].id, '')
Suspicions
It's obvious it's something about the code difference that is the culprit of why one way works and the other way doesn't. My dollar is on the following:
- The slice
- The iteration
Looking into Django Docs for QuerySet :
Iteration. A QuerySet is iterable, and it executes its database query the first time you iterate over it.
Then about slicing:
Slicing. As explained in Limiting QuerySets, a QuerySet can be sliced, using Python’s array-slicing syntax. Slicing an unevaluated QuerySet usually returns another unevaluated QuerySet, but Django will execute the database query if you use the “step” parameter of slice syntax, and will return a list
I can't be the slicing then, because I don't do a slice with a "step" parameter. Since it returns an unevaluated QuerySet the code I want to work, should in theory work. (Isn't that always the case?ha ha)
Ok so that clears up the fact that when, in the first option of coding, I did an iteration of for x in x_container
the QuerySet
was executed. Could that be the answer? So I modified the code:
Spoiler Alert: still does not work
final_qs = final_qs.all()
for i in range(paginator.get_offset(request),
paginator.get_offset(request) + paginator.get_limit(request)):
product = final_qs[i]
product.raw['super_cool_new_key'] = ms_response.get('results').get(product.id)
Emmm... help?
A suggested answer that, spoiler alert, did not work
from django.db.models import When, Case, Value, CharField
when = [ When(id=k, then=Value(v)) for k,v in ms_response.get('results').items()]
p = final_qs[paginator.get_offset(request)
:paginator.get_offset(request) + paginator.get_limit(request)]
p = p.annotate(super_cool_new_key=Case(
*when,
default=Value(''),
output_field=CharField()
)
)
I also tried it without slicing but with .all().annotate()
. Still didn't work. It doesn't work, not due to an Exception happening, but because when I see the output, that super_cool_new_key
is not there, meaning it didn't enrich the objects, which is the whole point.