i'm doing an upgrade to DRF3.1.1 from 2.4. I was using a custom serializer to create an instance of an object that's not a Model.
In 2.4, it was easy enough to do this because in the serializer, I would create the object in restore_object()
. In the view, i'd call serializer.is_valid()
and then pop the instance of the object out of the serializer with serializer.object
. Then I could do whatever I want.
With the 3.x changes, it's harder to get the instance out of the object because the create and update methods are supposed to do the saving, and "serializer.object" isn't available anymore.
As an example, I used to have this for my "UserRegistration" Object. This is not a model because it's a convenience object that the server parses up and stores data in a number of other objects/db tables.
class UserRegistration(object):
def __init__(self, full_name, stage_name, password="", email="", locale="en_US"):
self.full_name = full_name
self.password = password
self.locale = locale
self.email = email
self.stage_name = stage_name
Here's the associated DRF-2.4 serializer:
class UserRegistrationSerializer(serializers.Serializer):
full_name = serializers.CharField(max_length=128, required=False)
stage_name = serializers.CharField(max_length=128)
password = serializers.CharField(max_length=128, required=False)
locale = serializers.CharField(max_length=10, required=False)
# use CharField instead of EmailField for email. We do our own validation later to make for a better error msg.
email = serializers.CharField(max_length=254, required=False)
def restore_object(self, attrs, instance=None):
if instance is not None:
instance.full_name = attrs.get('full_name', instance.full_name)
instance.password = attrs.get('password', instance.password)
instance.locale = attrs.get('locale', instance.locale)
instance.email = attrs.get('email', instance.email)
instance.stage_name = attrs.get('stage_name', instance.stage_name)
return instance
return UserRegistration(**attrs)
Then in my view, I do something like this:
class UserRegistration(APIView):
throttle_classes = ()
serializer_class = UserRegistrationSerializer
def post(self, request, format=None):
event_type = "user_registration"
serializer = UserRegistrationSerializer(data=request.DATA, context={'request': request})
try:
if serializer.is_valid():
user_registration = serializer.object
# save user_registration pieces in various places...
However, in DRF3, I serializer.object
is gone. The docs say to do "validation" using serializer.validated_data
, but that's just a hash and not the real object. Is there a way to get the object?
The whole thing seems more married to DB objects, which in this particular case is exactly what i'm trying to avoid.
Am I just missing some new DRF3 concept?
Serializers in Django REST Framework are responsible for converting objects into data types understandable by javascript and front-end frameworks. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.
Serializers allow complex data such as querysets and model instances to be converted to native Python datatypes that can then be easily rendered into JSON , XML or other content types.
The HyperlinkedModelSerializer class is similar to the ModelSerializer class except that it uses hyperlinks to represent relationships, rather than primary keys. By default the serializer will include a url field instead of a primary key field.
According to Microsoft documentation: Serialization is the process of converting an object into a stream of bytes to store the object or transmit it to memory, a database or file. Its main purpose is to save the state of an object in order to be able to recreate it when needed.
Thanks @levi for the beginnings of an answer, but unfortunately, that's not all of it, so I think this is a more complete answer.
I originally asked:
Am I just missing some new DRF3 concept?
Turns out...Yep. I was. The docs talk about the new Single-step object creation
, which made me think the serialization and model had become more tightly coupled. This thought was incorrect, because if you write your own custom serializer, it's up to you to do the actual object save (or not) in the new serializer.update()
and serializer.create()
methods.
I also asked:
In 2.4, it was easy enough to do this because in the serializer, I would create the object in restore_object(). In the view, i'd call serializer.is_valid() and then pop the instance of the object out of the serializer with serializer.object. Then I could do whatever I want.
With the 3.x changes, it's harder to get the instance out of the object because the create and update methods are supposed to do the saving, and "serializer.object" isn't available anymore.
Although there's no serializer.object
that you can use to pull the created object out of after calling serializer.is_valid()
, the serializer.save()
method returns the object itself, which in my case was just fine.
So, turns out, the code change wasn't very big at all. Here's my new code that is pretty happy with DRF-3:
class UserRegistration(object):
def __init__(self, full_name, stage_name, password="", email="", locale="en_US", notification_pref="ask"):
self.full_name = full_name
self.password = password
self.locale = locale
self.email = email
self.stage_name = stage_name
class UserRegistrationSerializer(serializers.Serializer):
full_name = serializers.CharField(max_length=128, required=False)
stage_name = serializers.CharField(max_length=128)
password = serializers.CharField(max_length=128, required=False)
locale = serializers.CharField(max_length=10, required=False)
# use CharField instead of EmailField for email. We do our own validation later to make for a better error msg.
email = serializers.CharField(max_length=254, required=False)
def update(self, instance, validated_data):
instance.full_name = validated_data.get('full_name', instance.full_name)
instance.password = validated_data.get('password', instance.password)
instance.locale = validated_data.get('locale', instance.locale)
instance.email = validated_data.get('email', instance.email)
instance.stage_name = validated_data.get('stage_name', instance.stage_name)
return instance
def create(self, validated_data):
return UserRegistration(**validated_data)
notice that there's no saving of the object to any DB in the Serializer. I'm just creating or updating the object and then returning it.
Now the view looks like this:
class UserRegistration(APIView):
throttle_classes = ()
serializer_class = UserRegistrationSerializer
def post(self, request, format=None):
event_type = "user_registration"
serializer = UserRegistrationSerializer(data=request.DATA, context={'request': request})
try:
if serializer.is_valid():
user_registration = serializer.save()
# save user_registration pieces in various places...
I also said in my original post:
The whole thing seems more married to DB objects, which in this particular case is exactly what i'm trying to avoid.
This statement was also incorrect as seen by the fact that the create and update methods don't have to save anything to any DB.
One caveat here is that the code is functional, but obviously I'm just wrapping my head around some of the DRF2.x->3.x changes, so I could be doing this in a non-DRF way. If so, someone who knows please feel free to tell me how to do it better. :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With