Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Haystack Results to Django Rest Framework Serializer

I am attempting to do a search using Django Haystack and then upon retrieving results I need to pass these results to my Django Rest Framework serializer.

The Django Rest Framework serializers.ModelSerializer requires that a queryset of objects gets sent for the serializer to be able to serialize these objects along with their database fields.

When I create my API view and use search to get results haystack returns a searchqueryset.

How could I get this searchqueryset into a django queryset without doing something like:

article_queryset = Article.objects.filter(id__in=[i.object for i in searchqueryset])

As you could imagine, sometimes search can return excess of 1000 search results which means that the above would be very inefficient.

Right now the Django rest framework allows me to paginate my returned objects. I am paginating by 30 objects on each page. How would it be possible for me to do the same with my Haystack searchqueryset?

Any advice or ideas on how to use Haystack along with Django Rest Framework would be great. Examples of how others have done a similar thing would be cool too :)

like image 965
Zac Avatar asked Nov 15 '13 14:11

Zac


People also ask

What does a serializer return Django?

Serializers in Django REST Framework are responsible for converting objects into data types understandable by javascript and front-end frameworks. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.

What does serializer data return?

The BaseSerializer class caches its data attribute on the object, but Serializer. data returns a copy of BaseSerializer.

What is the difference between ModelSerializer and HyperlinkedModelSerializer?

The HyperlinkedModelSerializer class is similar to the ModelSerializer class except that it uses hyperlinks to represent relationships, rather than primary keys. By default the serializer will include a url field instead of a primary key field.


1 Answers

You could use Haystack's SearchView (instead of a DRF view) which exposes the page object for pagination. Then you can just pass that to your serializer.

E.G. Awhile back I was working on something that required the json object of the rendered results on the current HTML page, so I wrote a filter tag to do this (we were using FacetedSearchView for the UI, and DRF for a more general purpose RESTful API). Called like so in the template:

var articlesJson = {{ page.object_list|article_jsonify }}

Actual filter tag:

import collections
from django.template import Library
from django.utils.safestring import mark_safe
from rest_framework.renderers import JSONRenderer

register = Library()


def article_jsonify(object):
    if isinstance(object, collections.Iterable):
        many = True
        ids = [obj.object.id for obj in object]
    else:
        many = False
        ids = [object.id]

    articles = Article.objects.filter(pk__in=ids)
    serializer = ArticleSerializer(articles, many=many)
    content = JSONRenderer().render(serializer.data)
    return mark_safe(content)

register.filter('article_jsonify', article_jsonify)

You could also write a view inherited from generics.ListAPIView and override the get_queryset method, where you would pass the request's query parameter to a SearchQuerySet, then output it using the Serializer. More info here:

http://www.django-rest-framework.org/api-guide/filtering

Of course you might not be able to use the ModelSerializer this way, unless you do something like you mentioned. However DRF has an example on using the paginator on a queryset like so:

http://www.django-rest-framework.org/api-guide/pagination#paginating-querysets

UPDATE I ended up eventually using a DRF view that uses a Haystack SearchQuerySet as the queryset, and then passing it to a Paginator. Here is a simplified example (I'm sure it can be streamlined some), but I hope it helps someone gets started.

class ArticleList(ListCreateAPIView):

    """
    List, Create files
    """
    model = Article

    def get(self, request, *args, **kwargs):
        # simplified filtering of an SQS
        q = request.get('q', '')
        sqs = SearchQuerySet().filter(content=Clean(q))
        paginator = Paginator(sqs, 20)

        page = request.QUERY_PARAMS.get('page')
        try:
            articles = paginator.page(page)
        except PageNotAnInteger:
            # If page is not an integer, deliver first page
            articles = paginator.page(1)
        except PageNotAnInteger:
            # If page is out of range, deliver last page
            articles = paginator.page(paginator.num_pages)

        serializer_context = {'request': request}
        serializer = PaginatedArticleSerializer(articles, context=serializer_context)

        return Response(serializer.data)

class ArticleSerializer(serializers.ModelSerializer):

    """
    Base Article Serializer
    """

    class Meta:
        model = Article

class PaginatedArticleSerializer(pagination.PaginationSerializer):

    """
    Serializes page objects of article querysets.
    """
    start_index = serializers.SerializerMethodField('get_start_index')
    end_index = serializers.SerializerMethodField('get_end_index')
    num_pages = serializers.Field(source='paginator.num_pages')

    class Meta:
        object_serializer_class = ArticleSerializer

    def get_start_index(self, page):
        return page.start_index()

    def get_end_index(self, page):
        return page.end_index()

    def get_curr_page(self, page):
        return page.number
like image 66
mynameistechno Avatar answered Dec 07 '22 18:12

mynameistechno