Как использовать настраиваемый менеджер со связанными объектами?
У меня есть пользовательский менеджер. Я хочу использовать его для связанных объектов. Я нашел 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()
Ответы
Ответ 1
Настройка use_for_related_fields
на True
в менеджере сделает его доступным для всех отношений, которые указывают на модель, на которой вы определили этого менеджера как менеджера по умолчанию. Это описано здесь
class MyManager(models.Manager):
use_for_related_fields = True
# ...
Я предполагаю, что он включен только в вашей модели PostPages
, а не на вашей модели Gallery
(или какой бы то ни была модель, на которую ссылается через post_image_gallery
). Если вы хотите иметь дополнительную функциональность для этого менеджера realtion, вам нужно добавить настраиваемый менеджер по умолчанию с use_for_related_fields = True
к вашей модели Gallery
!
Ответ 2
Для полноты темы Django 1.7 (наконец) поддерживает с помощью настраиваемого реверсивного менеджера, поэтому вы можете сделать что-то подобное (просто копируя из django docs)
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()
Ответ 3
В 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'
Ответ 4
Кроме того, в пользовательском диспетчере обязательно получите доступ к запросу через self.get_query_set()
proxy при реализации настраиваемых фильтров, которые должны быть вызваны из связанного менеджера:
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()