Package: django-polymorphic-queryset
Django Polymorphic QuerySetHelps 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.Installationpip install django-polymorphic-querysetMotivationFor example, your application have two models:Productand derived from itPerishableProduct:fromdjango.dbimportmodelsclassProduct(models.Model):name=models.CharField(max_length=120)classPerishableProduct(Product):is_perished=models.BooleanField(default=False)classBrokenProduct(Product):repaired=models.BooleanField(default=False)If you want to add a view that returnsactiveproducts (products that are not perished and repaired) with
this hierarchy you will probably end with a one function that contains all that conditions:fromdjango.dbimportmodelsclassProductQuerySet(models.QuerySet):defget_active_products(self):returnself.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.fromdjango.dbimportmodelsclassProductQuerySet(models.QuerySet):defget_active_products(self):returnself.all()classPerishableProductQuerySet(ProductQuerySet):defget_active_products(self):returnself.filter(is_perished=False)classBrokenProductQuerySet(ProductQuerySet):defget_active_products(self):returnself.filter(repaired=True)But in this implementationProductQuerySetwill return all products including products that were saved
asPerishableProductwithis_perishedequal toTrueandBrokenProductwithrepairedequal toFalse.SolutionThis 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@classmethoddecorator):frompolymorphic_querysetimportQueryable,queryfromdjango.dbimportmodelsclassProductQuerySet(Queryable,models.QuerySet):model_name="Product"@query()defget_active_products(cls):returnquery.all()classPerishableProductQuerySet(ProductQuerySet):model_name="PerishableProduct"@query()defget_active_products(cls):returnmodels.Q(is_perished=False)classBrokenProductQuerySet(ProductQuerySet):model_name="BrokenProduct"@query()defget_active_products(cls):returnmodels.Q(repaired=True)This implementation separates query conditions between different QuerySets based on a specified model name.ProductQuerySet.get_active_productswill return any product that was saved asProduct,
products that were saved asPerishableProductifis_perishedisFalseandBrokenProductisrepairedisTrue.
QuerySet will still returnProductinstances.You can usedjango-polymorphiclibrary to return instances of derived classes instead.You can now use these querysets as a model manager:fromdjango.dbimportmodelsclassProduct(models.Model):objects=ProductQuerySet.as_manager()classPerishableProduct(Product):objects=PerishableProductQuerySet.as_manager()classBrokenProduct(Product):objects=BrokenProductQuerySet.as_manager()active_products=Product.objects.get_active_products()To use conditions of base classes in your querysets you can callconditionsfunction of your method decorated withquery:frompolymorphic_querysetimportQueryable,queryfromdjango.dbimportmodelsimportdatetimeclassProductQuerySet(Queryable,models.QuerySet):model_name="Product"@query()defget_new_products(cls):returnmodels.Q(date_created__gte=datetime.datetime.now()-datetime.timedelta(weeks=1))classPerishableProductQuerySet(ProductQuerySet):model_name="PerishableProduct"@query()defget_new_products(cls):returnsuper().get_new_products.conditions()&models.Q(date_perished__lt=datetime.datetime.now())In this exampleget_new_productswill return anyProductthat was created less than a week ago orPerishableProductif it was created a week ago and won't perish today.