【问题标题】:Is it possible to override filter lookup with predefined values with custom Manager/Queryset in Django是否可以使用 Django 中的自定义管理器/查询集使用预定义值覆盖过滤器查找
【发布时间】:2021-04-18 23:30:06
【问题描述】:

我正在尝试为我的模型实现该行为,以便在您删除对象时它不会物理删除,而只是添加一些属性以表明它已被删除。

所以我创建了自定义查询集、管理器和 mixin 来应用到每个创建的模型:

class StateQuerySet(models.query.QuerySet):
    def delete(self):
        self.update(active=False)

class StateManager(models.Manager):
    def get_queryset(self):
        return StateQuerySet(self.model, using=self._db).filter(active=True)

class ModelMixin(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    active = models.BooleanField(default=True)

    objects = StateManager()

    def delete(self, *args, **kwargs):
        self.active = False
        self.save()

    class Meta:
        abstract = True

还有模特:

class Organizer(ModelMixin, models.Model):
    name = models.CharField(max_length=256)

class EventData(ModelMixin, models.Model):
    caption = models.CharField(max_length=512)
    description = models.TextField(blank=True, default='')
    organizer = models.ForeignKey(Organizer, on_delete=models.CASCADE)

所以,这个想法是当我做任何事情时:

Organizer.objects.all() / Organizer.objects.filter(name__startswith='<some_start_prefix>')

我只会收到 active=True(即“未删除”)的 Organizer 对象。 一切看起来都不错,但是当我有相关对象时,就会出现问题。 例如,如果我这样做:

EventData.objects.filter(organizer__name__startswith='<some_start_prefix>')

它将返回所有 EventData 对象,即使是“已删除”。 但如果我这样做:

EventData.objects.filter(organizer__name__startswith='<some_start_prefix>', organizer__active=True)

所有工作都按预期进行,仅返回“活动”记录。所以我不想使用:

organizer__active=True

对于视图中的每个查询。

我已阅读文档,但仍然不明白如何使用自定义 Manager 和 QuerySet 创建它。您能否提供帮助或指导如何执行此操作?我错过了什么?

总结一下:当我这样做时:

EventData.objects.filter(organizer__name__startswith='<some_start_prefix>')

我想收到 Organizer.active 设置为 True 的所有 EventData 对象,但无需每次在客户端代码中编写额外的 Organizer__active=True。是否可以在Manager或QuerySet中进行?

【问题讨论】:

  • 您能否澄清一下,在您的过滤器中包含organizer__active=True 会为您提供您想要的结果,但您不想在查询中使用它?
  • @markwalker_ 不,我想要,我只是想要它在某个通用的地方。我不想将它添加到我每次在视图中使用它的所有地方(例如)。据我所知,可以通过自定义管理器和查询集来做到这一点,但我不明白如何。正如您在 get_queryset 中看到的一样: StateQuerySet(self.model, using=self._db).filter(active=True) 因此,对于在模型上完成的每个 get/filter,它只会返回“活动”。但它不适用于相关对象。

标签: python django django-models django-queryset django-managers


【解决方案1】:

好的,所以您确实可以向模型的查询集添加一些自定义查询。

你可能会做类似的事情;

def filter_active_organizer(self, prefix):
    return self.filter(organizer__name__startswith=prefix, organizer__active=True)

让我用我已有的项目中的一些东西来说明一下;

from django.db.models import Q, QuerySet
from django.utils import timezone


class EventQuerySet(QuerySet):
    """
    Custom queryset/manager for the Event model...adds common filtering
    operations
    """
    def filter_open(self):
        """
        Filter events in this queryset to only include those whose entry
        process is currently open.
        """
        now = timezone.now()

        return self.filter(
            Q(entry_open__isnull=True) | Q(entry_open__lte=now),
            Q(entry_close__isnull=True) | Q(entry_close__gt=now)
        )

    def filter_closed(self):
        """
        Filter events in this queryset to only include those whose entry
        process is currently closed.
        """
        now = timezone.now()

        return self.filter(
            (Q(entry_open__isnull=False) & Q(entry_open__gt=now)) |
            (Q(entry_close__isnull=False) & Q(entry_close__lte=now))
        )


EventManager = EventQuerySet.as_manager()

class Event(models.Model):

    class Meta:
        app_label = 'entry'
        verbose_name = _('Event')
        verbose_name_plural = _('Events')

    objects = EventManager

然后可以使用Event.objects.filter_open() 访问这些查询或使用过滤器类添加到管理员;



class FilterStatus(admin.SimpleListFilter):
    """
    List filter to filter events by whether they are open or closed
    (this is a computed value)
    """
    title = 'entry status'
    parameter_name = 'entry_status'

    def lookups(self, request, model_admin):
        """
        Return the lookup choices for this filter
        """
        return [
            ('open', _('Open')),
            ('closed', _('Closed')),
        ]

    def queryset(self, request, queryset):
        """
        Perform the filtering (if required)
        """
        if self.value() == 'open':
            return queryset.filter_open()

        if self.value() == 'closed':
            return queryset.filter_closed()

        return queryset

【讨论】:

  • 感谢您提供解决方案@markwalker_,但这不是我希望看到的。我知道我可以设置一个方法,该方法将按某些条件返回过滤后的查询集,但它不能作为默认方法工作,因此要为相关对象工作,我需要为查询集/模型定义多个方法。 p.s.正如我调查的那样,当时它并没有像我预期的那样工作......
  • 用你写的和你期望的来更新你的问题可能是值得的。
猜你喜欢
  • 2010-10-14
  • 1970-01-01
  • 2011-08-14
  • 2016-09-11
  • 1970-01-01
  • 1970-01-01
  • 2016-06-12
  • 2016-07-28
  • 2015-06-30
相关资源
最近更新 更多