Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django haystack SearchQuerySet to QuerySet

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:

  • I will lose the scores, although I will mantain the order
  • I would need to do a load_all() to get the whole object in

Any hints on how can I keep my template generic but accept SearchQuerySet or convert SearchQuerySet to QuerySet ?

like image 615
Sala Avatar asked Nov 30 '12 09:11

Sala


3 Answers

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.

like image 116
Dimitri Gnidash Avatar answered Oct 23 '22 19:10

Dimitri Gnidash


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
like image 44
Greg Avatar answered Oct 23 '22 20:10

Greg


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])
like image 1
Kakashi Sensei Avatar answered Oct 23 '22 21:10

Kakashi Sensei