DRF SerializerMethodField, как передать параметры - PullRequest
1 голос
/ 29 апреля 2020

Есть ли способ передать параметры в SerializerMethodField * * * * Restric Framework?

Предположим, у меня есть модели:

class Owner(models.Model):
    name = models.CharField(max_length=10)

class Item(models.Model):
    name = models.CharField(max_length=10)
    owner = models.ForeignKey('Owner', related_name='items')
    itemType = models.CharField(max_length=5) # either "type1" or "type2"

Мне нужно вернуть владельца JSON объект с полями: name, type1items, type2items.

Мое текущее решение таково:

class ItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Item
        fields = ('name', 'itemType')

class OwnerSerializer(serializers.ModelSerializer):
    type1items = serializers.SerializerMethodField(method_name='getType1Items')
    type2items = serializers.SerializerMethodField(method_name='getType2Items')

    class Meta:
        model = models.Owner
        fields = ('name', 'type1items', 'type2items')

    def getType1Items(self, ownerObj):
        queryset = models.Item.objects.filter(owner__id=ownerObj.id).filter(itemType="type1")
        return ItemSerializer(queryset, many=True).data

    def getType2Items(self, ownerObj):
        queryset = models.Item.objects.filter(owner__id=ownerObj.id).filter(itemType="type2")
        return ItemSerializer(queryset, many=True).data

Это работает. Но было бы намного чище, если бы я мог передать параметр методу вместо использования двух методов с почти точным кодом. В идеале это должно выглядеть так:

...
class OwnerSerializer(serializers.ModelSerializer):
    type1items = serializers.SerializerMethodField(method_name='getItems', "type1")
    type2items = serializers.SerializerMethodField(method_name='getItems', "type2")

    class Meta:
        model = models.Owner
        fields = ('name', 'type1items', 'type2items')

    def getItems(self, ownerObj, itemType):
        queryset = models.Item.objects.filter(owner__id=ownerObj.id).filter(itemType=itemType)
        return ItemSerializer(queryset, many=True).data

В документах SerializerMethodField принимает только один параметр , который method_name.

. Есть ли способ добиться такого поведения? используя SerializerMethodField? (Пример кода здесь слишком упрощен, поэтому возможны ошибки.)

1 Ответ

0 голосов
/ 29 апреля 2020

Нет способа сделать это с базовым полем.

Вам необходимо написать настраиваемое поле сериализатора для его поддержки. Вот пример, который вы, вероятно, захотите изменить в зависимости от того, как вы его используете.

Эта версия использует kwargs из поля для передачи в качестве аргументов функции. Я бы рекомендовал делать это, а не использовать *args, поскольку вы получите более заметные ошибки и гибкость в том, как вы пишете определения своих функций / полей.

class MethodField(SerializerMethodField):
    def __init__(self, method_name=None, **kwargs):
        # use kwargs for our function instead, not the base class
        super().__init__(method_name) 
        self.func_kwargs = kwargs

    def to_representation(self, value):
        method = getattr(self.parent, self.method_name)
        return method(value, **self.func_kwargs)

Использование поля в сериализаторе:

class Simple(Serializer):
    field = MethodField("get_val", name="sam")
    def get_val(self, obj, name=""):
        return "my name is " + name

>>> print(Simple(instance=object()).data)
{'field': 'my name is sam'}
...