Как использовать пользовательский менеджер со связанными объектами?

У меня есть собственный менеджер. Я хочу использовать его для связанных объектов. Я нашел use_for_related_fields в документах. Но это не работает так, как я его использовал:

class RandomQueryset(models.query.QuerySet):

    def randomize(self):       
        count = self.count()
        random_index = random.randint(0, count - 1)
        return self.all()[random_index]


class RandomManager(models.Manager):

    use_for_related_fields = True

    def get_query_set(self):
        return RandomQueryset(self.model, using=self._db)

    def randomize(self):
        return self.get_query_set().randomize()

Я использовал его для одной модели:

>>> post = PostPages.default_manager.filter(image_gallery__isnull=False).distinct().randomize()

И попытался сделать то же самое с объектом, связанным с m2m:

>>> post.image_gallery.randomize()

Получил ошибку:

AttributeError: 'ManyRelatedManager' object has no attribute 'randomize'

Можно ли использовать пользовательский менеджер так, как это сделал я? Если да, то как заставить его работать?

Редактировать

Мои модели:

class ShivaImage(models.Model, ImageResizing):
    image = models.ImageField(upload_to='img')
    slide_show = models.BooleanField() 
    title = models.CharField(max_length=100)
    text = models.TextField(max_length=400)
    ordering = models.IntegerField(blank=True, null=True)

    objects = RandomManager()


class PostPages(models.Model):
    image_gallery = models.ManyToManyField(ShivaImage, blank=True,
                                       related_name='gallery',)
    # all the other fields... 

    objects = RandomManager()

person I159    schedule 20.09.2011    source источник


Ответы (4)


Установка use_for_related_fields на True для менеджера сделает его доступным для всех отношений, которые указывают на модель, в которой вы определили этот менеджер как менеджер по умолчанию. Это задокументировано здесь

class MyManager(models.Manager):
    use_for_related_fields = True
    # ...

Я предполагаю, что он включен только в вашей модели PostPages, а не в вашей модели Gallery (или как там называется модель, на которую ссылается post_image_gallery). Если вы хотите иметь дополнительные функциональные возможности в этом менеджере недвижимости, вам нужно добавить собственный менеджер по умолчанию с use_for_related_fields = True в вашу модель Gallery!

person Bernhard Vallant    schedule 20.09.2011
comment
Я определил менеджера в обеих моделях. Если я правильно понял, objects означает менеджер по умолчанию. См. правки вопроса, я добавил туда модели. - person I159; 21.09.2011
comment
Определенный первый менеджер становится менеджером по умолчанию, независимо от того, называется он objects или нет... - person Bernhard Vallant; 21.09.2011
comment
Уууууууу! Это работает! Скорее всего, произошла какая-то очень глупая ошибка. Потому что теперь все в порядке. - person I159; 21.09.2011
comment
Предупреждение, начиная с Django 1.10, эта функция устарела, вместо нее в модели используется установка Meta.base_manager_name. См. примечания к выпуску в docs.djangoproject.com/en/1.10/releases/1.10/. - person migonzalvar; 05.10.2016
comment
Другое предупреждение: в документах также говорится, что менеджеры не используются при запросе связанных объектов. docs.djangoproject.com/en /1.11/topics/db/managers/. Это предотвращает использование этой функции для неявной фильтрации связанных объектов. - person David Guillot; 08.03.2018
comment
@DavidGuillot: Спасибо. Любая идея, как обойти это ограничение запроса связанного объекта? Вы знаете, что используется вместо менеджеров? - person djvg; 10.12.2018

Для полноты темы Django 1.7 (наконец-то) поддерживает используя собственный обратный менеджер, чтобы вы могли сделать что-то подобное (просто скопировав документы django):

from django.db import models

class Entry(models.Model):
    objects = models.Manager()  # Default Manager
    entries = EntryManager()    # Custom Manager

b = Blog.objects.get(id=1)
b.entry_set(manager='entries').all()
person Serafeim    schedule 24.09.2014

В django 2.0 use_for_related_fields устарело https://docs.djangoproject.com/en/2.0/releases/1.10/#manager-use-for-related-fields-and-inheritance-changes

Вы должны использовать base_manager_name: https://docs.djangoproject.com/en/2.0/ref/models/options/#django.db.models.Options.base_manager_name

Обновленные документы: https://docs.djangoproject.com/en/2.0/topics/db/managers/#using-managers-for-related-object-access

class MyModel(models.Model):
    field1 = ...
    field2 = ...
    special_manager = MyManager()

    class Meta:
        base_manager_name = 'special_manager'
person Sardorbek Imomaliev    schedule 06.01.2018
comment
base_manager_name ожидает имя рассматриваемого менеджера в виде строки, а не экземпляра. См. это. - person Nobilis; 28.02.2018
comment
для меня установка base_manager_name была даже необходима при переопределении атрибута objects по умолчанию в моей модели (раньше я думал, что это понадобится только в том случае, если имя менеджера отличается от objects по умолчанию)! - person Henhuy; 09.06.2020

Кроме того, в пользовательском менеджере обязательно получите доступ к набору запросов через прокси-метод self.get_query_set() при реализации пользовательских фильтров, которые будут вызываться из связанного менеджера:

class EventManager(models.Manager):

    use_for_related_fields = True

    def visible_events(self):
        today = datetime.date.today()
        # don't do this !!! 
        # unsuitable for related managers as could retrieve extraneous objects
        # qs = super(EventManager, self).get_query_set()
        # Use queryset proxy method as follows, instead:
        qs = self.get_query_set()
        qs = qs.filter(visible_from__lte=today, visible_to__gte=today)
        return qs


class Event(models.Model):

    visible_from = models.DateField(_(u'visible from'), null=False, blank=False)
    visible_to = models.DateField(_(u'visible to'), null=False, blank=False)
    concepts = models.ManyToManyField(Concept, through='ConceptEventRegistration')

    objects = EventManager()

Пример использования:

my_concept = Concept.objects.get(id=1)
# retrieve all events related to the object
my_concept.event_set.all()
# retrieve all visible events related to the object
my_concept.event_set.visible_events()
person Mario Orlandi    schedule 18.01.2015