FlexQuery
¶
The FlexQuery
class provides a decorator for functions declared on a custom
Manager
or QuerySet
:
from django_flexquery import FlexQuery, Manager, Q, QuerySet
# It's crucial to inherit from the QuerySet class of django_flexquery, because
# the FlexQuery's wouldn't make it over to a Manager derived using as_manager()
# with the stock Django implementation. That's the only difference however.
class FruitQuerySet(QuerySet):
# Declare a function that generates a Q object.
# base is a copy of the base QuerySet instance. It's not needed most of
# the time unless you want to embed a sub-query based on the current QuerySet
# into the Q object.
@FlexQuery.from_func
def large(base):
return Q(size__gte=10)
FruitQuerySet.large
now is a sub-type of FlexQuery
encapsulating the custom
function.
You can then derive a Manager
from FruitQuerySet
in two ways, using the
known Django API:
# Either use from_queryset() of the Manager class provided with this module.
class FruitManager(Manager.from_queryset(FruitQuerySet)):
...
# Or, if you don't want to add additional manager-only methods, create a Manager
# instance inside your model definition straight away.
class Fruit(Model):
objects = FruitQuerySet.as_manager()
...
When we assume such a Manager
being the default manager of a Fruit
model
with a size
field, we can now perform the following queries:
Fruit.objects.large()
Fruit.objects.filter(Fruit.objects.large.as_q())
Internally, this is made possible by some metaclass and descriptor magic instantiating
the FlexQuery
type whenever it is accessed as class attribute of a Manager
or QuerySet
object. The resulting FlexQuery
instance will be tied to its
owner and use that for all its filtering.
A FlexQuery
instance is directly callable (Fruit.objects.large()
), which just
applies the filters returned by our custom Q function to the base QuerySet
. This
is a well-known usage pattern you might have come across often when writing custom
Django model managers or querysets.
However, FlexQuery
also comes with an as_q()
method, which lets you access the
Q
object directly (Fruit.objects.filter(Fruit.objects.large.as_q())
). The
FlexQuery
can mediate between these two and deliver what you need in your
particular situation.
Conversion Costs¶
Providing a standalone QuerySet
filtered by the Q
from a supplied Q
function is a cheap operation. The Q
object generated by your custom function is
simply applied to the base using QuerySet.filter()
, resulting in a new QuerySet
you may either evaluate straight away or use to create a sub-query.
Why do I Need This?¶
This approach enables you to declare logic for filtering once with the Manager
or QuerySet
of the model it belongs to. When combined with the prefix() method
of the extended Q object implementation, related models can then simply fetch the
generated Q object and prefix it with the related field’s name in order to reuse it
in their own filtering code, without needing sub-queries. Think of something like:
class TreeQuerySet(QuerySet):
@FlexQuery.from_func
def having_ripe_apples(base):
return Q(kind="apple") & Fruid.objects.large.as_q().prefix("fruits")
API¶
This module provides a convenient way to declare custom filtering logic with Django’s model managers in a reusable fashion using Q objects.
-
class
django_flexquery.flexquery.
FlexQuery
(base)¶ Flexibly provides model-specific query constraints as an attribute of Manager or QuerySet objects.
When a sub-type of FlexQuery is accessed as class attribute of a Manager or QuerySet object, its metaclass, which is implemented as a descriptor, will automatically initialize and return an instance of the FlexQuery type bound to the holding Manager or QuerySet.
-
__call__
(*args, **kwargs)¶ Filters the base queryset using the provided function, relaying arguments.
- Returns QuerySet
-
as_q
(*args, **kwargs)¶ Returns the result of the configured function, relaying arguments.
- Returns Q
-
call_bound
(*args, **kwargs)¶ Calls the provided function with
self.base.all()
as first argument.This may be overwritten if arguments need to be preprocessed in some way before being passed to the custom function.
- Returns Q
-
classmethod
from_func
(func=None, **attrs)¶ Creates a
FlexQuery
sub-type from a function.This classmethod can be used as decorator. As long as
func
isNone
, afunctools.partial
with the given keyword arguments is returned.- Parameters
func (function | None) – function taking a base
QuerySet
and returning aQ
objectattrs – additional attributes to set on the newly created type
- Returns InitializedFlexQueryType | functools.partial
- Raises
TypeError – if
func
is no function
-
-
class
django_flexquery.flexquery.
Manager
¶ Use this class’
from_queryset
method if you want to derive aManager
from aQuerySet
withFlexQuery
members. If Django’s nativeManager.from_queryset
was used instead, those members would be lost.
-
class
django_flexquery.flexquery.
QuerySet
(model=None, query=None, using=None, hints=None)¶ Adds support for deriving a
Manager
from aQuerySet
class viaas_manager
, preservingFlexQuery
members.