Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django RestFramework group by

My issue is related to Django RestFramework and is about how to group elements.

This is my serializers.py

from collaborativeAPP.models import *
from rest_framework import serializers

class VocabSerializer(serializers.ModelSerializer):
    term_word = serializers.CharField(source='term.word',read_only=True)
    kwdGroup = serializers.StringRelatedField()
    class Meta:
        model = Vocab
        fields = ('id','term_word', 'meaning','kwdGroup')

class TermSerializer(serializers.ModelSerializer):
    word = serializers.CharField(read_only=True)
    class Meta:
        model = Term
        fields = ('url', 'word')

The following json it's the actual result:

{"results":[
            {
                "id": 5,
                "term_word": "word1",
                "meaning": "Text1"
                "kwdGroup": "A"
            },
            {
                "id": 6,
                "term_word": "word2",
                "meaning": "Text2"
                "kwdGroup": "A"
            },
            {
                "id": 7,
                "term_word": "word3",
                "meaning": "Text3"
                "kwdGroup": "A"
            }
        ]}

As you can notice "kwdGroup" is a repetitive element that i which to group.

I would like to group by kwdGroup

{"A":[
       {
        "id": 5,
        "term_word": "word1",
        "meaning": "Text1"
        },
        {
        "id": 6,
        "term_word": "word2",
        "meaning": "Text2"
        },
        {
        "id": 7,
        "term_word": "word3",
        "meaning": "Text3"
        }
    ]
}

I'm looking for answers on http://www.django-rest-framework.org/ on api guide but i'm having difficulties to find an approach to lead with it. Do you share this same issue? Do you have any suggestion how can i do this? Do you have any example that deals with element grouping using Django RestFramework?

Thanks in advance.

like image 993
ePascoal Avatar asked Jun 26 '15 11:06

ePascoal


2 Answers

One way to achieve this is to use a SerializerMethodField. The below might be slightly different than your use case, but you can adopt accordingly. There are other ways of achieving this as well, including overwriting to_representation methods, but they rely on messing with the inner workings of DRF more than is relevant here.

models.py

class Dictionary(Model):
    id = PrimaryKey


class Word(Model):
    dictionary = ForeignKey(Dictionary, related_name='words')
    word = Charfield()
    group = Charfield()

serializers.py

class WordSerializer(serializers.ModelSerializer):
    word = serializers.CharField(read_only=True)

    class Meta:
            model = Word
            fields = ('word',)


class DictionarySerializer(serializers.ModelSerializer):
    group_a = serializers.SerializerMethodField()
    group_b = serializers.SerializerMethodField()

    def get_group_a(self, instance):
        return WordSerializer(instance.words.filter(group='a'), many=True).data

    def get_group_b(self, instance):
        return WordSerializer(instance.words.filter(group='b'), many=True).data

    class Meta:
        model = Dictionary
        fields = ('group_a', 'group_b')

An example

>>> my_dictionary = Dictionary.objects.create()
>>> Word.objects.bulk_create(
        Word(word='arrow', group='a' dictionary=my_dictionary),
        Word(word='apple', group='a' dictionary=my_dictionary),
        Word(word='baby', group='b' dictionary=my_dictionary),
        Word(word='banana', group='b' dictionary=my_dictionary)
)
>>> serializer = DictionarySerializer(my_dictionary)
>>> print serializer.data
{
    'group_a': {
        'word': 'arrow',
        'word': 'apple'
    },
    'group_b': {
        'word': 'baby',
        'word': 'banana'
    },
}
like image 50
Rex Salisbury Avatar answered Nov 19 '22 02:11

Rex Salisbury


Let's assume that the kwdGroup field is the relation field to a model called KeyWordGroup.

The default ListSerializer uses the to_representation method to render a list of the serialized objects rest_framework :

class ListSerializer(BaseSerializer):
     ...

    def to_representation(self, data):
        """
          List of object instances -> List of dicts of primitive datatypes.
        """
        # Dealing with nested relationships, data can be a Manager,
        # so, first get a queryset from the Manager if needed
        iterable = data.all() if isinstance(data, models.Manager) else data

        return [
            self.child.to_representation(item) for item in iterable
        ]

We can modify the ListSerializer to group the results for example:

class VocabListSerializer(serializers.ListSerializer):

    def to_representation(self, data):
        iterable = data.all() if isinstance(data, models.Manager) else data
        return {
            kwdGroup: super().to_representation(Vocab.objects.filter(kwdGroup=kwdGroup))
            for kwdGroup in KeyWordGroup.objects.all()
        }

We can then use the modified VocabListSerializer with the VocabSerializer.

class VocabSerializer(serializers.Serializer):
...
    class Meta:
        list_serializer_class = VocabListSerializer
like image 8
Crystal Avatar answered Nov 19 '22 03:11

Crystal