Skip to main content

Django Polymorphic QuerySet

Helps to implement filtering logic depended on model type.

You should not confuse filtering and permission checking. If you need to get items based on user, you should probably use Django Permissions.

Installation

pip install django-polymorphic-queryset

Motivation

For example, your application have two models: Product and derived from it PerishableProduct:

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=120)


class PerishableProduct(Product):
    is_perished = models.BooleanField(default=False)


class BrokenProduct(Product):
    repaired = models.BooleanField(default=False)

If you want to add a view that returns active products (products that are not perished and repaired) with this hierarchy you will probably end with a one function that contains all that conditions:

from django.db import models

class ProductQuerySet(models.QuerySet):
    def get_active_products(self):
        return self.filter(
            perishableproduct__is_perished=False,
            brokenproduct__repaired=True
        )

In a big application with a lot of models and conditions this function can become complex and difficult to maintain. Also, with this implementation base queryset will know about details of derived models.

QuerySet filter methods can be separated in a parallel hierarchy, so that derived QuerySets will add a custom logic to base methods or override them:

# Not a real example, just the idea.

from django.db import models

class ProductQuerySet(models.QuerySet):
    def get_active_products(self):
        return self.all()


class PerishableProductQuerySet(ProductQuerySet):
    def get_active_products(self):
        return self.filter(is_perished=False)


class BrokenProductQuerySet(ProductQuerySet):
    def get_active_products(self):
        return self.filter(repaired=True)

But in this implementation ProductQuerySet will return all products including products that were saved as PerishableProduct with is_perished equal to True and BrokenProduct with repaired equal to False.

Solution

This library allows you to create polymorphic querysets that takes into account all conditions in a queryset hierarchy (note that query functions belongs to class, not class instance, works as @classmethod decorator):

from polymorphic_queryset import Queryable, query
from django.db import models

class ProductQuerySet(Queryable, models.QuerySet):
    model_name = "Product"

    @query()
    def get_active_products(cls):
        return query.all()


class PerishableProductQuerySet(ProductQuerySet):
    model_name = "PerishableProduct"

    @query()
    def get_active_products(cls):
        return models.Q(is_perished=False)


class BrokenProductQuerySet(ProductQuerySet):
    model_name = "BrokenProduct"

    @query()
    def get_active_products(cls):
        return models.Q(repaired=True)

This implementation separates query conditions between different QuerySets based on a specified model name. ProductQuerySet.get_active_products will return any product that was saved as Product, products that were saved as PerishableProduct if is_perished is False and BrokenProduct is repaired is True. QuerySet will still return Product instances.

You can use django-polymorphic library to return instances of derived classes instead.

You can now use these querysets as a model manager:

from django.db import models

class Product(models.Model):
    objects = ProductQuerySet.as_manager()


class PerishableProduct(Product):
    objects = PerishableProductQuerySet.as_manager()


class BrokenProduct(Product):
    objects = BrokenProductQuerySet.as_manager()


active_products = Product.objects.get_active_products()

To use conditions of base classes in your querysets you can call conditions function of your method decorated with query:

from polymorphic_queryset import Queryable, query
from django.db import models
import datetime


class ProductQuerySet(Queryable, models.QuerySet):
    model_name = "Product"

    @query()
    def get_new_products(cls):
        return models.Q(date_created__gte=datetime.datetime.now() - datetime.timedelta(weeks=1))


class PerishableProductQuerySet(ProductQuerySet):
    model_name = "PerishableProduct"

    @query()
    def get_new_products(cls):
        return super().get_new_products.conditions() & models.Q(date_perished__lt=datetime.datetime.now())        

In this example get_new_products will return any Product that was created less than a week ago or PerishableProduct if it was created a week ago and won't perish today.

Metadata

Release files for django-polymorphic-queryset 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-polymorphic-queryset 0.2.1
File Size Uploaded
django-polymorphic-queryset-0.2.1.tar.gz 5.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-polymorphic-queryset 0.2.1
File Interpreter ABI Platform
django_polymorphic_queryset-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 12.9 kB

Release files / django-polymorphic-queryset-0.2.1.tar.gz

Download URL django-polymorphic-queryset-0.2.1.tar.gz
Size 5.8 kB
Tags Source
SHA-256 checksum
How to use checksums
dc3075cd8a8309d55c207cba8b887e84c5baf75d11992c7c0d216bfa4a5e8748
BLAKE2b-256 checksum
How to use checksums
6c70adc3f1fdabc2658fb1e73c1cca8a1a13415fc539cf76ce7cf30f565b93ff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/1.11.0 pkginfo/1.4.2 requests/2.19.1 setuptools/40.0.0 requests-toolbelt/0.8.0 tqdm/4.24.0 CPython/3.6.1

Release files / django_polymorphic_queryset-0.2.1-py3-none-any.whl

Download URL django_polymorphic_queryset-0.2.1-py3-none-any.whl
Size 7.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3ba110625737f6b6ab3bea758ba7513d80ba3231523b362d0553e322fe4096b2
BLAKE2b-256 checksum
How to use checksums
05f713d6f236fa38afa61199d5c1d25d8e8cf709f9225aab28ea247c48167a98
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/1.11.0 pkginfo/1.4.2 requests/2.19.1 setuptools/40.0.0 requests-toolbelt/0.8.0 tqdm/4.24.0 CPython/3.6.1

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2

2 release files

0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page