{"id":13501279,"url":"https://github.com/vintasoftware/django-virtual-models","last_synced_at":"2025-04-07T15:06:05.771Z","repository":{"id":61519202,"uuid":"537639003","full_name":"vintasoftware/django-virtual-models","owner":"vintasoftware","description":"Improve performance and maintainability with a prefetching layer in your Django project","archived":false,"fork":false,"pushed_at":"2024-01-19T17:00:22.000Z","size":673,"stargazers_count":152,"open_issues_count":32,"forks_count":4,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-03-31T14:11:34.984Z","etag":null,"topics":["django","django-rest-framework","orm","performance","python"],"latest_commit_sha":null,"homepage":"https://vintasoftware.github.io/django-virtual-models/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vintasoftware.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.rst","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS.rst","dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-09-16T22:38:37.000Z","updated_at":"2024-11-26T15:36:40.000Z","dependencies_parsed_at":"2024-01-16T10:35:19.027Z","dependency_job_id":"bb5912b9-94b2-4f74-9a46-5563645fb02f","html_url":"https://github.com/vintasoftware/django-virtual-models","commit_stats":{"total_commits":51,"total_committers":1,"mean_commits":51.0,"dds":0.0,"last_synced_commit":"1a695be8e40ff63a180c5a3d228e116ae9be1d26"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vintasoftware%2Fdjango-virtual-models","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vintasoftware%2Fdjango-virtual-models/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vintasoftware%2Fdjango-virtual-models/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vintasoftware%2Fdjango-virtual-models/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vintasoftware","download_url":"https://codeload.github.com/vintasoftware/django-virtual-models/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247675596,"owners_count":20977376,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["django","django-rest-framework","orm","performance","python"],"created_at":"2024-07-31T22:01:31.412Z","updated_at":"2025-04-07T15:06:05.738Z","avatar_url":"https://github.com/vintasoftware.png","language":"Python","readme":"\u003cp align=\"center\" style=\"margin: 0 0 10px\"\u003e\n  \u003cimg width=\"100\" src=\"https://user-images.githubusercontent.com/397989/193950778-4550eebb-8650-45f8-8f57-d3bbab15e91a.svg\" alt=\"Django Virtual Models Icon\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eDjango Virtual Models\u003c/strong\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n    \u003cem\u003eImprove performance and maintainability with a prefetching layer in your Django / Django REST Framework project\u003c/em\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://github.com/vintasoftware/django-virtual-models/actions?query=workflow%3ATest+event%3Apush+branch%3Amain\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://github.com/vintasoftware/django-virtual-models/workflows/tests/badge.svg?event=push\u0026branch=main\" alt=\"Test\"\u003e\n\u003c/a\u003e\u0026nbsp;\n\u003ca href=\"https://coveralls.io/github/vintasoftware/django-virtual-models?branch=main\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://coveralls.io/repos/github/vintasoftware/django-virtual-models/badge.svg?branch=main\" alt=\"Coverage Status\" /\u003e\n\u003c/a\u003e\u0026nbsp;\n\u003ca href=\"https://pypi.org/project/django-virtual-models\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/pypi/v/django-virtual-models?color=%2334D058\u0026label=pypi%20package\" alt=\"Package version\"\u003e\n\u003c/a\u003e\u0026nbsp;\n\u003ca href=\"https://pypi.org/project/django-virtual-models\" target=\"_blank\"\u003e\n    \u003cimg src=\"https://img.shields.io/pypi/pyversions/django-virtual-models.svg?color=%2334D058\" alt=\"Supported Python versions\"\u003e\n\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n**Documentation**: \u003ca href=\"https://vintasoftware.github.io/django-virtual-models/\" target=\"_blank\"\u003ehttps://vintasoftware.github.io/django-virtual-models/\u003c/a\u003e\n\n**Example project**: \u003ca href=\"https://github.com/vintasoftware/django-virtual-models/tree/main/example\" target=\"_blank\"\u003ehttps://github.com/vintasoftware/django-virtual-models/tree/main/example\u003c/a\u003e\n\n**Source Code**: \u003ca href=\"https://github.com/vintasoftware/django-virtual-models\" target=\"_blank\"\u003ehttps://github.com/vintasoftware/django-virtual-models\u003c/a\u003e\n\n---\n\nDjango 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.\n\nWhen implementing Django REST Framework serializers, developers need to be careful to avoid causing the [*N+1 selects problem*](https://stackoverflow.com/questions/97197/what-is-the-n1-selects-problem-in-orm-object-relational-mapping) due to missing `prefetch_related` or `select_related` calls on the associated queryset. Additionaly, developers must not miss `annotate` calls for fields that are computed at queryset-level.\n\nWith 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.\n\nFor example, imagine if you have following nested serializers starting from `MovieSerializer`:\n\n```python\nfrom movies.models import Nomination, Person, Movie\n\nclass AwardSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = Nomination\n        fields = [\"award\", \"category\", \"year\", \"is_winner\"]\n\nclass PersonSerializer(serializers.ModelSerializer):\n    awards = AwardSerializer(many=True)\n    nomination_count = serializers.IntegerField(read_only=True)\n\n    class Meta:\n        model = Person\n        fields = [\"name\", \"awards\", \"nomination_count\"]\n\nclass MovieSerializer(serializers.ModelSerializer):\n    directors = PersonSerializer(many=True)\n\n    class Meta:\n        model = Movie\n        fields = [\"name\", \"directors\"]\n\n```\n\nFor good performance and correct functionality, all nested serializers must have a corresponding `prefetch_related` on the queryset used by `MovieSerializer`. Also, the `nomination_count` field should be `annotate`d on it. Therefore, you'll need to write this complex chain of nested prefetches:\n\n```python\nfrom django.db.models import Prefetch\n\nawards_qs = Nomination.objects.filter(is_winner=True)\n\ndirectors_qs = Person.objects.prefetch_related(\n    Prefetch(\n        \"nominations\",\n        queryset=awards_qs,\n        to_attr=\"awards\"\n    )\n).annotate(\n    nomination_count=Count(\"nominations\")\n).distinct()\n\nqs = Movie.objects.prefetch_related(\n    Prefetch(\n        \"directors\",\n        queryset=directors_qs\n    )\n)\n```\n\nConversely, you can declare Virtual Models for this read logic to easily reuse and customize those classes in multiple places of the codebase:\n\n```python\nimport django_virtual_models as v\n\nclass VirtualAward(v.VirtualModel):\n    class Meta:\n        model = Nomination\n\n    def get_prefetch_queryset(self, **kwargs):\n        return Nomination.objects.filter(is_winner=True)\n\n\nclass VirtualPerson(v.VirtualModel):\n    awards = VirtualAward(lookup=\"nominations\")\n    nomination_count = v.Annotation(\n        lambda qs, **kwargs: qs.annotate(\n            nomination_count=Count(\"nominations\")\n        ).distinct()\n    )\n\n    class Meta:\n        model = Person\n\n\nclass VirtualMovie(v.VirtualModel):\n    directors = VirtualPerson()\n\n    class Meta:\n        model = Movie\n```\n\nTo configure your DRF view and serializer to use Virtual Models, inherit from the proper classes:\n\n```python\nimport django_virtual_models as v\n\nclass MovieSerializer(v.VirtualModelSerializer):\n    ...\n\n    class Meta:\n        ...\n        virtual_model = VirtualMovie\n\nclass MovieList(v.VirtualModelListAPIView):\n    queryset = Movie.objects.all()\n    serializer_class = MovieSerializer\n    ...\n```\n\n**Then the library will automatically do the right prefetches and annotations for you!**\n\nIf, for example, you forget to add the `nomination_count` field on `VirtualPerson`, the following exception will appear when using `MovieSerializer`:\n\n![MissingVirtualModelFieldException exception](https://user-images.githubusercontent.com/397989/193944879-5205d80b-4102-415e-b178-7630a14db5a1.png)\n\nIf you aren't using DRF serializers, you hydrate your queryset with *virtual fields* manually:\n\n```python\nqs = VirtualMovie().get_optimized_queryset(\n    Movie.objects.all(),\n    lookup_list=[\n        \"directors__awards\",\n        \"directors__nomination_count\",\n    ]\n)\n```\n\nTo learn more, check the [Installation](https://vintasoftware.github.io/django-virtual-models/installation/) and the [Tutorial](https://vintasoftware.github.io/django-virtual-models/tutorial/).\nOr the [example project](https://github.com/vintasoftware/django-virtual-models/tree/main/example).\n","funding_links":[],"categories":["Python","Database"],"sub_categories":["Tools"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvintasoftware%2Fdjango-virtual-models","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvintasoftware%2Fdjango-virtual-models","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvintasoftware%2Fdjango-virtual-models/lists"}