Package: djangorestframework-deepserializer
DeepSerializerEasiest Django REST framework addon to complete a django rest framework api in less than 1 dayIntroductiondjangorestframework-deepserializeris a Django REST framework package that provides deep serialization of nested JSON. It supports various types of relationships includingone_to_one,one_to_many,many_to_one,many_to_many, and also in reverse through theirrelated_name. This package is particularly useful if you really don't want to work.
This projects presume that you already have some or all your django models completed.InstallationYou can installdjangorestframework-deepserializerusing pip:pipinstalldjangorestframework-deepserializerUsageFor ultra-fast development.If you just want to have an API ready for your model.models.pyfromdjango.dbimportmodelsclassImage(models.Model):id=models.IntegerField(primary_key=True,editable=False)url=models.CharField(max_length=200)classTag(models.Model):name=models.CharField(primary_key=True)description=models.TextField(max_length=4000)classBook(models.Model):title=models.CharField(primary_key=True)description=models.TextField(max_length=4000)tags=models.ManyToManyField(Tag)cover=models.ForeignKey(Image,on_delete=models.CASCADE,blank=True,null=True,related_name="books")classChapter(models.Model):classMeta:unique_together=(('book','number'),)id=models.CharField(primary_key=True,editable=False)book=models.ForeignKey(Book,on_delete=models.CASCADE,related_name="chapters")number=models.IntegerField(default=0)content=models.TextField()defsave(self,*args,**kwargs):self.id="_".join(str(getattr(self,field).pkifisinstance(getattr(self,field),models.Model)elsegetattr(self,field))forfieldinself._meta.unique_together[0])super().save(*args,**kwargs)urls.pyfromrest_frameworkimportroutersfromdeepserializerimportDeepViewSetfrommyapp.modelsimportBook,Chapter,Tagrouter=routers.DefaultRouter()DeepViewSet.init_router(router,[Book,Chapter,Tag])If you need the read only ViewSets version, replace DeepViewSet with ReadOnlyDeepViewSetTheinit_routerfunction will create all the necessary ViewSets and Serializers for The given models and will register them in the router.For ultra-fast development, with a bit of control.If you need one of the serializer to act in a specific way, for example also return the number of chapter, you can write your own:serializers.pyfromrest_frameworkimportserializersfromdeepserializerimportDeepSerializerfrommyapp.modelsimportBook,Chapter,TagclassBookSerializer(DeepSerializer):classMeta:model=Bookdepth=10fields='__all__'# use_case = "" # by default the use_case is an empty stringchapters_count=serializers.SerializerMethodField()defget_chapters_count(self,obj):returnlen(obj.chapters)This serializer, because of the absence ofuse_caseinside the Meta class, will be considered the main serializer for this model and will automatically be retrieved when this model need a serializer.urls.pyfromrest_frameworkimportroutersfromdeepserializerimportDeepViewSetfrommyapp.modelsimportBook,Chapter,Tagfrommyapp.serializersimport*# is needed to allow DeepViewSet to load the serializer in its dict of serializersrouter=routers.DefaultRouter()router.register("Book",DeepViewSet.get_view(Book),basename="Book")The Serializer used for the Book model will be BookSerializer, and this at any depth.For ultra-fast development, with even more control.If you need one of the viewsets to act in a specific way, for example using one serializer for list and another for the rest, you can write your own:serializers.py...classNoInfoBookSerializer(DeepSerializer):classMeta:model=Bookdepth=0fields=("title","description")use_case="NoInfo"views.pyfromdeepserializerimportDeepViewSetfrommyapp.modelsimportBookfrommyapp.serializersimportNoInfoBookSerializerclassBookViewSets(DeepViewSet):queryset=Book.objectsdefget_serializer_class(self):ifhasattr(self,'action')andself.action=='list':# return DeepSerializer.get_serializer_class(self.queryset.model, use_case="NoInfo")  # will do the same thing, importing it this way will protect against circular importreturnNoInfoBookSerializerreturnNoInfoBookSerializer.get_serializer_class(self.queryset.model)# this will retrieve BookSerializer without having to manually import itThis viewsets will use two different serializer depending on the current action.get_serializer_classfunction will get a defined serializer if it exists or create a new one for the given model and use_case if not.For deep serialization of nested Model.If you have a dict of list of ...., and you want to create it in one request, you can:The posted request:{"title":"My Book","description":"My first try to write something","tags":[{"name":"action","description":"battles!!!!!"},{"name":"adventure","description":"Story driven"}],"chapters":[{"number":1,"content":"Chapter 1 ..."},{"number":2,"content":"Chapter 2 ..."},{"number":3,"content":"Chapter 3 ..."}]}views.pyfromrest_frameworkimportstatusfromrest_framework.responseimportResponsefromdeepserializerimportDeepViewSetfrommyapp.modelsimportBook,ChapterclassDeepCreateBookViewSets(DeepViewSet):queryset=Book.objectsdefcreate(self,request,*args,**kwargs):serializer=self.get_serializer()results=serializer.deep_update_or_create(self.queryset.model,[request.data],delete_models=[Chapter])ifany("ERROR"initemforiteminresultsifisinstance(item,dict)):returnResponse(results,status=status.HTTP_409_CONFLICT)returnResponse(results,status=status.HTTP_201_CREATED)Thedeep_update_or_createfunction get a list of data and return either a list of primary_keys or a list of representations
based on the optional parameter verbose.The optional parameter are:verboseDefine the amount of returned ny the function, by default it isTrue:Ifverbose=Trueit will return a list of representations, in this case it will return almost the same as therequest.databut withidandbookinside thechaptersdicts.Ifverbose=Falseit will return a list of primary_keys, in this case it will return["My Book"].delete_models: List of all the model to delete the previously linked instances not present in therequest.dataIf a validation error occurred during the creation process it will return the representations, regardless ofverbose, with only the problematic fields + a newERRORfield, even for the modelsTheupdateorcreatein thedeep_update_or_createfunction are based on the primary key of the dict:With primary key:  The function will search for this data in the database:If the key exist: it will update this instance with the given data (the update will be withpartial=True), but only one time. If other dict are found with this primary key for this model, the updating process will be skipped.If the key does not exist: It will create a new instance of this model with the given data, but only one time. If other dict are found with this primary key for this model, the creation process will be skipped.Without primary key: The function will not search for this data in the database and will directly create it.With this you can do things like this:[{"title":"My Book","description":"My first try to write something","tags":[{"name":"action","description":"battles!!!!!"},"adventure"],"chapters":[],"cover":{"id":-1,"url":"http://example.com/000000"},},{"title":"My Book 2","description":"My second try to write something","tags":[{"name":"action","description":"battles!!!!!"}],"chapters":[],"cover":{"id":-1,"url":"http://example.com/000000"},}]In the case thatactionand-1do not exist in the database:
TheactionTag will be created only one time and will be linked to bothMy BookandMy Book 2,
And the-1Image will be created one time with the new primary key42(for example) and be linked to bothMy BookandMy Book 2.The types of relationships that are supported include:one_to_one: One instance of a model is related to one instance of another model.one_to_many: One instance of a model is related to many instances of another model.many_to_one: Many instances of a model are related to one instance of another model.many_to_many: Many instances of a model are related to many instances of another model.And in reverse with:related_name: The name to use for the relation from the related object back to this one.ContributingContributions are welcome! Please read the contributing guidelines before getting started.LicenseThis project is licensed under the terms of the MIT license.