DocumentSerializer

DocumentSerializer is a subclass of DRF's ModelSerializer.

If you are not familiar with DRF ModelSerializer, you should first visit it's documentation page.

For basic implementation, DocumentSerializer works similar to ModelSerializer.

Sample Implementation

models.py

class User(Document):
    username = StringField(max_length=30)
    email = EmailField(max_length=30)
    friends = ListField(ReferenceField('self'))
    extra = DictField()

class BlogExtension(EmbeddedDocument):
    further_read = StringField(required=True)
    references = ListField(StringField())

class Blog(DynamicDocument):
    owner = ReferenceField(User)
    title = StringField(max_length=30)
    tags = ListField(StringField())

class Comment(EmbeddedDocument):
    author = ReferenceField(User)
    text = StringField(max_length=140)
    is_approved = BooleanField(default=False)

class Post(Document):
    author = ReferenceField(User)
    blog = ReferenceField(Blog)
    text = StringField()
    comments = ListField(EmbeddedDocumentField(Comment))
    extension = EmbeddedDocumentField(BlogExtension)

serializers.py

from rest_framework_mongoengine.serializers import DocumentSerializer

class PostSerializer(DocumentSerializer):
    class Meta:
        model = Post
        depth = 2

Post document is serialized to:

{
    "id": "54b453dd03c9804f7fbd822f",
    "blog": {
        "id": "5452025903c980036a7221f1",
        "owner": "5452020703c980036a7221f0",
        "title": "new blog"
        "tags": [
            "python",
            "django",
            "mongodb"
        ],
    },
    "author": {
        "id": "5452020703c980036a7221f0",
        "username": "John",
        "email": null,
        "friends": [
            "id": "54b188db03c98125076329c4",
            "username": "Tim",
            "email": null,
            "friends": [
                "5452020703c980036a7221f0"
                ]
        ],
        "extra": {"is_verified": true}
    },
    "text": "Yet another new post about MongoDB",
    "comments": [
        {
            "text": "Definitely a good read.",
            "is_approved": true,
            "author": "54b188db03c98025076329c4"
        },
        {
            "text": "Thanks for the post!",
            "is_approved": true,
            "author": "54b188db03c98125076329c4"
        }
    ],
    "extension": {
        "references": [
            "Tom",
            "Mongoengine",
            "DRF"
        ],
        "further_read": "See Tom's post about Mongoengine"
    }
}

Warnings

DocumentSerializer can get basic-field's (like StringField) kwargs and pass it to serializer for validation. But on compound fields like ListField, EmbeddedDocumentField, there is no kwarg to pass for validation. For example ListField(User), serializer can not know User's fields and their kwargs. If you want that to restrict users and improve validation on compound fields, you should use nested serializers.

Nested Serializers

In many cases, you may want to customize serialization process. You can use nested serializers.

You can use DocumentSerializer for:

EmbeddedDocumentSerializer for:

For concerns about ambiguity and complexity about automatic nested serialization, Django Rest Framework has decided to NOT to do it. Instead, you can explicitly define your nested-serialization behavior. See DRF documentation

For using DocumentSerializer as nested serializer, you have to implement it manually like documented.

Sample Implementation

from rest_framework_mongoengine.serializers import DocumentSerializer, EmbeddedDocumentSerializer

class ExtensionSerializer(EmbeddedDocumentSerializer):
    class Meta:
        model = BlogExtension

class BlogSerializer(DocumentSerializer):
    extension = ExtensionSerializer(many=False)

    class Meta:
    model = Blog

class PostSerializer(DocumentSerializer):
    author = FriendSerializer(many=False)
    comments = CommentSerializer(many=True)
    extension = ExtensionSerializer(many=False)

    class Meta:
        model = Post
        fields = ('id', 'blog', 'author', 'text', 'comments', 'extension')
        depth = 2

DynamicDocumentSerializer

Using DynamicDocuments, you can save any extra attributes without defining excplicitly on the model. See Mongoengine docs for further info.

DynamicDocumentSerializer is built to support that feature. Any extra key-value combination on request.data will be saved. It can be risky to save any data that comes with request, so use use it at your own risk.

With great power, comes great responsibility.

EmbeddedDocumentSerializer

Unlike DocumentSerializer, behavior on EmbeddedDocuments are not ambiguous. You dont have to implement when nesting EmbeddedDocumentSerializer, it is done automatically for you on the go while (de)serializing.

Note:Calling EmbeddedDocumentSerializer.save() will raise an exception, because EmbeddedDocuments need a Document to attach to.

Warning About EmbeddedDocumentSerializer(many=True)

On DRF 3, when you set serializer with many=True kwarg, it automatically converts to ListSerializer, which will lead to a nested serializer exception (from DRF). There is a workaround which is supplying .create() and/or .update() methods.

For example, as you see below, custom .update() is provided because CommentSerializer has kwarg many=True.

class CommentSerializer(EmbeddedDocumentSerializer):
    class Meta:
        model = Comment

class PostSerializer(DocumentSerializer):
    comments = CommentSerializer(many=True)
    extension = ExtensionSerializer(many=False)

    class Meta:
        model = Post
        fields = ('id', 'blog', 'author', 'text', 'comments', 'extension')
        depth = 2

    def update(self, instance, validated_data):
        comments = validated_data.pop('comments')
        updated_instance = super(PostSerializer, self).update(instance, validated_data)

        for comment_data in comments:
            updated_instance.comments.append(Comment(**comment_data))

        updated_instance.save()
        return updated_instance