본문 바로가기

프레임워크/Django

As a Beginner, going through Django Rest Framework tutorial: #2

1. Serialization

Enough of Django basic settings. Now let's move on to the rest framework the very fisrt of which is Serialization. We start by creating a serializer as given in the tutorial

# tutorial/snippets/serializers.py
from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES


class SnippetSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(required=False, allow_blank=True, max_length=100)
    code = serializers.CharField(style={'base_template': 'textarea.html'})
    linenos = serializers.BooleanField(required=False)
    language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
    style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')

    def create(self, validated_data):
        """
        Create and return a new `Snippet` instance, given the validated data.
        """
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        Update and return an existing `Snippet` instance, given the validated data.
        """
        instance.title = validated_data.get('title', instance.title)
        instance.code = validated_data.get('code', instance.code)
        instance.linenos = validated_data.get('linenos', instance.linenos)
        instance.language = validated_data.get('language', instance.language)
        instance.style = validated_data.get('style', instance.style)
        instance.save()
        return instance

First things first, what is serializer? My lame understanding about serialization is that we convert objects into bytes that is appropriate to transfer via network. Obviously, there are many formats to choose like JSON, XML, pickle, etc (Link) ; I will however only focus on JSON. Lets's continue a bit further with the tutorial.

from snippets.serializers import SnippetSerializer

snippet = Snippet(code='print("hello, world")\n')
serializer = SnippetSerializer(snippet)

With the snippet instance as argument, the instance of SnippetSerializer called serializer is created and it has the instance field called data which contains the following:

serializer.data
# {'id': 2, 'title': '', 'code': 'print("hello, world")\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}

It looks like JSON but it is still a dictonary object of some kind as shown below:

type(serializer.data)
# <class 'rest_framework.utils.serializer_helpers.ReturnDict'>

So we need to continue on conversion. In this sense, I feel like the name serializer is a bit deceptive because when I first get my hands on this I naturally assume that all the serialization processes must be completed just using serializer. Let's finish serialization by polishing serializer.data with JSONRenderer:

from rest_framework.renderers import JSONRenderer
content = JSONRenderer().render(serializer.data)
content
# b'{"id": 2, "title": "", "code": "print(\\"hello, world\\")\\n", "linenos": false, "language": "python", "style": "friendly"}'
type(content)
# <class 'bytes'>

Voilà! The information that the snippet instance was carrying was successfully converted in to JSON format. It is worth mentioning that there is b preceding a quotation mark in content which stands for bytes.

Deserialization from content to data can be done using serializer as well:

import io
from rest_framework.parsers import JSONParser

stream = io.BytesIO(content)
data = JSONParser().parse(stream)

serializer = SnippetSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# OrderedDict([('title', ''), ('code', 'print("hello, world")\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
serializer.save()
# <Snippet: Snippet object>

It is worth paying attention to the difference between the two cases for instantiating SnippetSerializer:

# First case
snippet = Snippet(code='print("hello, world")\n')
serializer = SnippetSerializer(snippet)
serializer.save()
# Success for save


# Second case
stream = io.BytesIO(content)
data = JSONParser().parse(stream)
serializer = SnippetSerializer(data=data)
serializer.save()
# AssertionError: You must call `.is_valid()` before calling `.save()`.
serializer.is_valid()
# True
serializer.save()
# created called
# <Snippet: Snippet object (11)>
# Success for save

For the second case, save method right after the instantiation led to an error saying You must call `.is_valid()` before calling `.save(). This is to make sure that the data provided from a client through the network is consistent with the fields and the types of the values as defined in class SnippetSerializer above. What seems to be critical is that what is defined in class SnippetSerializer should be consistent with what is defined in the model definition of class Snippet.

At this point, the compariosn of the fields bewteen class Snippet and class SnippetSerializer is as follows:

Fields class Snippet(models.Model) class SnippetSerializer(serializers.Serializer)
created models.DateTimeField(auto_now_add=True)  
id   serializers.IntegerField(read_only=True)
title models.CharField(max_length=100, blank=True, default='') serializers.CharField(required=False, allow_blank=True, max_length=100)
code models.TextField() serializers.CharField(style={'base_template': 'textarea.html'})
linenos models.BooleanField(default=False) serializers.BooleanField(required=False)
language models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100) serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
style models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100) serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')

Basically, all the fields except created and id are almost the same to each other. Obviously, this is not optimal because the fields of a serializer have to be hard-coded every time according to the fields definition of a model class. This is prone to errors and not scalable either. This is where ModelSerializer instead of just Serializer comes into play.

class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ['id', 'title', 'code', 'linenos', 'language', 'style']

With this, we have the same implementation as the previous one with a lot less code. I repete here what is decribed in the tutorial:

It's important to remember that ModelSerializer classes don't do anything particularly magical, they are simply a shortcut for creating serializer classes:

    - An automatically determined set of fields.
    - Simple default implementations for the create() and update() methods.

'프레임워크 > Django' 카테고리의 다른 글

As a Beginner, going through Django Rest Framework tutorial: #1  (0) 2020.06.11
Django http  (0) 2020.06.06
장고 모델  (0) 2020.06.06
장고 (Django)  (0) 2020.06.06