Skip to content

Instantly share code, notes, and snippets.

@dimi-tree
Last active November 17, 2020 07:16
Show Gist options
  • Save dimi-tree/031cdf9ec67dbdcad869218b755a741c to your computer and use it in GitHub Desktop.
Save dimi-tree/031cdf9ec67dbdcad869218b755a741c to your computer and use it in GitHub Desktop.
Django REST Framework: understaning ModelSerializer
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)
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.
# 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
# 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
# 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
# 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.']}
@CuriousLearner2
Copy link

Thank you for all the useful code. Saved me hours of digging through Django docs!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment