如何使用自定义管理器与相关对象?

51

我有一个自定义manager,想要在相关对象中使用它。我在文档中找到了 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()
4个回答

49

为了完整地讨论这个主题,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()

29

这只适用于Django 1.09或更早版本 - 文档证明

在该管理器上设置use_for_related_fieldsTrue 将使其在所有指向该模型的关系中可用,该模型是您将此管理器定义为默认管理器的模型。有关详细信息,请参见此处记录的文档。

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

我猜你只在 PostPages 模型上启用了它,而没有在 Gallery 模型(或者通过 post_image_gallery 引用的任何模型)上启用它。如果你想在这个关联管理器上有额外的功能,你需要在你的 Gallery 模型上添加一个带有 use_for_related_fields = True 的自定义默认管理器!


我在两个模型中定义了管理器。如果我理解正确的话,objects 是指默认的管理器。请查看问题编辑,我在那里添加了模型。 - I159
3
无论objects是否被称为名称,第一个定义的管理器将成为默认管理器。 - Bernhard Vallant
哦呼!它能工作了!很可能是因为有一些非常愚蠢的错误。因为现在没问题了。 - I159
30
警告:自Django 1.10版本起,该功能已被弃用,建议在模型上设置Meta.base_manager_name。请参阅https://docs.djangoproject.com/en/1.10/releases/1.10/#manager-use-for-related-fields-and-inheritance-changes中的发布说明。 - migonzalvar
8
另外需要注意的是,文档还指出在查询相关对象时不使用管理器。这会防止使用该特性隐式地过滤相关对象。 - David Guillot
@DavidGuillot:谢谢。有什么办法可以避开与相关对象查询的限制吗?你知道除了管理器之外还用了什么吗? - djvg

20
在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_namehttps://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
base_manager_name 期望一个字符串作为所需经理的名称,而不是实例。请参见此链接 - Nobilis
1
对我而言,当覆盖我的模型中的默认对象属性“objects”时,设置“base_manager_name”甚至是必要的(之前,我认为只有在管理器名称与默认的“objects”不同时才需要这样做)! - Henhuy

1
此外,在自定义管理器中,确保通过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()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接