Skip to content

Instantly share code, notes, and snippets.

@djangosporti
Forked from dimi-tree/01_models.py
Created October 17, 2017 21:34
Show Gist options
  • Save djangosporti/a84c7485f34e24f5d63fa16908f08b72 to your computer and use it in GitHub Desktop.
Save djangosporti/a84c7485f34e24f5d63fa16908f08b72 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': 'lord.commander@winterfell.com',
'username': 'lord-commander'
}
>>> s = serializers.MemberSerializer(data=data, context={})
>>> s
MemberSerializer(context={}, data={'username': 'lord-commander', 'first_name': 'John', 'last_name': 'Snow', 'email': 'lord.commander@winterfell.com'}):
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'lord.commander@winterfell.com', '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.']}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment