Last active
November 17, 2020 07:16
-
-
Save dimi-tree/031cdf9ec67dbdcad869218b755a741c to your computer and use it in GitHub Desktop.
Django REST Framework: understaning ModelSerializer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.db import models | |
class Member(models.Model): | |
# RE: null vs blank | |
# | |
# NULL https://docs.djangoproject.com/en/1.10/ref/models/fields/#null | |
# Avoid using null on string-based fields such as CharField and TextField because | |
# empty string values will always be stored as empty strings, not as NULL. | |
# | |
# BLANK https://docs.djangoproject.com/en/1.10/ref/models/fields/#blank | |
# Note that this is different than null. null is purely database-related, whereas blank is validation-related. | |
# If a field has blank=True, form validation will allow entry of an empty value. | |
# If a field has blank=False, the field will be required. | |
first_name = models.CharField(max_length=120, blank=False) | |
last_name = models.CharField(max_length=120, blank=True) # optional | |
email = models.EmailField(blank=False) | |
username = models.CharField(max_length=120, blank=False) | |
created = models.DateTimeField(max_length=120, auto_now_add=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from rest_framework import serializers | |
from models import Member | |
class MemberSerializer(serializers.ModelSerializer): | |
class Meta: | |
model = Member | |
fields = ( | |
'first_name', | |
'last_name', | |
'email', | |
'username', | |
'created' | |
) | |
read_only_fields = ( | |
'created', | |
) | |
# If model fields have `blank` parameter specified, then `required_fields` are not necessary. | |
# But | |
# "Explicit is better than implicit." ~ The Zen of Python | |
required_fields = ( | |
'first_name', | |
'email', | |
'username' | |
) | |
extra_kwargs = {field: {'required': True} for field in required_fields} | |
# | |
# VALIDATION | |
# | |
# self.is_valid() (defined in BaseSerializer) | |
# -> run_validation() (defined in Serializer) | |
# -> field-level validation is performed | |
# then validate() is called | |
# Step 1 | |
# (custom) field-level validation, if applicable | |
# http://www.django-rest-framework.org/api-guide/serializers/#validation | |
# Step 2: | |
def validate(self, data): | |
# Object-level validation | |
# Custom validation logic will go here | |
return data # validated_data | |
# | |
# SAVING INSTANCES | |
# | |
# http://www.django-rest-framework.org/api-guide/serializers/#saving-instances | |
def save(self, **kwargs): | |
# Code below is how .save() is defined in Serializer class | |
validated_data = self.validated_data + kwargs # pseudocode | |
if self.instance is not None: | |
self.instance = self.update(self.instance, validated_data) | |
else: | |
self.instance = self.create(validated_data) | |
return self.instance | |
def create(self, validated_data): | |
return Member.objects.create(**validated_data) | |
def update(self, instance, validated_data): | |
return instance | |
# NOTE: | |
# self.context (dict) is available in any methods above. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# http://www.django-rest-framework.org/api-guide/serializers/#inspecting-a-modelserializer | |
# Serializer: bare minimum | |
class MemberSerializer(serializers.ModelSerializer): | |
class Meta: | |
model = Member | |
fields = '__all__' | |
# Out | |
>>> serializers.MemberSerializer() | |
MemberSerializer(): | |
id = IntegerField(label='ID', read_only=True) | |
first_name = CharField(max_length=120) | |
last_name = CharField(allow_blank=True, max_length=120, required=False) | |
email = EmailField(max_length=254) | |
username = CharField(max_length=120) | |
created = DateTimeField(read_only=True) | |
# Serializer: explicitly set fields (recommended) | |
class MemberSerializer(serializers.ModelSerializer): | |
class Meta: | |
model = Member | |
fields = ( | |
'first_name', | |
'last_name', | |
'email', | |
'username', | |
'created' | |
) | |
# Out | |
>>> serializers.MemberSerializer() | |
MemberSerializer(): | |
first_name = CharField(max_length=120) | |
last_name = CharField(allow_blank=True, max_length=120, required=False) | |
email = EmailField(max_length=254) | |
username = CharField(max_length=120) | |
created = DateTimeField(read_only=True) | |
# Diff | |
# id field is no longer present | |
# Serializer: add read_only fields | |
class MemberSerializer(serializers.ModelSerializer): | |
class Meta: | |
model = Member | |
fields = ( | |
'first_name', | |
'last_name', | |
'email', | |
'username', | |
'created' | |
) | |
read_only_fields = ( | |
'created', | |
) | |
# Out | |
>>> serializers.MemberSerializer() | |
MemberSerializer(): | |
first_name = CharField(max_length=120) | |
last_name = CharField(allow_blank=True, max_length=120, required=False) | |
email = EmailField(max_length=254) | |
username = CharField(max_length=120) | |
created = DateTimeField(read_only=True) | |
# Diff: None | |
# This is expected as read-only fields are included in the API output, | |
# but not inlcuded in the input during create/update operations. | |
# Serializer: add required_fields | |
class MemberSerializer(serializers.ModelSerializer): | |
class Meta: | |
model = Member | |
fields = ( | |
'first_name', | |
'last_name', | |
'email', | |
'username', | |
'created' | |
) | |
read_only_fields = ( | |
'created', | |
) | |
required_fields = ( | |
'first_name', | |
'email', | |
'username' | |
) | |
extra_kwargs = {field: {'required': True} for field in required_fields} | |
# Out | |
MemberSerializer(): | |
first_name = CharField(max_length=120, required=True) | |
last_name = CharField(allow_blank=True, max_length=120, required=False) | |
email = EmailField(max_length=254, required=True) | |
username = CharField(max_length=120, required=True) | |
created = DateTimeField(read_only=True) | |
# Diff | |
# first_name, email and username now have required=True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# http://www.django-rest-framework.org/api-guide/serializers/#deserializing-objects | |
# JSON -> Django Model Instance | |
# FLOW | |
s = serializer(data=data) | |
if s.is_valid(): | |
s.save() # will call .create() | |
return s.data | |
else: | |
return s.errors | |
# After .is_valid() is called, we have available: | |
# .validated_data | |
# .errors | |
# .data | |
# .save() | |
# Example | |
data = { | |
'first_name': 'John', | |
'last_name': 'Snow', | |
'email': '[email protected]', | |
'username': 'lord-commander' | |
} | |
>>> s = serializers.MemberSerializer(data=data, context={}) | |
>>> s | |
MemberSerializer(context={}, data={'username': 'lord-commander', 'first_name': 'John', 'last_name': 'Snow', 'email': '[email protected]'}): | |
first_name = CharField(max_length=120, required=True) | |
last_name = CharField(allow_blank=True, max_length=120, required=False) | |
email = EmailField(max_length=254, required=True) | |
username = CharField(max_length=120, required=True) | |
created = DateTimeField(read_only=True) | |
>>> s.is_valid() | |
True | |
>>> s.save() | |
<Member: Member object> | |
>>> s.data | |
{'username': u'lord-commander', 'first_name': u'John', 'last_name': u'Snow', 'email': u'[email protected]', 'created': u'2016-11-18T15:11:30.456318Z'} # Note: `created` field is now present |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# http://www.django-rest-framework.org/api-guide/serializers/#serializing-objects | |
# Django Model Instance -> Json | |
# FLOW | |
s = serializer(instance=instance) | |
s.data # returns json representation | |
s = serializer(instance=instance, data=data) # you won't be able to call .is_valid() and, subsequently, .save() if no data is passed in | |
if s.is_valid(): | |
s.save() # will call .update() | |
return s.data | |
else: | |
return s.errors |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Let's see how field-validation works | |
>>> del data['username'] | |
>>> s = serializers.MemberSerializer(data=data) | |
>>> s.is_valid() | |
False | |
>>> s.errors | |
{'username': [u'This field is required.']} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you for all the useful code. Saved me hours of digging through Django docs!