'''
FlexiModel tidies up adding up methods to models and querysets.
'''
import six
from django.db import models
from django.db.models.base import ModelBase
[docs]class CustomRootManager(models.Manager):
use_for_related_fields = True
[docs] def get_or_none(self, *args, **kwargs):
try:
x = self.get(*args, **kwargs)
except self.model.DoesNotExist:
x = None
return x
[docs]def allow_floating_methods(cls):
"""
Decorator for models that allows functions to be transposed to querysets and managers
can decorate models directly - or make a subclass of FlexiModel.
"""
class CustomQuerySet(models.QuerySet):
pass
"""
Move flagged queryset methods to queryset
"""
for i in cls.__dict__.keys():
method = cls.__dict__[i]
if hasattr(method, "_querysetmethod"):
setattr(CustomQuerySet, i, method)
class CustomManager(CustomRootManager):
def get_queryset(self):
return CustomQuerySet(self.model, using=self._db)
"""
Move flagged manager methods to manager
"""
for i in cls.__dict__.keys():
method = cls.__dict__[i]
if hasattr(method, "_managermethod"):
setattr(CustomManager, i, method)
cls.add_to_class('objects', CustomManager())
try:
cls._default_manager = cls.objects
except AttributeError:
pass # django 1.11 doesn't need this.
return cls
[docs]class FlexiModel(six.with_metaclass(ApplyManagerMethodMeta, models.Model)):
"""
Class for models to inherit to receive correct metaclass that allows the floating decorators
use instead of models.Model
"""
[docs]def querysetmethod(func):
"""
Decorator for a model method to make it apply to the queryset.
Will be accessible as model.objects.all().foo()
"self" will then be the query object.
"""
func._querysetmethod = True
return func
[docs]def managermethod(func):
"""
Decorator for a model method to make it a manager method instead.
will be accessible as model.objects.foo()
"self" will then be the manager object.
self.model - can then be used to access model.
self.get_queryset() - to get access to a query
"""
func._managermethod = True
return func