Package: django-serializable-model
django-serializable-modelDjango classes to make your models, managers, and querysets serializable, with built-in support for related objects in ~100 LoC (shorter than this README!)Table of ContentsI.InstallationII.UsageIII.How it WorksIV.Related LibrariesV.BackstoryInstallationpipinstalldjango-serializable-modelIt is expected that you already have Django installedCompatibilityTestedon Django 2.2, 1.11, 1.9, and 1.5 as well as Python 3.5, 3.4, and 2.7Should work with Django 1.4-2.x and Python 2.7-3.xHas several Django backward & forward compatibility fixes built-inLikely works with Django 0.95-1.3 as wellPre 1.3 does not support theon_deleteargumenton relations.
This only affects the usage and examples below; the internals are unaffected.Pre 0.95, the Manager API didn't exist, so some functionality may be limited in those versions, or it may just error on importHave not confirmed if this works with earlier versions of Python.Please submit a PR or file an issue if you have a compatibility problem or have confirmed compatibility on versions.UsageSimplest use case, just implements the.serialize()function on a model:fromdjango.dbimportmodelsfromdjango_serializable_modelimportSerializableModelclassUser(SerializableModel):email=models.CharField(max_length=765,blank=True)name=models.CharField(max_length=100)new_user=User.objects.create(name='John Doe',email='john@doe.com',)printnew_user.serialize()# {'id': 1, 'email': 'john@doe.com', 'name': 'John Doe'}With an override of the default.serialize()function to only include whitelisted fields in the serialized dictionary:fromdjango.dbimportmodelsfromdjango_serializable_modelimportSerializableModelclassUser(SerializableModel):email=models.CharField(max_length=765,blank=True)name=models.CharField(max_length=100)# whitelisted fields that are allowed to be seenWHITELISTED_FIELDS=set(['name',])defserialize(self,*args,**kwargs):"""Override serialize method to only serialize whitelisted fields"""fields=kwargs.pop('fields',self.WHITELISTED_FIELDS)returnsuper(User,self).serialize(*args,fields=fields)new_user=User.objects.create(name='John Doe',email='john@doe.com',)printnew_user.serialize()# {'name': 'John Doe'}With a simple, one-to-one relation:fromdjango.dbimportmodelsfromdjango_serializable_modelimportSerializableModelclassUser(SerializableModel):email=models.CharField(max_length=765,blank=True)name=models.CharField(max_length=100)classSettings(SerializableModel):user=models.OneToOneField(User,primary_key=True,on_delete=models.CASCADE)email_notifications=models.BooleanField(default=False)defserialize(self,*args):"""Override serialize method to not serialize the user field"""returnsuper(Settings,self).serialize(*args,exclude=['user'])new_user=User.objects.create(name='John Doe',email='john@doe.com',)Settings.objects.create(user=new_user)new_user_refreshed=User.objects.select_related('settings').get(pk=new_user.pk)printnew_user_refreshed.serialize()# {'id': 1, 'email': 'john@doe.com', 'name': 'John Doe'}# recursively serialize Settings object by passing the join inprintnew_user_refreshed.serialize('settings')# {'id': 1, 'email': 'john@doe.com', 'settings': {'email_notifications': False}, 'name': 'John Doe'}With a foreign key relation:fromdjango.dbimportmodelsfromdjango_serializable_modelimportSerializableModelclassUser(SerializableModel):email=models.CharField(max_length=765,blank=True)name=models.CharField(max_length=100)classPost(SerializableModel):user=models.ForeignKey(User,on_delete=models.CASCADE)text=models.TextField()new_user=User.objects.create(name='John Doe',email='john@doe.com',)Post.objects.create(user=new_user,text='wat a nice post')Post.objects.create(user=new_user,text='another nice post')# called on QuerySetprintPost.objects.all().serialize()# [{'id': 1, 'text': 'wat a nice post', 'user_id': 1}, {'id': 2, 'text': 'another nice post', 'user_id': 1}]# adds an _id to the foreign key name, just like when using `.values()`# called on Manageruser1=User.objects.get(pk=new_user.pk)printuser1.post_set.serialize()# [{'id': 1, 'text': 'wat a nice post', 'user_id': 1}, {'id': 2, 'text': 'another nice post', 'user_id': 1}]# recursively serialize Post objects by passing the join inprintUser.objects.prefetch_related('post_set').get(pk=new_user.pk).serialize('post_set')"""{'id': 1,'email': 'john@doe.com','name': 'John Doe','post_set': [{'id': 1, 'text': 'wat a nice post', 'user_id': 1}, {'id': 2, 'text': 'another nice post', 'user_id': 1}]}""".serializetakes in any number of joins as its*argsand they can be of any depth, using the same__syntax asprefetch_related. This means if yourPostobject also hadCommentobjects, you could write:User.objects.prefetch_related('post_set__comment_set').serialize('post_set__comment_set')and get an array ofCommentdictionaries within eachPostdictionary. If yourPostobject also hadLikeobjects:joins=['post_set__comment_set','post_set__like_set']User.objects.prefetch_related(*joins).serialize(*joins)JSON and APIsSince.serializeoutputs a dictionary, one can turn it into JSON simply by usingjson.dumpson the dictionary.If you're building an API, you can useJSONResponseon the dictionary as well.How it worksImplementing a.serializemethod on Models, Managers, and QuerySets allows for easily customizable whitelists and blacklists (among other things) on a per Model basis.
This type of behavior was not possible a simple recursive version ofmodel_to_dict, but is often necessary for various security measures and overrides.
In order to recurse over relations / joins, it accepts the same arguments as the familiarprefetch_related, which, in my use cases, often immediately precedes the.serializecalls..serializealso uses a custommodel_to_dictfunction that behaves a bit differently than the built-in one in a variety of ways that are more expected when building an API (see the docstring).I'd encourage you to read the source code, since it's shorter than this README :)Related Librariesdjango-api-decoratorsTiny decorator functions to make it easier to build an API using Django in ~100 LoCBackstoryThis library was built while I was working onYorango's ad-hoc API. Writing code to serialize various models was complex and quite tedious, resulting in messy spaghetti code for many of our API methods. The only solutions I could find online were theDjango Full Serializersfromwadofstuffas well as some recursivemodel_to_dictsnippets online -- none of which gave the option for customizable whitelists and blacklists on a per Model basis.
Later on, I found thatDjango REST Framework's ModelSerializersdo offer similar functionality to what I was looking for (andwithoutrequiring buy-in to the rest of the framework), albeit with some added complexity and robustness.I ended up writing my own solution in ~100 LoC that handled basically all of my needs and replaced a ton of messy serialiazation code from all around the codebase. It was used in production with fantastic results, including on queries with quite the complexity and depth, such as:joins=['unit_set','unit_set__listing_set','unit_set__listing_set__tenants','unit_set__listing_set__bill_set','unit_set__listing_set__payment_set__payer','unit_set__listing_set__contract']s_props=(user.property_set.all().prefetch_related(*joins).serialize(*joins))Had been meaning to extract and open source this as well as other various useful utility libraries I had made at Yorango and finally got the chance!