Package: drf-sideloading
:warning: Note that there are major API changes since version 0.1.1 that have to be taken into account when upgrading!:warning: Python 2 and Django 1.11 are no longer supported from version 1.4.0!Django rest framework sideloadingDRF-sideloading is an extension to provide side-loading functionality of related resources. Side-loading allows related resources to be optionally included in a single API response minimizing requests to the API.QuickstartInstall drf-sideloading:pipinstalldrf-sideloadingImportSideloadableRelationsMixin:fromdrf_sideloading.mixinsimportSideloadableRelationsMixinWrite your SideLoadableSerializer:You need to define theprimaryserializer in the Meta data and can define prefetching rules.
Also notice themany=Trueon the sideloadable relationships.fromdrf_sideloading.serializersimportSideLoadableSerializerclassProductSideloadableSerializer(SideLoadableSerializer):products=ProductSerializer(many=True)categories=CategorySerializer(source="category",many=True)primary_suppliers=SupplierSerializer(source="primary_supplier",many=True)secondary_suppliers=SupplierSerializer(many=True)suppliers=SupplierSerializer(many=True)partners=PartnerSerializer(many=True)classMeta:primary="products"prefetches={"categories":"category","primary_suppliers":"primary_supplier","secondary_suppliers":"secondary_suppliers","suppliers":{"primary_suppliers":"primary_supplier","secondary_suppliers":"secondary_suppliers",},"partners":"partners",}PrefetchesFor fields where the source is provided or where the source matches the field name, prefetches are not strictly requiredMultiple prefetches can be added to a single sideloadable field, but when using Prefetch object check that they don't clash with prefetches made in the get_queryset() methodfromdjango.db.modelsimportPrefetchprefetches={"categories":"category","primary_suppliers":["primary_supplier","primary_supplier__some_related_object"],"secondary_suppliers":Prefetch(lookup="secondary_suppliers",queryset=Supplier.objects.prefetch_related("some_related_object")),"partners":Prefetch(lookup="partners",queryset=Partner.objects.select_related("some_related_object"))}Multiple sources can be added to a field using a dict.
Each key is a source_key that can be used to filter what sources should be sideloaded.
The values set the source and prefetches for this source.Note that this prefetch reusesprimary_supplierandsecondary_suppliersif suppliers and primary_supplier or secondary_suppliers are sideloadedprefetches={"primary_suppliers":"primary_supplier","secondary_suppliers":"secondary_suppliers","suppliers":{"primary_suppliers":"primary_supplier","secondary_suppliers":"secondary_suppliers"}}Usage of Prefetch() objects is supported.
Prefetch() objects can be used to filter a subset of some relations or just to prefetch or select complicated related objects
In case there are prefetch conflicts,to_attrcan be set but be aware that this prefetch will now be a duplicate of similar prefetches.
prefetch conflicts can also come from prefetched made in the ViewSet.get_queryset() method.Note that this prefetch noes not reuseprimary_supplierandsecondary_suppliersifsuppliersandprimary_supplierorsecondary_suppliersare sideloaded at the same time.fromdjango.db.modelsimportPrefetchprefetches={"categories":"category","primary_suppliers":"primary_supplier","secondary_suppliers":"secondary_suppliers","suppliers":{"primary_suppliers":Prefetch(lookup="secondary_suppliers",queryset=Supplier.objects.select_related("some_related_object"),to_attr="secondary_suppliers_with_preselected_relation"),"secondary_suppliers":Prefetch(lookup="secondary_suppliers",queryset=Supplier.objects.filter(created_at__gt=pendulum.now().subtract(days=10)).order_by("created_at"),to_attr="latest_secondary_suppliers")},}Configure sideloading in ViewSet:IncludeSideloadableRelationsMixinmixin in ViewSet and definesideloading_serializer_classas shown in example below.
Everything else stays just like a regular ViewSet.
Since version 2.0.0 there are 3 new methods that allow to overwrite the serializer used based on the request version for examplefromdrf_sideloading.mixinsimportSideloadableRelationsMixinclassProductViewSet(SideloadableRelationsMixin,viewsets.ModelViewSet):"""A simple ViewSet for viewing and editing products."""queryset=Product.objects.all()serializer_class=ProductSerializersideloading_serializer_class=ProductSideloadableSerializerdefget_queryset(self):# Add prefetches for the viewset as normalreturnsuper().get_queryset().prefetch_related("created_by")defget_sideloading_serializer_class(self):# use a different sideloadable serializer for older versionifself.request.version<"1.0.0":returnOldProductSideloadableSerializerreturnsuper().get_sideloading_serializer_class()defget_sideloading_serializer(self,*args,**kwargs):# if modifications are required to the serializer initialization this method can be used.returnsuper().get_sideloading_serializer(*args,**kwargs)defget_sideloading_serializer_context(self):# Extra context provided to the serializer class.return{"request":self.request,"format":self.format_kwarg,"view":self}Enjoy your API with sideloading supportExample request and response when fetching all possible valuesGET /api/products/?sideload=categories,partners,primary_suppliers,secondary_suppliers,suppliers,products{"products":[{"id":1,"name":"Product 1","category":1,"primary_supplier":1,"secondary_suppliers":[2,3],"partners":[1,2,3]}],"categories":[{"id":1,"name":"Category1"}],"primary_suppliers":[{"id":1,"name":"Supplier1"}],"secondary_suppliers":[{"id":2,"name":"Supplier2"},{"id":3,"name":"Supplier3"}],"suppliers":[{"id":1,"name":"Supplier1"},{"id":2,"name":"Supplier2"},{"id":3,"name":"Supplier3"}],"partners":[{"id":1,"name":"Partner1"},{"id":2,"name":"Partner1"},{"id":3,"name":"Partner3"}]}The user can also select what sources to load to Multi source fields.
Leaving the selections empty or omitting the brackets will load all the prefetched sources.Example:GET /api/products/?sideload=suppliers[primary_suppliers]{"products":[{"id":1,"name":"Product 1","category":1,"primary_supplier":1,"secondary_suppliers":[2,3],"partners":[1,2,3]}],"suppliers":[{"id":1,"name":"Supplier1"}]}Example ProjectDirectoryexamplecontains an example project using django rest framework sideloading library. You can set it up and run it locally using following commands:cdexample
shscripts/devsetup.sh
shscripts/dev.shContributingContributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.Setup for contributionsource<YOURVIRTUALENV>/bin/activate(myenv)$pipinstall-rrequirements_dev.txtTest$maketestRun tests with environment matrix$maketoxRun tests with specific environment$tox--listenvs
py37-django22-drf39
py38-django31-drf311
py39-django32-drf312# ...$tox-epy39-django32-drf312Test coverage$makecoverageUsepyenvfor testing using different python versions locally.LicenseMITCreditsDemur NodiaTõnis VäinMadis VäinLenno NagelChangelog2.0.1 (2021-12-16)Ensure that only allowed methods are sideloaded2.0.0 (2021-12-10)Major refactoring to allow for multi source fields.Add support for multi source fieldsAdd support for detail view sideloadingDropped formless BrowsableAPIRenderer enforcementRaises error in case invalid fields are requested for sideloading1.4.2 (2021-04-12)Add support for lists in filter_related_objects1.4.1 (2021-04-09)Fix sideloadable prefetches1.4.0 (2021-04-07)Python supported versions3.6 -> 3.9Django supported versions2.2,3.1,3.2Django-rest-framework supported versions.3.9 -> 3.121.3.1 (2021-04-07)Added support fordjango.db.models.Prefetch1.3.0 (2019-04-23)Fix empty related fields sideloading bugSupport for Django 2.21.2.0 (2018-10-29)Completely refactored sideloading configuration via a custom serializer.Support for Django 2.1Support for Django-rest-framework 3.90.1.10 (2017-07-20)Support for Django 2.00.1.8 (2017-07-20)change sideloadable_relations dictalways required to define 'serializer'key is referenced to url and serialized in as rendered jsonaddsourcewhich specifies original model field name0.1.0 (2017-07-20)First release on PyPI.