Поле Django не проходит через сериализатор

Используя Django REST Framework 2.2, я имею Person модель следующим образом в models.py::

class Person(models.Model):
    id = models.CharField(max_length = 20, primary_key = True, blank = True)
    name = Models.CharField(max_length = 1024, blank = True)
    values = {}

Все данные хранятся в базе данных Firestore для сохранения и получения данных через REST API. Перед внесением новых записей в базу данных используется сериализатор для проверки входящих POST-данных.

Маршрут /person принимает данные POST-запроса и запускает их по команде PersonCreateSerializer в views.py:

def create_person(request):
    """
        Route: /person
        Method: POST
    """
    try:
        print(request.data)
        # Above print outputs:
        # <QueryDict: {'name': ['John Doe'], 'values': ['{ "height": 180 }']}>

        serializer = PersonCreateSerializer(data = request.data)
        serializer.is_valid(raise_exception = True)
        person = Person.create_person(request.data)
        ...
    except APIException as exception:
            return JsonResponse(exception.APIError, status = exception.status)

serializers.py:

class PersonCreateSerializer(CreateModelSerializer):
    class Meta:
        model = Person
        fields = "__all__"
    
    def validate(self, data):
        print(data)
        # Above print outputs:
        # OrderedDict([('name', 'John Doe')])
        # Notice missing 'values' field.

        if not data.get("values"): # Ensure we have a values field within the data.
            raise APIException("ERROR_MISSING_FIELD", "Missing required field 'values'.", 400)

        return data

Проблема заключается в том, что любое значение, предоставленное для словаря values, отбрасывается, когда функция сериализатора validate() получает его.

POST Payload:

Post Payload Image

Мой вопрос заключается в том, почему словарь, полученный из POST-запроса, не попадает в сериализатор, чтобы его можно было разобрать? Это то, как вы используете словарные поля в Django?

Отправлено в сериализатор:

<QueryDict: {'name': ['John Doe'], 'values': ['{ "height": 180 }']}>

Получено сериализатором:

OrderedDict([('name', 'John Doe')])

Я рассмотрел альтернативные варианты, такие как HStoreField и JSONField, однако эти данные хранятся в базе данных Firestore, и необходимо сохранить ассоциацию ключ-значение, а не хранить их в виде простой строки JSON.

Я полагаю, что это потому, что values не является полем. Это просто переменная класса.

Сериализатор фильтрует данные по полям, на которые вы указали. Вы указали __all__, что означает все поля в модели.

Вы можете попробовать:

fields = ['name', 'values']

А если это не сработало, сделайте функцию и передайте ее "как поле":

# models.py

class Person(models.Model):
    id = models.CharField(max_length = 20, primary_key = True, blank = True)
    name = Models.CharField(max_length = 1024, blank = True)
    values = {}

    def get_values(self):
        return self.values

# serializers.py

class PersonCreateSerializer(CreateModelSerializer):
    class Meta:
        model = Person
        fields = ['name', 'get_values']

Решением, которое я нашел, было использование модуля django-dictionaryfield, который предоставляет тип поля Dictionary, который можно использовать для преобразования в и из всех типов массивов, таких как словари и списки.

Без поля, объявленного в model.py, сериализатор игнорирует его, поскольку оно не считается частью самой модели, поэтому использование пользовательской модели DictionaryField позволяет хранить его как поле модели Django.

Django DictionaryField Setup

  1. Установите модуль в ваш проект:

    $ pip install django-dictionaryfield
    
  2. Добавьте dictionaryfield в ваш INSTALLED_APPS конфигурационный файл Django:

    INSTALLED_APPS = (
        ...
        "dictionaryfield",
    )
    

Модельный класс

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

from django.db import models
from dictionaryfield import DictionaryField

class Person(models.Model):
    id = models.CharField(max_length = 20, primary_key = True, blank = True)
    name = Models.CharField(max_length = 1024, blank = True)
    values = DictionaryField(default = {})

    @staticmethod
    def create_person(personData):
        person = Person(
            name = personData.get("name", "Unknown"),
            values = personData.get("values", {}),
        )

        return person
Вернуться на верх