Пользовательское поле сериализатора Django Rest Framework ModelSerializer to_internal_value не сохраняется на объекте - PullRequest
1 голос
/ 17 мая 2019

У меня есть модель и сериализатор, в этой модели есть ArrayField (postgres).

Теперь я хотел создать поле сериализатора, которое получит список [1,2] и сохранить его в объекте, но для списка и деталей в сериализаторе для отображения списка объектов JSON.

Модель:

class User(models.Model):
    email = models.EmailField('Email', unique=True, blank=False)
    full_name = models.CharField(
        'Full name', max_length=150, blank=True, null=True)
    roles = ArrayField(
        models.PositiveSmallIntegerField(),
        default=list,
        blank=True
    )

Serializer:

class ArraySerializerField(ListField):

    def __init__(self, queryset, serializer_class):
        super(ArraySerializerField, self).__init__()
        self.queryset = queryset
        self.serializer_class = serializer_class

    def to_representation(self, value):
        if value:
            qs = self.queryset.filter(pk__in=value)
            return self.serializer_class(qs, many=True).data

        return []

    def to_internal_value(self, value):
        super(ArraySerializerField, self).to_internal_value(value)
        print(value)  # [1, 2]
        return value


class UserSerializer(SerializerExtensionsMixin, serializers.ModelSerializer):
    roles = ArraySerializerField(queryset=Role.objects.all(), serializer_class=RoleSerializer)

    class Meta:
        model = User
        fields = ('id', 'email', 'full_name', 'roles')

    def create(self, validated_data):
        print(validated_data) 
        # {'email': 'test@test.com', 'full_name': 'Test', 'roles': []}
        user = super(UserSerializer, self).create(validated_data)

        return user

Теперь, когда я делаю список или подробный запрос, все в порядке, я получаю список ролей как JSON.

Но когда я пытаюсь отправить данные POST и отправить с этими данными:

{
  "email": "test@test.com",
  "full_name": "Test",
  "roles": [1, 2]
}

validated_data в методе create показывает роли всегда как [], а объект сохраняется без ролей, но печать из to_internal_value показывает [1, 2].

Что я делаю не так? Это должно сохранить отправленные данные, потому что to_internal_value работает нормально.

EDIT:

Ответ GET and LIST дает мне правильный формат:

{
  "id": 1,
  "email": "test@test.com",
  "full_name": "Test",
  "roles": [
    {
      "id": 1,
      "name": "Role 1"
    },
    {
      "id": 2,
      "name": "Role 2"
    }
  ]
}

Ответы [ 2 ]

2 голосов
/ 23 мая 2019

Вы пробовали это?

class UserSerializer(SerializerExtensionsMixin, serializers.ModelSerializer):
    <b>roles = serializers.ListField(child=serializers.IntegerField(), allow_empty=True, required=False)</b>

    class Meta:
        model = User
        fields = ('id', 'email', 'full_name', 'roles')

    def create(self, validated_data):
        # check validated_data here
        ...

Примечание

Я не уверен насчет природы класса SerializerExtensionsMixin здесь.Кроме того, я не уверен, что намерения, стоящие за аргументами queryset и serializer_class вашего пользовательского ListField


Вывод оболочки Django

In [7]: from rest_framework import serializers                                                                                                                                                                     

In [8]: class UserSerializer(serializers.Serializer): <b> # Created a simple serializer without model</b>
   ...:     roles = serializers.ListField(child=serializers.IntegerField(), allow_empty=True, required=False) 
   ...:     email = serializers.EmailField() 
   ...:     full_name = serializers.CharField() 
   ...:                                                                                                                                                                                                            

In [9]: data = { <b># your data</b>
   ...:   "email": "test@test.com", 
   ...:   "full_name": "Test", 
   ...:   "roles": [1, 2] 
   ...: }                                                                                                                                                                                                          

In [10]: u = UserSerializer(data=data)                                                                                                                                                                             

In [11]: u.is_valid()                                                                                                                                                                                              
Out[11]: True

In [12]: u.data  <b># got valid data</b>                                                                                                                                                                                                  
Out[12]: {'roles': [1, 2], 'email': 'test@test.com', 'full_name': 'Test'}

In [13]: data["roles"] = [] <b># change data to accept empty list</b>                                                                                                                                                                                       

In [14]: u = UserSerializer(data=data)                                                                                                                                                                             

In [15]: u.is_valid()                                                                                                                                                                                              
Out[15]: True

In [16]: u.data  <b># got validated data with empty list</b>                                                                                                                                                                                                  
Out[16]: {'roles': [], 'email': 'test@test.com', 'full_name': 'Test'} 

In [17]: data["roles"] = ["foo","bar"]   <b>#added string to the list</b>                                                                                                                                                                          

In [18]: u = UserSerializer(data=data)                                                                                                                                                                             

In [19]: u.is_valid()  <b># validation failed   </b>                                                                                                                                                                                         
Out[19]: False

In [20]: u.errors                                                                                                                                                                                                  
Out[20]: {'roles': {0: [ErrorDetail(string='A valid integer is required.', code='invalid')], 1: [ErrorDetail(string='A valid integer is required.', code='invalid')]}}

UPDATE-1

Создайте RoleSerializer и используйте его в UserSerializer

<b>class RoleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Role
        fields = ('id', 'name')</b>


class UserSerializer(SerializerExtensionsMixin, serializers.ModelSerializer):
    roles = serializers.ListField(child=serializers.IntegerField(), allow_empty=True, required=False)

    class Meta:
        model = User
        fields = ('id', 'email', 'full_name', 'roles')

    def create(self, validated_data):
        # check validated_data here
        ...

    <b>def to_representation(self, instance):
        rep = super().to_representation(instance)
        rep['roles'] = RoleSerializer(Role.objects.filter(id__in=rep['roles']), many=True).data
        return rep</b>

ОБНОВЛЕНИЕ-2

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

<b>class ArrayField(serializers.ListField):
    def __init__(self, *args, **kwargs):
        self.queryset = kwargs.pop('queryset', None)
        self.serializer_class = kwargs.pop('serializer_class', None)
        super().__init__(*args, **kwargs)

    def to_representation(self, data):
        qs = self.queryset.filter(id__in=data)
        serializer = self.serializer_class(qs,many=True)
        return serializer.data</b>

class UserSerializer(SerializerExtensionsMixin, serializers.ModelSerializer):
    <b>roles = ArrayField(queryset=Role.objects.all(), serializer_class=RoleSerializer)</b>

    class Meta:
        model = User
        fields = ('id', 'email', 'full_name', 'roles')

    def create(self, validated_data):
        # check validated_data here
        ...
0 голосов
/ 17 мая 2019

Попробуйте переключиться на PrimaryKeyRelatedField. Хотя вам нужно изменить свою модель пользователя, чтобы использовать реальное отношение. Как правило, это хорошая идея, поскольку она поможет обеспечить целостность данных в вашем проекте.

class User(models.Model):
    email = models.EmailField('Email', unique=True, blank=False)
    full_name = models.CharField(
        'Full name', max_length=150, blank=True, null=True)
    roles = models.ManyToManyField(Role, blank=True)

class UserSerializer(SerializerExtensionsMixin, serializers.ModelSerializer):
    roles = serializers.PrimaryKeyRelatedField(
        many=True,
        queryset=Role.objects.all(),
    )

    class Meta:
        model = User
        fields = ('id', 'email', 'full_name', 'roles')

    def create(self, validated_data):
        print(validated_data) 
        # {'email': 'test@test.com', 'full_name': 'Test', 'roles': []}
        user = super(UserSerializer, self).create(validated_data)

        return user
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...