首页 > 解决方案 > ManyToMany 字段默认选择所有模型实例/更改默认 get_queryset

问题描述

我改变了我的.all方法,所以它只会选择实例published=True

class EventManager(models.Manager):
    def all(self, *args, **kwargs):
        return super().get_queryset().filter(published=True, *args, **kwargs)

这与问题模型字段有关:

class Event(models.Model):
    related_events = models.ManyToManyField('self', blank=True, related_name='related')
    published = models.BooleanField(default=False)
    objects = EventManager()

结果ManyToManyField最终选择了所有Event实例。为了保存published功能并能够手动添加相关事件,您建议我做什么?谢谢你。

标签: pythondjangodjango-models

解决方案


据我所知,Django 不使用Model.objects作为管理器,而是使用Model._basemanager,它通常应该返回所有对象。

您可以在此处使用limit_choices_to[Django-doc]来限制多对多字段的选择,例如:

from django.db.models import Q

class Event(models.Model):
    related_events = models.ManyToManyField(
        'self',
        limit_choices_to=Q(published=True)
        blank=False,
        related_name='related'
    )
    published = models.BooleanField(default=False)
    objects = EventManager()

您可能还想删除blank=True,因为这意味着默认情况下,您使该字段不在表单中显示 op。所以如果要手动编辑相关事件的话。blank=False.

此外,ManyToManyFieldto'self'默认情况下是symmatrical。因此,这意味着如果event1related_eventsof 中event2,那么event2也在related_eventsofevent1中。如果您不希望这样,您可能需要添加symmetrical=False[Django-doc]

请注意,在某些情况下,未发布的事件可能最终会出现在相关事件中。例如,通过将已发布事件添加到相关事件中,然后“取消发布”它。

至于经理,我认为你最好修补get_queryset方法:

class EventManager(models.Manager):

    def get_queryset(self):
        return super().get_queryset().filter(published=True)

否则有很多方法可以“规避filtering。例如:Event.objects.filter(id__gt=-1)仍然会给我all Event s,但由于我没有调用.all(),因此不会在发布时过滤。

在 中ModelAdmin,您可以通过以下方式为此指定查询集ManyToManyField

class EventAdmin(admin.ModelAdmin):

    def get_field_queryset(self, db, db_field, request):
        if db_field.name == 'event_dates':
            return db_field.remote_field.model.base_manager.all()
        else:
            super(EventAdmin, self).get_field_queryset(db, db_field, request)

推荐阅读