Django RestFramework group by

2020-02-28 04:49发布

问题:

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.

回答1:

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


回答2:

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'
    },
}


回答3:

You might use "serializer relations", and especially "nested relationships". See documentation here