I have a very generic view/template to display the contents of a queryset of a given model.
I use from 12 places with 12 different querysets, now I wanted to integrate the haystack search in there but I can't because SearchQuerySet does not match a QuerySet in the template.
With the normal querysets I do
{%for obj in qs%}
{{obj.foreign_key.name }}
{%endfor%}
With the SearchQuerySet I would need to do
{%for obj in qs%}
{{obj.object.foreign_key.name}}
{%endfor%}
Which basically breaks my generic template and view that do now know where the queryset comes from.
I would like a way to have a searchqueryset behave like the normal queryset and I am aware that:
Any hints on how can I keep my template generic but accept SearchQuerySet or convert SearchQuerySet to QuerySet ?
There is a neat generator trick that I used that avoids adding object
method to your models, and lets you use the same template for both Django's QuerySet
and Haystack SearchQuerySet
.
The trick is to wrap your SearchQuerySet into a generator.
# In your view...
def queryset_gen(search_qs):
for item in search_qs:
yield item.object # This is the line that gets the model instance out of the Search object
qs = queryset_gen(sqs)
The advantage of this approach is that it maintains the order in which SearchQuerySet is returned and saves computation and memory as you will not need create or store another instance of the list.
What about adding a method on your models called object
, so that your querysets behave like the SearchQuerySet? Eg
class MyModel(models.Model):
...
@property
def object(self):
return self
For others who came here looking for a more general approach to treating SQS like QS.
I needed something like this for Django Rest Framework so I created the following wrapper for sqs -> qs. It allows the SQS to be processed as if it were a queryset with limited support for iteration and slicing.
You could also invoke load_all before iterating in the generator function.
class SearchQuerySetWrapper(object):
"""
Decorates a SearchQuerySet object using a generator for efficient iteration
"""
def __init__(self, qs):
self.qs = qs
def count(self):
return self.qs.count()
def __iter__(self):
for result in self.qs:
yield result.object
def __getitem__( self, key):
if isinstance(key, int) and (key >= 0 or key < self.count()):
# return the object at the specified position
return self.qs[key].object
# Pass the slice/range on to the delegate
return SearchQuerySetWrapper(self.qs[key])
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With