【发布时间】:2011-03-06 15:22:28
【问题描述】:
当我要求模型管理器获取对象时,它会在没有匹配对象时引发DoesNotExist。
go = Content.objects.get(name="baby")
我怎样才能让go 代替None 而不是None?
【问题讨论】:
标签: python django django-queryset
当我要求模型管理器获取对象时,它会在没有匹配对象时引发DoesNotExist。
go = Content.objects.get(name="baby")
我怎样才能让go 代替None 而不是None?
【问题讨论】:
标签: python django django-queryset
没有“内置”方法可以做到这一点。 Django 每次都会引发 DoesNotExist 异常。 在 python 中处理此问题的惯用方法是将其包装在 try catch 中:
try:
go = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
go = None
我所做的是继承models.Manager,像上面的代码一样创建safe_get,并将该管理器用于我的模型。这样你就可以写:SomeModel.objects.safe_get(foo='bar').
【讨论】:
SomeModel.objects.filter(foo='bar').first() 这将返回第一个匹配项,或者无。如果有多个实例,例如queryset.get(),它不会失败
filter().first(),否则我认为例外是要走的路。
从 django 1.6 开始,您可以像这样使用first() 方法:
Content.objects.filter(name="baby").first()
【讨论】:
get()如果未找到给定参数的对象,则会引发DoesNotExist异常。这个异常也是模型类的一个属性。DoesNotExist异常继承自django.core.exceptions.ObjectDoesNotExist
你可以捕获异常并分配None去。
from django.core.exceptions import ObjectDoesNotExist
try:
go = Content.objects.get(name="baby")
except ObjectDoesNotExist:
go = None
【讨论】:
您可以为此创建一个通用函数。
def get_or_none(classmodel, **kwargs):
try:
return classmodel.objects.get(**kwargs)
except classmodel.DoesNotExist:
return None
像下面这样使用:
go = get_or_none(Content,name="baby")
如果没有条目匹配,go 将是 None,否则将返回 Content 条目。
注意:如果为name="baby" 返回多个条目,则会引发异常MultipleObjectsReturned。
您应该在数据模型上处理它以避免此类错误,但您可能更喜欢在运行时记录它,如下所示:
def get_or_none(classmodel, **kwargs):
try:
return classmodel.objects.get(**kwargs)
except classmodel.MultipleObjectsReturned as e:
print('ERR====>', e)
except classmodel.DoesNotExist:
return None
【讨论】:
你可以这样做:
go = Content.objects.filter(name="baby").first()
现在 go 变量可以是你想要的对象,也可以是 None
参考:https://docs.djangoproject.com/en/1.8/ref/models/querysets/#django.db.models.query.QuerySet.first
【讨论】:
为了让事情更简单,这里是我编写的代码的 sn-p,基于此处精彩回复的输入:
class MyManager(models.Manager):
def get_or_none(self, **kwargs):
try:
return self.get(**kwargs)
except ObjectDoesNotExist:
return None
然后在你的模型中:
class MyModel(models.Model):
objects = MyManager()
就是这样。 现在你有了 MyModel.objects.get() 和 MyModel.objetcs.get_or_none()
【讨论】:
您可以将exists 与过滤器一起使用:
Content.objects.filter(name="baby").exists()
#returns False or True depending on if there is anything in the QS
如果您只想知道它是否存在,只是一种选择
【讨论】:
Note: If you only want to determine if at least one result exists (and don’t need the actual objects), it’s more efficient to use exists().
exists() 将在获取对象之前与if 子句一起使用,从而对数据库造成双重打击。我仍然会保留评论以防它对其他人有所帮助。
这是您可能不想重新实现的 annoying functions 之一:
from annoying.functions import get_object_or_None
#...
user = get_object_or_None(Content, name="baby")
【讨论】:
get_object_or_None 的代码,但发现如果有多个对象,它仍然会引发MultipleObjectsReturned。因此,用户可能会考虑使用try-except(函数本身已经有try-except)。
在视图的不同点处理异常可能真的很麻烦..在 models.py 文件中定义自定义模型管理器怎么样,比如
class ContentManager(model.Manager):
def get_nicely(self, **kwargs):
try:
return self.get(kwargs)
except(KeyError, Content.DoesNotExist):
return None
然后将其包含在内容模型类中
class Content(model.Model):
...
objects = ContentManager()
通过这种方式可以很容易地在视图中处理,即
post = Content.objects.get_nicely(pk = 1)
if post:
# Do something
else:
# This post doesn't exist
【讨论】:
return self.get(**kwargs) 让它为我工作。并不是说答案有什么问题,只是给任何试图在更高版本中使用它的人(或其他任何阻止它为我工作的东西)的提示。
也许你使用更好:
User.objects.filter(username=admin_username).exists()
【讨论】:
我觉得用get_object_or_404()也不错
from django.shortcuts import get_object_or_404
def my_view(request):
my_object = get_object_or_404(MyModel, pk=1)
这个例子相当于:
from django.http import Http404
def my_view(request):
try:
my_object = MyModel.objects.get(pk=1)
except MyModel.DoesNotExist:
raise Http404("No MyModel matches the given query.")
您可以在 django 在线文档中阅读有关 get_object_or_404() 的更多信息。
【讨论】:
如果您想要一个不涉及异常处理、条件语句或 Django 1.6+ 要求的简单单行解决方案,请改为:
x = next(iter(SomeModel.objects.filter(foo='bar')), None)
【讨论】:
从 django 1.7 开始,您可以这样做:
class MyQuerySet(models.QuerySet):
def get_or_none(self, **kwargs):
try:
return self.get(**kwargs)
except self.model.DoesNotExist:
return None
class MyBaseModel(models.Model):
objects = MyQuerySet.as_manager()
class MyModel(MyBaseModel):
...
class AnotherMyModel(MyBaseModel):
...
“MyQuerySet.as_manager()”的优点是以下两个都可以工作:
MyModel.objects.filter(...).get_or_none()
MyModel.objects.get_or_none()
【讨论】:
这是 Django 的 get_object_or_404 的复制品,只是该方法返回 None。当我们必须使用only() 查询仅检索某些字段时,这非常有用。此方法可以接受模型或查询集。
from django.shortcuts import _get_queryset
def get_object_or_none(klass, *args, **kwargs):
"""
Use get() to return an object, or return None if object
does not exist.
klass may be a Model, Manager, or QuerySet object. All other passed
arguments and keyword arguments are used in the get() query.
Like with QuerySet.get(), MultipleObjectsReturned is raised if more than
one object is found.
"""
queryset = _get_queryset(klass)
if not hasattr(queryset, 'get'):
klass__name = klass.__name__ if isinstance(klass, type) else klass.__class__.__name__
raise ValueError(
"First argument to get_object_or_none() must be a Model, Manager, "
"or QuerySet, not '%s'." % klass__name
)
try:
return queryset.get(*args, **kwargs)
except queryset.model.DoesNotExist:
return None
【讨论】:
这是帮助函数的一个变体,它允许您有选择地传入 QuerySet 实例,以防您想从模型的 all 对象查询集以外的查询集中获取唯一对象(如果存在)(例如来自属于父实例的子项子集):
def get_unique_or_none(model, queryset=None, **kwargs):
"""
Performs the query on the specified `queryset`
(defaulting to the `all` queryset of the `model`'s default manager)
and returns the unique object matching the given
keyword arguments. Returns `None` if no match is found.
Throws a `model.MultipleObjectsReturned` exception
if more than one match is found.
"""
if queryset is None:
queryset = model.objects.all()
try:
return queryset.get(**kwargs)
except model.DoesNotExist:
return None
这可以通过两种方式使用,例如:
obj = get_unique_or_none(Model, **kwargs) 如前所述obj = get_unique_or_none(Model, parent.children, **kwargs)【讨论】:
无一例外:
if SomeModel.objects.filter(foo='bar').exists():
x = SomeModel.objects.get(foo='bar')
else:
x = None
使用异常:
try:
x = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
x = None
关于何时应该在 python 中使用异常存在一些争论。一方面,“请求宽恕比请求许可更容易”。虽然我同意这一点,但我认为应该保留一个例外,好吧,例外,并且“理想情况”应该运行而不会碰到一个。
【讨论】:
我们可以使用 Django 内置异常,它附加到名为 .DoesNotExist 的模型上。所以,我们不必导入ObjectDoesNotExist 异常。
改为:
from django.core.exceptions import ObjectDoesNotExist
try:
content = Content.objects.get(name="baby")
except ObjectDoesNotExist:
content = None
我们可以这样做:
try:
content = Content.objects.get(name="baby")
except Content.DoesNotExist:
content = None
【讨论】:
我也面临同样的问题。当您想从模型中获取元素时,就像@Arthur Debert 的回答一样,每次都很难编写和阅读try-except。所以,我的解决方案是创建一个由模型继承的Getter 类:
class Getter:
@classmethod
def try_to_get(cls, *args, **kwargs):
try:
return cls.objects.get(**kwargs)
except Exception as e:
return None
class MyActualModel(models.Model, Getter):
pk_id = models.AutoField(primary_key=True)
...
这样就可以得到MyActualModel或者None的实际元素了:
MyActualModel.try_to_get(pk_id=1)
【讨论】:
我使用 Django 2.2.16。这就是我解决这个问题的方法:
from typing import Any
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.db.models.base import ModelBase
from django.db.models.manager import Manager
class SManager(Manager):
def get_if_exist(self, *args: Any, **kwargs: Any):
try:
return self.get(*args, **kwargs)
except ObjectDoesNotExist:
return None
class SModelBase(ModelBase):
def _prepare(cls):
manager = SManager()
manager.auto_created = True
cls.add_to_class("objects", manager)
super()._prepare()
class Meta:
abstract = True
class SModel(models.Model, metaclass=SModelBase):
managers = False
class Meta:
abstract = True
然后,在每个模型中,您只需要导入:
from custom.models import SModel
class SUser(SModel):
pass
在views,你可以这样调用:
SUser.objects.get_if_exist(id=1)
【讨论】:
切片怎么样?它将解析到一个限制 1。
go = Content.objects.filter(name="baby")[0]
【讨论】:
IndexError。干得好