django-rest-framework-mongoengine
django-rest-framework-mongoengine copied to clipboard
KeyError on nested serializer
I'm trying to create a Writable nested serializer as metioned in DRF Documentation.
I created following Models and serializers and am using dummy data to test it but it throws KeyError.
Models:
class User(Document):
FirstName = StringField()
LastName = StringField()
Email = EmailField()
Country = StringField()
City = StringField()
ProfilePic = URLField()
class Contact(Document):
PresentAddress = StringField()
PermanentAddress = StringField()
PhoneNumber = StringField()
class ContactSerializer(DocumentSerializer):
class Meta:
model = Contact
depth = 2
class UserSerializer(DocumentSerializer):
ContactDetail=ContactSerializer()
class Meta:
model = User
depth = 2
data:
{
"FirstName": "eamczj",
"LastName": "nrbifaxu",
"Country": "Mexico",
"Email": "[email protected]",
"ContactDetail": {
"PresentAddress":"nbirnbienibei",
"PermanentAddress":"fbfwubfuwbgw",
"PhoneNumber":"9862537341"
}
}
KeyError: u"Got KeyError when attempting to get a value for field
ContactDetail
on serializerUserSerializer
.\nThe serializer field might be named incorrectly and not match any attribute or key on thedict
instance.\nOriginal exception text was: u'ContactDetail'."
Hi, Shipra! In Mongonengine paradigm you don't need to create a separate collection for storing ContactDetails - it is an EmbeddedDocument
within the User Document
. Thus, you don't need a nested serializer for Contacts. Write it like this:
class User(Document):
FirstName = StringField()
LastName = StringField()
Email = EmailField()
Country = StringField()
City = StringField()
ProfilePic = URLField()
Contacts = EmbeddedDocumentListField(ContactDetail, required=False) # this is a list of embedded documents, e.g. [{PresentAddress: "", PermanentAddress: "", PhoneNumber: ""}, {PresentAddress: "", PermanentAddress: "", PhoneNumber: ""}], required=False allows it to be empty
class ContactDetail(EmbeddedDocument): # note, this is EmbeddedDocument, not a separate Document
PresentAddress = StringField()
PermanentAddress = StringField()
PhoneNumber = StringField()
class UserSerializer(DocumentSerializer):
class Meta:
model = User
class UserViewSet(ModelViewSet):
lookup_field = 'id'
serializer_class = UserSerializer
def get_queryset(self):
return User.objects.all()
Typically, you don't need to create nested serializers by hand because DRF-Mongoengine's DocumentSerializer
automatically parses/writes subdocuments. In rarest cases, if you know what you're doing and really-really need to create a nested serializer, you'lll have to override create()
/update()
methods for the parent serializer as DRF-Mongoengine inherited the ban for writable nested serializers from DRF.
This release of DRFM is based on DRF 3.3, which did not had writable nested. (I invented ComboReferenceField as workaround)
But it is good news they have such a thing. We can now move forward to 3.4
@BurkovBA It's a simplified version of my requirement to get the point across. Using EmbeddedDocument will not be suitable as it is referenced by other models too.
@qwiglydee I did not find ComboReferenceField in Documentation and I cannot clearly understand how to use it. Is there any example or docs for this. I would also like to know when will the support for DRF 3.4 be available?
@ShipraShalini it is experimental feature, not documented yet. I was going to put it in 'contrib' section.
@qwiglydee I'd like to use ComboReferenceField in my project too, but I'm not sure about how to use it. Am I right that mongoengine Document field, corresponding to ComboReferenceField, either contains a reference to another Document (in that case corresponding Mongoengine Document field contains id of referenced Document) or contains the data itself, as if it were an EmbeddedDocument?
Is the following code an appropriate way to define ComboReferenceField? I mean, should I just use a DynamicField in Mongoengine Document to back DRFME's ComboReferenceField?
class MyDocument(Document):
embed_or_reference = DynamicField()
class MyDocumentSerializer(DocumentSerializer):
embed_or_reference = ComboReferenceField()
class Meta:
model = MyDocument
What's the current status of ComboReferenceField? Does it work?
And a related question. So far, DRFME github repostiory doesn't contain documentation at all, so we can not update http://umutbozkurt.github.io/django-rest-framework-mongoengine/ as we don't have the sources. It seems to use Sphinx, not mkdocs right? Are you planning to store it in the repository as DRF does?
And I wanted to say that I'm working on issues, I posted. Had to dig through call graph to undertsand what's going on with custom ids, working on that issue now.
ComboReference can parse (to_internal_value
):
str
or dict {_id}
- to DBRef
, like usial ReferenceField, other dict
- to unsaved model instance.
representation depends on depth of parent serializer - either objectid, or nested object
The problem is that you need to extend serializer save/create/update method to handle the created model. This is not very good approach. Writable nested seems to be better.
Documentation is in separate branch gh-pages - the branch that automatically deployed on github.io.
To generate docs, there only needed is to checkout that branch and run make checkout && make pages
@ShipraShalini Just wanted to mention, that use of NestedSerializers is a classical way to crush your performance in DRF: http://ses4j.github.io/2015/11/23/optimizing-slow-django-rest-framework-performance/
Is there anyway to get the referenceField's data in validated_data so that I can override the create and update function to update/create the reference fields from the Parent DocumentSerializer? Also, I never get the instance in validated_data, when I explicitly include instance of the reference object in data to be validated and have to query again to assign it.
I figured it out with ComboReferenceField, though it's not included in PyPI package.
@ShipraShalini Great to hear that! PyPI package is currently broken, IIRC. I suggest that you use latest commits straight from github. In fact, we're preparing several pull requests with bugfixes in DictField(required=True) parsing, MapField(EmbeddedField), String field regex validator and other stuff.