Package: django-virtual-models
Django Virtual ModelsImprove performance and maintainability with a prefetching layer in your Django / Django REST Framework projectDocumentation:https://vintasoftware.github.io/django-virtual-models/Example project:https://github.com/vintasoftware/django-virtual-models/tree/main/exampleSource Code:https://github.com/vintasoftware/django-virtual-modelsDjango Virtual Models introduces a new "prefetching layer" to Django codebases that assists developers to express complex read logic without sacrificing maintainability, composability and performance. A Virtual Model allows developers to declare all nesting they need along with all annotations, prefetches, and joins in a single declarative class.When implementing Django REST Framework serializers, developers need to be careful to avoid causing theN+1 selects problemdue to missingprefetch_relatedorselect_relatedcalls on the associated queryset. Additionaly, developers must not missannotatecalls for fields that are computed at queryset-level.With Virtual Models integration with DRF, if you change a DRF Serializer, you won't forget to modify the associated queryset with additional annotations, prefetches, and joins. If you do forget to update the queryset, Django Virtual Models will guide you by raising friendly exceptions to assist you to write the correct Virtual Model for the serializer you're changing. This guidance will prevent N+1s and missing annotations in all serializers that use Virtual Models.For example, imagine if you have following nested serializers starting fromMovieSerializer:frommovies.modelsimportNomination,Person,MovieclassAwardSerializer(serializers.ModelSerializer):classMeta:model=Nominationfields=["award","category","year","is_winner"]classPersonSerializer(serializers.ModelSerializer):awards=AwardSerializer(many=True)nomination_count=serializers.IntegerField(read_only=True)classMeta:model=Personfields=["name","awards","nomination_count"]classMovieSerializer(serializers.ModelSerializer):directors=PersonSerializer(many=True)classMeta:model=Moviefields=["name","directors"]For good performance and correct functionality, all nested serializers must have a correspondingprefetch_relatedon the queryset used byMovieSerializer. Also, thenomination_countfield should beannotated on it. Therefore, you'll need to write this complex chain of nested prefetches:fromdjango.db.modelsimportPrefetchawards_qs=Nomination.objects.filter(is_winner=True)directors_qs=Person.objects.prefetch_related(Prefetch("nominations",queryset=awards_qs,to_attr="awards")).annotate(nomination_count=Count("nominations")).distinct()qs=Movie.objects.prefetch_related(Prefetch("directors",queryset=directors_qs))Conversely, you can declare Virtual Models for this read logic to easily reuse and customize those classes in multiple places of the codebase:importdjango_virtual_modelsasvclassVirtualAward(v.VirtualModel):classMeta:model=Nominationdefget_prefetch_queryset(self,**kwargs):returnNomination.objects.filter(is_winner=True)classVirtualPerson(v.VirtualModel):awards=VirtualAward(lookup="nominations")nomination_count=v.Annotation(lambdaqs,**kwargs:qs.annotate(nomination_count=Count("nominations")).distinct())classMeta:model=PersonclassVirtualMovie(v.VirtualModel):directors=VirtualPerson()classMeta:model=MovieTo configure your DRF view and serializer to use Virtual Models, inherit from the proper classes:importdjango_virtual_modelsasvclassMovieSerializer(v.VirtualModelSerializer):...classMeta:...virtual_model=VirtualMovieclassMovieList(v.VirtualModelListAPIView):queryset=Movie.objects.all()serializer_class=MovieSerializer...Then the library will automatically do the right prefetches and annotations for you!If, for example, you forget to add thenomination_countfield onVirtualPerson, the following exception will appear when usingMovieSerializer:If you aren't using DRF serializers, you hydrate your queryset withvirtual fieldsmanually:qs=VirtualMovie().get_optimized_queryset(Movie.objects.all(),lookup_list=["directors__awards","directors__nomination_count",])To learn more, check theInstallationand theTutorial.
Or theexample project.