【问题标题】:How to exclude results with get_object_or_404?如何使用 get_object_or_404 排除结果?
【发布时间】:2011-03-04 01:12:25
【问题描述】:

在 Django 中,您可以使用 exclude 创建类似于 not equal 的 SQL。一个例子可能是。

Model.objects.exclude(status='deleted')

现在这很好用,并且 exclude 非常灵活。由于我有点懒,我想在使用get_object_or_404 时获得该功能,但我还没有找到方法来做到这一点,因为您不能在get_object_or_404 上使用排除。

我想要做这样的事情:

model = get_object_or_404(pk=id, status__exclude='deleted')

但不幸的是,这不起作用,因为没有排除查询过滤器或类似的。到目前为止,我想出的最好的方法是做这样的事情:

object = get_object_or_404(pk=id)
if object.status == 'deleted':
    return HttpResponseNotfound('text')

这样做确实违背了使用get_object_or_404 的意义,因为它不再是一个方便的单线。

或者我可以这样做:

object = get_object_or_404(pk=id, status__in=['list', 'of', 'items'])

但这不太容易维护,因为我需要更新列表。

我想知道我是否缺少 django 中的一些技巧或功能来使用 get_object_or_404 来获得所需的结果?

【问题讨论】:

  • 这不是一个真正的答案,但似乎您实际上并不想为软删除的对象设置 404。软删除的重点是保持对象“以防万一”。

标签: python django orm


【解决方案1】:

使用django.db.models.Q:

from django.db.models import Q

model = get_object_or_404(MyModel, ~Q(status='deleted'), pk=id)

除了 AND 之外,Q 对象还允许您做 NOT(使用 ~ 运算符)和 OR(使用 | 运算符)。

注意 Q 对象必须在 pk=id 之前,因为关键字参数在 Python 中必须在最后。

【讨论】:

  • 当然是 Q 对象。不知道它会在那场比赛中发挥作用。谢谢。
  • 除了模型之外,您还可以将 Manager 或 QuerySet 作为第一个参数传递。见stackoverflow.com/a/26476339/336694
【解决方案2】:

最常见的用例是传递模型。但是,您也可以传递一个 QuerySet 实例:

queryset = Model.objects.exclude(status='deleted')
get_object_or_404(queryset, pk=1)

Django 文档示例: https://docs.djangoproject.com/en/1.10/topics/http/shortcuts/#id2

【讨论】:

  • 我认为这是一个比接受的答案更容易遵循的解决方案。我的意思是,如果我正在查看 6 个月前的代码,如果我使用 Q 对象而不是使用排除进行过滤,我会想打自己的脸。
  • 我同意。 Q 应该只用于复杂的查询。您的解决方案对于初级 Django 开发人员来说更具可读性。
  • 最佳解决方案恕我直言
【解决方案3】:

还有另一种方法可以代替使用 Q 对象。不要将模型传递给get_object_or_404,而是将 QuerySet 传递给函数:

model = get_object_or_404(MyModel.objects.filter(pk=id).exclude(status='deleted'))

但是,这样做的一个副作用是,如果 QuerySet 返回多个结果,它将引发 MultipleObjectsReturned 异常。

【讨论】:

    【解决方案4】:

    get_object_or_404 使用对象管理器的get_queryset 方法。如果您覆盖 get_queryset 方法以仅返回未“删除”的项目,则 get_object_or_404 将自动按照您的意愿行事。但是,像这样覆盖 get_queryset 可能会在其他地方出现问题(可能在管理页面中),但您可以添加备用管理器,以便在需要访问软删除的项目时。

    from django.db import models
    
    class ModelManger(models.Manger):
        def get_queryset(self):
            return super(ModelManger, self).get_queryset().exclude(status='deleted')
    
    class Model(models.Model):
        # ... model properties here ...
    
        objects = ModelManager()
        all_objects = models.Manager()
    

    因此,如果您只需要未删除的项目,您可以使用get_object_or_404(Models, id=id),但如果您需要所有项目,您可以使用get_object_or_404(Models.all_objects, id=id)

    【讨论】:

      猜你喜欢
      • 2018-09-07
      • 1970-01-01
      • 2018-06-30
      • 1970-01-01
      • 1970-01-01
      • 2020-10-08
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多