Skip to content
Advertisement

Django field not passing through serializer

Using the Django REST Framework 2.2, I have a Person model as follows in 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 = {}

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

        return person

All data is stored in a Firestore database for saving and retrieving data via the REST API. Before new entries are made into the database, a serializer is used to validate incoming POST data.

The route /person takes POST request data and runs it by the PersonCreateSerializer in 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

The problem is however any value provided for the values dictionary is discarded when the serializer validate() function receives it.

POST Payload:

Post Payload Image

My question is why is the dictionary received from the POST request not received by the serializer so it can be parsed? What is the correcy way to create dictionary fields in Django?

Sent to Serializer:

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

Received by Serializer:

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

The problem with JSONField and HStoreField

I have looked at alternatives mentioned such as HStoreField and JSONField however this data is being stored in a Firestore database and the key-value association needs to be retained rather than it being stored as a plain JSON string.

Because the data is being stored in Firestore, the structure of the dictionary array needs to be retained as a map in the database, this allows it to be indexed and queried with Firestore queries.

img

If we use JSONField, this simply converts the value to a string and removes this functionality.

img

Advertisement

Answer

The solution I found was to make use of the django-dictionaryfield module, this provides a Dictionary field type that can be used for converting to and from all array types, such as dictionaries and lists.

Without a field declared in the model.py, the serializer ignores it since it isn’t considered part of the model itself, therefore using a custom DictionaryField model allows it to be stored as a Django model field.

Django DictionaryField Setup

  1. Install the module into your project:

    $ pip install django-dictionaryfield
    
  2. Add dictionaryfield into your INSTALLED_APPS in the Django configuration file:

    INSTALLED_APPS = (
        ...
        "dictionaryfield",
    )
    

Model Class

Use the DictionaryField for fields that should be arrays.

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
User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement