[Solved]-Django Rest Framework 3 Serializers on non-Model objects?

13👍

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. 🙂

1👍

Yes, you can get the object itself using DRF 3. Your update method should have this signature update(self, instance, validated_data)

Your serializer should looks like this:

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):
          // here instance is the object . 
👤levi

Leave a comment