13

If I have two serializers, where one is nested, how do I setup the restore_object method? For example, if I have the following serializers defined, how do I define the restore object field for my nested serializer? It is not obvious from the documentation how to handle such a case.

class UserSerializer(serializers.Serializer):
    first_name = serializers.CharField(required=True, max_length=30)
    last_name = serializers.CharField(required=True, max_length=30)
    username = serializers.CharField(required=True, max_length=30)
    email = serializers.EmailField(required=True)
    password = serializers.CharField(required=True)

    def restore_object(self, attrs, instance=None):
        if instance:
            instance.first_name = attrs.get('first_name', instance.first_name)
            instance.last_name = attrs.get('last_name', instance.last_name)
            instance.email = attrs.get('email', instance.email)
            instance.password = attrs.get('password', instance.password)

class UserProfileSerializer(serializers.Serializer):
    user = UserSerializer()
    bio = serializers.CharField()
    def restore_object(self, attrs, instance=None):
        if instance:
            instance.bio = attrs.get('bio', instance.bio)
            instance.user = ?????

1 Answer 1

29
+200

Important sidenote: It looks like you are using the old User/Profile methodology. Since Django 1.5 there is no seperation of the default User and your custom Profile models. You have to create your own user model and use that instead of the default one: Custom User Profile @ Django Docs

Serialization

I want to present you a different approach to serialize and restore models. All model objects can be serialized by using the following snippet:

from django.core import serializers
serialized_data = serializers.serialize("json", myInstance)

or to serialize more than one object:

serialized_data = serializers.serialize("json", User.objects.all())

Foreign keys and m2m relations are then stored in an array of ids.

If you want only a subset of fields to be serialized:

serialized_data = serializers.serialize("json", myUserInstance, fields=('first_name ','last_name ','email ','password '))

To save the user profile you would just write:

serialized_data = serializers.serialize("json", myUserProfileInstance)

The user id is saved in the serialized data and looks like this:

{
    "pk": 1,
    "model": "profile.UserProfile",
    "fields": {
        "bio": "self-taught couch potato",
        "user": 1
    }
}

If you want related user fields in the serialization too, you need to modify your User model:

class UserManager(models.Manager):
    def get_by_natural_key(self, first_name, last_name):
        return self.get(first_name=first_name, last_name=last_name)

class User(models.Model):
    objects = UserManager()

    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    ...

    def natural_key(self):
        return (self.first_name, self.last_name)

    class Meta:
        unique_together = (('first_name', 'last_name'),)

When serializing with natural keys you need to add the use_natural_keys argument:

serialized_data = serializers.serialize("json", myUserProfileInstance, use_natural_keys=True)

Which leads to the following output:

{
    "pk": 2,
    "model": "profile.UserProfile",
    "fields": {
        "bio": "time-traveling smartass",
        "user": ["Dr.", "Who"]
    }
}

Deserialization

Deserializing and saving is just as easy as:

for deserialized_object in serializers.deserialize("json", serialized_data):
    deserialized_object.save()

More information can be found in the Django docs: Serializing Django objects

Sign up to request clarification or add additional context in comments.

6 Comments

That is an interesting approach, and I do agree with you that I should use the new model. But, how do I decide which fields to output. I am sure people using an API would not want to see "model": "profile.UserProfile"
Or should this be felicitated through the json module?
You can just use serialized_json = simplejson.loads(serialized_data) and provide only the fields part of the JSON: serialized_json[0]['fields']
This is a great find. One correction however, the second argument to serializers.serialize() should be an iterator, so passing an instance will bork with a TypeError. Wrap the instance in a list/tuple and you're good to go. Tested in Django 1.6.2
Perhaps this has changed since this answer was first written, but trying to serialize single instances this way generates "Not iterable" errors. To fix, use e.g. serialized_data = serializers.serialize("json", [myUserProfileInstance, ])
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.