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 :)
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.
The BaseSerializer class caches its data attribute on the object, but Serializer. data returns a copy of BaseSerializer.
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.
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
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