This is a Python 3 re-write for https://djangosnippets.org/snippets/2117/
1 2 3 4 5 6 7 8 9101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 | fromdjango.db.models.managerimportManagerfromdjango.db.models.queryimportQuerySetdefmanager_from(*mixins_and_funcs,**kwargs):""" Returns a Manager instance with extra methods, also available and chainable on generated querysets. (param) mixins_and_funcs: Each mixin can be either a class or a function. The generated manager and associated queryset subclasses extend the mixin classes and include the mixin functions (as methods). (kwarg) queryset_cls: The base queryset class to extend from (`django.db.models.query.QuerySet` by default). (kwarg) manager_cls: The base manager class to extend from (`django.db.models.manager.Manager` by default). """base_queryset=kwargs.get("queryset_cls",QuerySet)manager_class=kwargs.get("manager_cls",Manager)# Collect the mixin classes and methods into separate variables.bases=[base_queryset]methods={}formixin_or_funcinmixins_and_funcs:# If the mixin is a class (all classes in Python 3+ inherit from the base `type` class).ifisinstance(mixin_or_func,type):# Add it to our bases list.bases.append(mixin_or_func)# If it is not a class, is it a function?else:try:methods.update({mixin_or_func.__name__,mixin_or_func})exceptAttributeError:# If you pass in a variable of a bool data type, for example, it will raise an attribute error! The __name__ property is only available on classes and methods (?).raiseTypeError(f"Mixin must be class or function, not{mixin_or_func.__class__}")kwargs_as_tuple=tuple(iter(kwargs.items()))args_id=hash(mixins_and_funcs+kwargs_as_tuple)# Create the QuerySet subclass: name it deterministically (same set of arguments returns the same queryset class name), add base classes and class methods.new_queryset_class=type(f"Queryset_{args_id}",tuple(bases),methods)# Create the Manager subclass.bases[0]=manager_classnew_manager_class=type(f"Manager_{args_id}",tuple(bases),methods)# And finally, override new manager's get_query_set.super_get_queryset=manager_class.get_querysetdefget_queryset(self):# First honor the super manager's get_query_setqs=super_get_queryset(self)# And then try to bless the returned queryset by reassigning it to the newly created Queryset class, though this may not be feasible.ifnotissubclass(new_queryset_class,qs.__class__):raiseTypeError("QuerySet subclass conflict: cannot determine a unique class for queryset instance")qs.__class__=new_queryset_classreturnqsnew_manager_class.get_queryset=get_querysetreturnnew_manager_class() |
Pleaselogin first before commenting.