Ответ 1
Похоже, этот фрагмент обеспечивает решение вашей ситуации: Пользовательские менеджеры с цельнофильтрованными фильтрами.
Я создал пользовательский менеджер, который должен рандомизировать мой запрос:
class RandomManager(models.Manager):
def randomize(self):
count = self.aggregate(count=Count('id'))['count']
random_index = random.randint(0, count - 1)
return self.all()[random_index]
Когда я использую метод, определенный в моем менеджере, в первую очередь, он работает нормально:
>>> PostPages.random_objects.randomize()
>>> <PostPages: post 3>
Мне нужно рандомизировать уже отфильтрованный запрос. Когда я попытался использовать менеджер и метод в цепочке, я получил сообщение об ошибке:
PostPages.random_objects.filter(image_gallary__isnull=False).randomize()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/home/i159/workspace/shivaroot/shivablog/<ipython-input-9-98f654c77896> in <module>()
----> 1 PostPages.random_objects.filter(image_gallary__isnull=False).randomize()
AttributeError: 'QuerySet' object has no attribute 'randomize'
Результат фильтрации не является экземпляром класса модели, но он django.db.models.query.QuerySet
, так что у него нет моего менеджера и метода соответственно.
Можно ли использовать пользовательский менеджер в цепочном запросе?
Похоже, этот фрагмент обеспечивает решение вашей ситуации: Пользовательские менеджеры с цельнофильтрованными фильтрами.
Вот как вы настраиваете пользовательские методы в пользовательском менеджере, то есть: Post.objects.by_author (user = request.user).published()
from django.db.models.query import QuerySet
class PostMixin(object):
def by_author(self, user):
return self.filter(user=user)
def published(self):
return self.filter(published__lte=datetime.now())
class PostQuerySet(QuerySet, PostMixin):
pass
class PostManager(models.Manager, PostMixin):
def get_query_set(self):
return PostQuerySet(self.model, using=self._db)
Ссылка здесь: django-custom-model-manager-chaining
В Django 1.7 у вас есть это из коробки. Проверьте QuerySet.as_manager
Просто пример кода, использующий новый метод as_manager() (см. информацию об обновлении от @zzart.
class MyQuerySet(models.query.QuerySet):
def randomize(self):
count = self.aggregate(count=Count('id'))['count']
random_index = random.randint(0, count - 1)
return self.all()[random_index]
class MyModel(models.Model):
.....
.....
objects = MyQuerySet.as_manager()
.....
.....
И тогда вы сможете использовать что-то вроде этого в своем коде:
MyModel.objects.filter(age__gt=16).randomize()
Как вы можете видеть, новый as_manager() действительно опрятен:)
Как о чем-то вроде ниже, который динамически создает пользовательский QuerySet и позволяет нам "пересаживать" наши пользовательские запросы на возвращаемый экземпляр QuerySet:
class OfferManager(models.Manager):
"""
Additional methods / constants to Offer objects manager
"""
### Model (db table) wide constants - we put these and
### not in model definition to avoid circular imports.
### One can access these constants through like
<foo>.objects.STATUS_DISABLED or ImageManager.STATUS_DISABLED
STATUS_DISABLED = 0
...
STATUS_CHOICES = (
(STATUS_DISABLED, "Disabled"),
(STATUS_ENABLED, "Enabled"),
(STATUS_NEGOTIATED, "Negotiated"),
(STATUS_ARCHIVED, "Archived"),
)
...
# we keep status and filters naming a little different as
# it is not one-to-one mapping in all situations
QUERYSET_PUBLIC_KWARGS = {'status__gte': STATUS_ENABLED}
QUERYSET_ACTIVE_KWARGS = {'status': STATUS_ENABLED}
def get_query_set(self):
""" our customized method which transpalats manager methods
as per get_query_set.<method_name> = <method> definitions """
CustomizedQuerySet = QuerySet
for name, function in self.get_query_set.__dict__.items():
setattr(CustomizedQuerySet, name, function)
return CustomizedQuerySet(self.model, using=self._db)
def public(self):
""" Returns all entries accessible through front end site"""
return self.all().filter(**OfferManager.QUERYSET_PUBLIC_KWARGS)
get_query_set.public = public # will tranplat the function onto the
# returned QuerySet instance which
# means 'self' changes depending on context.
def active(self):
""" returns offers that are open to negotiation """
return self.public().filter(**OfferManager.QUERYSET_ACTIVE_KWARGS)
get_query_set.active = active
...
Более полированная версия этого метода и билет django здесь: https://code.djangoproject.com/ticket/20625.
Учитывая, что у вас есть существующий models.Manager
, и вы не хотите раскрывать какой-либо из методов менеджера цепочке запросов, вы можете использовать Manager.from_queryset(QuerySet)()
.
Итак, вы все равно можете поместить весь свой метод запроса на цепочку внутри метода QuerySet и вашего менеджера независимо.
Пример, приведенный на официальном сайте.
Фрагмент из Django Docs
class BaseManager(models.Manager):
# Available only on Manager.
def manager_only_method(self):
return
class CustomQuerySet(models.QuerySet):
# Available on both Manager and QuerySet.
def manager_and_queryset_method(self):
return
# Available only on QuerySet.
def _private_method(self):
return
CustomManager = BaseManager.from_queryset(CustomQuerySet)
class MyModel(models.Model):
objects = CustomManager()