serializing a dict

3,895 views
Skip to first unread message

Bayard Randel

unread,
Mar 25, 2013, 12:35:28 AM3/25/13
to django-res...@googlegroups.com
Hi there,

I'm trying to return an alternative json representation of a collection instead of a flat array when multiple IDs are provided in the querystring to make things more pleasant for parsing on the client. Unfortunately returning a dict instead of a queryset seems to break things, and presumably I need a custom serializer of some kind for this.

Django Model
class Item(models.Model):

    CATEGORY_CHOICES = (
            ('CATAGORY1', 'Category 1'),
            ('CATAGORY2', 'Category 2'),
            )

    text = models.TextField(max_length=300)
    category = models.CharField(max_length=23, choices=CATEGORY_CHOICES)

DRF View
class ItemList(generics.ListCreateAPIView):
    """
    API endpoint represents list of items.
    Supports GET and POST
    """
    model = Item
    serializer_class = ItemSerializer

    def get_queryset(self):
        """
        Return queryset filtered by id(s).
        """

        queryset = Item.objects.order_by('category')
        # queryset: [<'item', 'category1'>, <'item2', 'category1'>, <'item3', 'category2'>]

        # if multiple ids are specified in querystring, return a dict instead
        item_ids = self.request.QUERY_PARAMS.get('ids', None)
        if item_ids is not None:
            self.paginate_by = None
            queryset = queryset.filter(id__in=(map(int, item_ids.split(','))))
            # unideal queryset representation looks like:
            # [{'item1', 'category1'}, {'item2', 'category1'}, {'item3', 'category2'}] 

            # improve representation of objects for frontend with a defaultdict
            # e.g. { category1: [item, item2], category2: [item3] }
            item_dict = defaultdict(list)
            for item in queryset:
                item_dict[item.category].append(item)
            return item_dict

        return queryset

This results in the error:

'unicode' object has no attribute 'pk'
Request Method:GET
Request URL:http://127.0.0.1:8000/items/?ids=1,2
Django Version:1.5
Exception Type:AttributeError
Exception Value:
'unicode' object has no attribute 'pk'
Exception Location:/Users/me/env/cmap-api/lib/python2.7/site-packages/rest_framework/relations.py in field_to_native, line 436
Python Executable:/Users/me/env/cmap-api/bin/python
Python Version:2.7.2

Hopefully this isn't too convoluted. Thanks for your help - a bit stumped!

Tom Christie

unread,
Mar 25, 2013, 8:53:51 AM3/25/13
to django-res...@googlegroups.com
In this sort of case I'd always recommend trying to break things down a bit more than simply relying on the generic view to do what you need.
Drop into the shell and try serializing your data representation to get a better idea of whats going on, or at least override the `list` method completely instead of overriding the `get_queryset` hook, so that you have a clearer and more explicit view of exactly what's going on.

> 'unicode' object has no attribute 'pk'

You're getting this error because you're trying to serialize a string, and REST framework is throwing an error when it can't access the 'pk' attribute.

That'll be happening because you're returning a dict (in some cases), which is passed to a serializer which is expecting a list (many=True is being set).
The serializer code then iterates over the items being passed to it, which is the set of all the keys in the dictionary, and then proceeds to blow up when it can't serialize the first item.

Serializers handle objects or iterables of objects, so if you want a dictionary with lists of values you'll need to deal with that manually, eg something like this...

    def list(self, request, *args, **kwargs):
        ...
        item_dict = defaultdict(list)
        for item in queryset:
            serializer = ItemSerializer(item)
            item_dict[item.category].append(serializer.data)
        return Response(item_dict)

Bayard Randel

unread,
Mar 25, 2013, 7:02:05 PM3/25/13
to django-res...@googlegroups.com

Thanks Tom, that is working superbly now for queries in the form of /items/id and /items/ids=?1,2,3 however the 'list' view at /items returns "'QuerySet' object does not support item assignment"

I've overridden list() as you suggested:

 def list(self, request, *args, **kwargs):
        queryset = Item.objects.order_by('category')

        item_ids = self.request.QUERY_PARAMS.get('ids', None)
        if item_ids is not None:
            self.paginate_by = None
            queryset = queryset.filter(id__in=(map(int, item_ids.split(','))))
            item_dict = defaultdict(list)
            for item in queryset:
                serializer = ItemSerializer(item)
                item_dict[item.get_category_display()].append(serializer.data)
            return Response(item_dict)
        return queryset

Tom Christie

unread,
Mar 26, 2013, 1:47:36 PM3/26/13
to django-res...@googlegroups.com
Yup, that method needs to return a response, so the last bit of your method should serialize the queryset rather than simply returning it.

Bayard Randel

unread,
Mar 26, 2013, 4:15:48 PM3/26/13
to django-res...@googlegroups.com
Ah of course, that makes perfect sense. Working now, thanks again Tom!
Reply all
Reply to author
Forward
0 new messages