Serializing Google App Engine model objects using the AMF protocol is very efficient. AMF even supports references to objects, so repeated serialization of references to the same object won’t waste bandwidth.
For instance, if we have a model like this:
class Project(db.Model): code = db.IntegerProperty() name = db.StringProperty() department = db.IntegerProperty(default=0) created_at = db.DateTimeProperty(auto_now_add=True) modified_at = db.DateTimeProperty(auto_now=True) class ProjectParticipant(db.Model): project = db.ReferenceProperty(Project, collection_name='participants') name = db.StringProperty() created_at = db.DateTimeProperty(auto_now_add=True) modified_at = db.DateTimeProperty(auto_now=True)
And we return a list of 5 participants of 2 projects, each project’s data could be serialized only once, although on the client side we will have the project data in each participant instance. This is good and PyAMF supports this feature. [Edited after some clarification from Nick Joyce.]
But sometimes we may want more control over what is serialized. For instance, we can have an object that has references to 5 other objects. But our client may not need to use their data, so it would be a waste to serialize it.
We can have fine control over what is serialized, with more work on the server side, by creating a data transfer object (DTO) that will be returned. That way we can decide whether we want deep or shallow serialization. We can also restrict the data that will be serialized and return keys or references as we think will be better for use on client side.
This is an example of the DTO approach:
class DTO(object): pass class ProjectParticipantsService: def get(self, key): return self.to_dto(ProjectParticipant.get(key)) def to_dto(self, participant): dto = DTO() dto._key = str(participant.key()) dto._id = participant.key().id() dto.project_key = str(participant.project.key()) dto.name = participant.name dto.created_at = participant.created_at dto.modified_at = participant.modified_at return dto
A generic DTO class is used. I don’t see much point on creating a specific class, like you would do on Java. This way, you only have one place where you control your DTO’s structure. DRY.
Some caveats that may bite you when you are coding late at night:
- Your DTO cannot be a direct instance of object, like dto = object(). Instances of object cannot be extended with dynamic attributes.
- Be sure to convert the key to a string. If you try to serialize the key itself, like dto.project_key = participant.project.key(), there will be an error like “AttributeError: ‘str’ object has no attribute ‘iteritems'”.
I published an example at github.
“At last! It works.” (Cosmix)