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:
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.
If we use JSONField
, this simply converts the value to a string and removes this functionality.
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
Install the module into your project:
$ pip install django-dictionaryfield
Add
dictionaryfield
into yourINSTALLED_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