[英]How do I get the object if it exists, or None if it does not exist in Django?
When I ask the model manager to get an object, it raises DoesNotExist
when there is no matching object.当我要求 model 经理获取DoesNotExist
时,如果没有匹配的 object,它会引发 DoesNotExist。
go = Content.objects.get(name="baby")
Instead of DoesNotExist
, how can I have go
be None
instead?而不是DoesNotExist
,我怎么能让go
变成None
呢?
There is no 'built in' way to do this.没有“内置”方法可以做到这一点。 Django will raise the DoesNotExist exception every time. Django 每次都会引发 DoesNotExist 异常。 The idiomatic way to handle this in python is to wrap it in a try catch:在 python 中处理此问题的惯用方法是将其包装在 try catch 中:
try:
go = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
go = None
What I did do, is to subclass models.Manager, create a safe_get
like the code above and use that manager for my models.我所做的是继承models.Manager,像上面的代码一样创建一个safe_get
,并将该管理器用于我的模型。 That way you can write: SomeModel.objects.safe_get(foo='bar')
.这样你就可以写: SomeModel.objects.safe_get(foo='bar')
。
从 django 1.6 开始,您可以像这样使用first()方法:
Content.objects.filter(name="baby").first()
get()
raises aDoesNotExist
exception if an object is not found for the given parameters.如果未找到给定参数的对象,get()
将引发DoesNotExist
异常。 This exception is also an attribute of the model class.这个异常也是模型类的一个属性。 TheDoesNotExist
exception inherits fromdjango.core.exceptions.ObjectDoesNotExist
DoesNotExist
异常继承自django.core.exceptions.ObjectDoesNotExist
You can catch the exception and assign None
to go.您可以捕获异常并分配None
去。
from django.core.exceptions import ObjectDoesNotExist
try:
go = Content.objects.get(name="baby")
except ObjectDoesNotExist:
go = None
You can create a generic function for this.您可以为此创建一个通用函数。
def get_or_none(classmodel, **kwargs):
try:
return classmodel.objects.get(**kwargs)
except classmodel.DoesNotExist:
return None
Use this like below:像下面这样使用:
go = get_or_none(Content,name="baby")
go
will be None
if no entry matches else will return the Content entry.如果没有条目匹配,则go
将为None
,否则将返回 Content 条目。
Note:It will raises exception MultipleObjectsReturned
if more than one entry returned for name="baby"
.注意:如果name="baby"
返回多个条目,则会引发异常MultipleObjectsReturned
。
You should handle it on the data model to avoid this kind of error but you may prefer to log it at run time like this:您应该在数据模型上处理它以避免这种错误,但您可能更喜欢在运行时记录它,如下所示:
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
You can do it this way:你可以这样做:
go = Content.objects.filter(name="baby").first()
Now go variable could be either the object you want or None现在 go 变量可以是你想要的对象,也可以是 None
Ref: https://docs.djangoproject.com/en/1.8/ref/models/querysets/#django.db.models.query.QuerySet.first参考: https ://docs.djangoproject.com/en/1.8/ref/models/querysets/#django.db.models.query.QuerySet.first
To make things easier, here is a snippet of the code I wrote, based on inputs from the wonderful replies here:为了让事情变得更简单,这是我编写的代码片段,基于此处精彩回复的输入:
class MyManager(models.Manager):
def get_or_none(self, **kwargs):
try:
return self.get(**kwargs)
except ObjectDoesNotExist:
return None
And then in your model:然后在你的模型中:
class MyModel(models.Model):
objects = MyManager()
That's it.而已。 Now you have MyModel.objects.get() as well as MyModel.objetcs.get_or_none()现在你有 MyModel.objects.get() 以及 MyModel.objetcs.get_or_none()
you could use exists
with a filter:您可以将exists
与过滤器一起使用:
Content.objects.filter(name="baby").exists()
#returns False or True depending on if there is anything in the QS
just an alternative for if you only want to know if it exists如果您只想知道它是否存在,只是一种选择
It's one of those annoying functions that you might not want to re-implement:这是您可能不想重新实现的烦人功能之一:
from annoying.functions import get_object_or_None
#...
user = get_object_or_None(Content, name="baby")
也许你使用更好:
User.objects.filter(username=admin_username).exists()
Handling exceptions at different points in your views could really be cumbersome..What about defining a custom Model Manager, in the models.py file, like在视图中的不同点处理异常可能真的很麻烦..在 models.py 文件中定义自定义模型管理器怎么样,比如
class ContentManager(model.Manager):
def get_nicely(self, **kwargs):
try:
return self.get(kwargs)
except(KeyError, Content.DoesNotExist):
return None
and then including it in the content Model class然后将其包含在内容模型类中
class Content(model.Model):
...
objects = ContentManager()
In this way it can be easily dealt in the views ie通过这种方式,它可以很容易地在视图中处理,即
post = Content.objects.get_nicely(pk = 1)
if post:
# Do something
else:
# This post doesn't exist
如果您想要一个不涉及异常处理、条件语句或 Django 1.6+ 要求的简单单行解决方案,请改为执行以下操作:
x = next(iter(SomeModel.objects.filter(foo='bar')), None)
I think it isn't bad idea to use get_object_or_404()
我认为使用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)
This example is equivalent to:这个例子等价于:
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.")
You can read more about get_object_or_404() in django online documentation.您可以在 django 在线文档中阅读有关get_object_or_404()的更多信息。
From django 1.7 onwards you can do like:从 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):
...
The advantage of "MyQuerySet.as_manager()" is that both of the following will work: “MyQuerySet.as_manager()”的优点是以下两个都可以工作:
MyModel.objects.filter(...).get_or_none()
MyModel.objects.get_or_none()
This is a copycat from Django's get_object_or_404 except that the method returns None.这是 Django 的 get_object_or_404 的复制品,只是该方法返回 None。 This is extremely useful when we have to use only()
query to retreive certain fields only.当我们必须使用only()
查询仅检索某些字段时,这非常有用。 This method can accept a model or a queryset.此方法可以接受模型或查询集。
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
Here's a variation on the helper function that allows you to optionally pass in a QuerySet
instance, in case you want to get the unique object (if present) from a queryset other than the model's all
objects queryset (eg from a subset of child items belonging to a parent instance):这是帮助函数的一个变体,它允许您有选择地传入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
This can be used in two ways, eg:这可以通过两种方式使用,例如:
obj = get_unique_or_none(Model, **kwargs)
as previosuly discussed obj = get_unique_or_none(Model, **kwargs)
obj = get_unique_or_none(Model, parent.children, **kwargs)
Without exception:毫无例外:
if SomeModel.objects.filter(foo='bar').exists():
x = SomeModel.objects.get(foo='bar')
else:
x = None
Using an exception:使用异常:
try:
x = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
x = None
There is a bit of an argument about when one should use an exception in python.关于何时应该在 python 中使用异常存在一些争论。 On the one hand, "it is easier to ask for forgiveness than for permission".一方面,“请求原谅比请求许可更容易”。 While I agree with this, I believe that an exception should remain, well, the exception, and the "ideal case" should run without hitting one.虽然我同意这一点,但我认为应该保留一个例外,好吧,例外,并且“理想情况”应该运行而不会碰到一个。
We can use Django builtin exception which attached to the models named as .DoesNotExist
.我们可以使用附加到名为.DoesNotExist
的模型的 Django 内置异常。 So, we don't have to import ObjectDoesNotExist
exception.因此,我们不必导入ObjectDoesNotExist
异常。
Instead doing:而是这样做:
from django.core.exceptions import ObjectDoesNotExist
try:
content = Content.objects.get(name="baby")
except ObjectDoesNotExist:
content = None
We can do this:我们做得到:
try:
content = Content.objects.get(name="baby")
except Content.DoesNotExist:
content = None
I was facing with the same problem too.我也面临同样的问题。 It's hard to write and read try-except
for each time when you want to get an element from your model as in @Arthur Debert's answer.很难编写和阅读try-except
每次您想从模型中获取元素,如@Arthur Debert 的回答。 So, my solution is to create an Getter
class which is inherited by the models:所以,我的解决方案是创建一个由模型继承的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)
...
In this way, I can get the actual element of MyActualModel
or None
:这样,我可以获得MyActualModel
或None
的实际元素:
MyActualModel.try_to_get(pk_id=1)
I use Django 2.2.16.我使用 Django 2.2.16。 And this is how I solve this problem:这就是我解决这个问题的方法:
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
And after that, in every models, you just need to import in:之后,在每个模型中,您只需要导入:
from custom.models import SModel
class SUser(SModel):
pass
And in views
, you can call like this:在views
中,你可以这样调用:
SUser.objects.get_if_exist(id=1)
I prefer this method without using exceptions.我更喜欢这种方法而不使用异常。 It also handles multiple objects as well as no objects.它还处理多个对象以及没有对象。
go_list = Content.objects.filter(name="baby")
if (len(go_list) == 1):
go = go_list[0]
else:
go = None # optionally do other things if there are multiple objects / no objects.
What if something like this?如果是这样怎么办?
go = (Content.objects.filter(name="value") or [None])[0]
How about a slice?切片怎么样? It will parse to a limit 1.它将解析到限制 1。
go = Content.objects.filter(name="baby")[0]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.