Django haystack SearchQuerySet to QuerySet
Asked Answered
C

4

6

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 ?

Cambrai answered 30/11, 2012 at 9:47 Comment(2)
how about returning a list from the view like: [obj.object for obj in qs], then the forloop in the template should still workRepudiation
That would instantiate the whole queryset, plus I would lose pagination and all, I need to return a queryset-like objectCambrai
F
7

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.

Frangible answered 18/1, 2014 at 15:58 Comment(0)
A
4

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
Aerostatics answered 5/12, 2012 at 21:35 Comment(1)
Wouldn't you need to add an @property?Patentee
T
1

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])
Telesis answered 21/11, 2014 at 20:8 Comment(1)
the generator only mode is usefull, but this is better, it supports for example paginators, and others...Growing
M
0

You can make an array like this:

q = request.GET['q']
    results = SearchQuerySet().auto_query(q)
    obj = []
    for r in results:
        obj.append(r.object)
    return render_to_response('template.html',
        {'obj': obj )
Minter answered 3/12, 2012 at 4:43 Comment(1)
If you did this (and I'd recommend not doing this), you should at least wrap it in an generator class, which exposes the count(), and returns the object during iteration. Otherwise, if your query contains thousands of records, you risk a single request killing your server.Fenny

© 2022 - 2024 — McMap. All rights reserved.