.filter() 与 .get() 对于单个对象? (姜戈)

发布于 2024-07-25 04:48:18 字数 503 浏览 6 评论 0原文

我和一些同事就这个问题进行了辩论。 当您只需要一个对象时,是否有一种首选方法可以在 Django 中检索对象?

两种明显的方法是:

try:
    obj = MyModel.objects.get(id=1)
except MyModel.DoesNotExist:
    # We have no object! Do something...
    pass

并且:

objs = MyModel.objects.filter(id=1)

if len(objs) == 1:
    obj = objs[0]
else:
    # We have no object! Do something...
    pass

第一种方法在行为上似乎更正确,但在控制流中使用异常,这可能会带来一些开销。 第二个更加迂回,但永远不会引发异常。

对于其中哪一个更可取有什么想法吗? 哪个更有效率?

I was having a debate on this with some colleagues. Is there a preferred way to retrieve an object in Django when you're expecting only one?

The two obvious ways are:

try:
    obj = MyModel.objects.get(id=1)
except MyModel.DoesNotExist:
    # We have no object! Do something...
    pass

And:

objs = MyModel.objects.filter(id=1)

if len(objs) == 1:
    obj = objs[0]
else:
    # We have no object! Do something...
    pass

The first method seems behaviorally more correct, but uses exceptions in control flow which may introduce some overhead. The second is more roundabout but won't ever raise an exception.

Any thoughts on which of these is preferable? Which is more efficient?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

油饼 2024-08-01 04:48:18

get()提供了 专门针对此案例。 用它。

选项 2 几乎正是 get() 方法在 Django 中实际实现的方式,因此不应该有“性能”差异(并且您正在考虑它的事实表明您违反了一个编程的基本规则,即在编写和分析代码之前尝试优化代码 - 直到您拥有代码并可以运行它,您不知道它将如何执行,并且在此之前尝试优化是一条路径的疼痛)。

get() is provided specifically for this case. Use it.

Option 2 is almost precisely how the get() method is actually implemented in Django, so there should be no "performance" difference (and the fact that you're thinking about it indicates you're violating one of the cardinal rules of programming, namely trying to optimize code before it's even been written and profiled -- until you have the code and can run it, you don't know how it will perform, and trying to optimize before then is a path of pain).

旧故 2024-08-01 04:48:18

您可以安装一个名为 django-annoying 的模块,然后执行以下操作:

from annoying.functions import get_object_or_None

obj = get_object_or_None(MyModel, id=1)

if not obj:
    #omg the object was not found do some error stuff

You can install a module called django-annoying and then do this:

from annoying.functions import get_object_or_None

obj = get_object_or_None(MyModel, id=1)

if not obj:
    #omg the object was not found do some error stuff
冰雪之触 2024-08-01 04:48:18

1 是正确的。 在 Python 中,异常与返回具有相同的开销。 对于简化的证明,您可以查看 这个

2 这就是 Django 在后端所做的事情。 get 调用 filter 并在未找到任何项目或找到多个对象时引发异常。

1 is correct. In Python an exception has equal overhead to a return. For a simplified proof you can look at this.

2 This is what Django is doing in the backend. get calls filter and raises an exception if no item is found or if more than one object is found.

燕归巢 2024-08-01 04:48:18

我来晚了一点,但在 Django 1.6 中,查询集上有 first() 方法。

https://docs. djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.first


返回查询集匹配的第一个对象,如果没有匹配的对象,则返回 None。 如果查询集没有定义排序,则查询集将自动按主键排序。

例子:

p = Article.objects.order_by('title', 'pub_date').first()
Note that first() is a convenience method, the following code sample is equivalent to the above example:

try:
    p = Article.objects.order_by('title', 'pub_date')[0]
except IndexError:
    p = None

I'm a bit late to the party, but with Django 1.6 there is the first() method on querysets.

https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.first


Returns the first object matched by the queryset, or None if there is no matching object. If the QuerySet has no ordering defined, then the queryset is automatically ordered by the primary key.

Example:

p = Article.objects.order_by('title', 'pub_date').first()
Note that first() is a convenience method, the following code sample is equivalent to the above example:

try:
    p = Article.objects.order_by('title', 'pub_date')[0]
except IndexError:
    p = None
慈悲佛祖 2024-08-01 04:48:18

为什么这一切都有效? 用 1 个内置快捷方式替换 4 行。 (这会执行自己的尝试/例外。)

from django.shortcuts import get_object_or_404

obj = get_object_or_404(MyModel, id=1)

Why do all that work? Replace 4 lines with 1 builtin shortcut. (This does its own try/except.)

from django.shortcuts import get_object_or_404

obj = get_object_or_404(MyModel, id=1)
抱着落日 2024-08-01 04:48:18

我无法谈论 Django 的任何经验,但选项 #1 清楚地告诉系统您正在请求 1 个对象,而第二个选项则不然。 这意味着选项#1 可以更轻松地利用缓存或数据库索引,特别是在您要过滤的属性不能保证唯一的情况下。

另外(再次推测)第二个选项可能必须创建某种结果集合或迭代器对象,因为 filter() 调用通常可以返回许多行。 你可以用 get() 绕过这个。

最后,第一个选项更短,并且省略了额外的临时变量——只有微小的差异,但每一点都有帮助。

I can't speak with any experience of Django but option #1 clearly tells the system that you are asking for 1 object, whereas the second option does not. This means that option #1 could more easily take advantage of cache or database indexes, especially where the attribute you're filtering on is not guaranteed to be unique.

Also (again, speculating) the second option may have to create some sort of results collection or iterator object since the filter() call could normally return many rows. You'd bypass this with get().

Finally, the first option is both shorter and omits the extra temporary variable - only a minor difference but every little helps.

慢慢从新开始 2024-08-01 04:48:18

有关例外的更多信息。 如果不筹集,它们几乎不需要花费任何成本。 因此,如果您知道可能会得到结果,请使用异常,因为使用条件表达式无论如何都会付出每次检查的成本。 另一方面,当它们被引发时,它们的成本比条件表达式要高一些,因此,如果您预计不会以某种频率得到结果(例如,如果没记错的话,30%的时间),则条件检查结果是要便宜一点。

但这是 Django 的 ORM,并且可能到数据库的往返,甚至是缓存的结果,可能会主导性能特征,因此有利于可读性,在这种情况下,因为您只期望一个结果,所以使用 get ()

Some more info about exceptions. If they are not raised, they cost almost nothing. Thus if you know you are probably going to have a result, use the exception, since using a conditional expression you pay the cost of checking every time, no matter what. On the other hand, they cost a bit more than a conditional expression when they are raised, so if you expect not to have a result with some frequency (say, 30% of the time, if memory serves), the conditional check turns out to be a bit cheaper.

But this is Django's ORM, and probably the round-trip to the database, or even a cached result, is likely to dominate the performance characteristics, so favor readability, in this case, since you expect exactly one result, use get().

肩上的翅膀 2024-08-01 04:48:18

我稍微研究了一下这个问题,发现选项 2 执行两个 SQL 查询,这对于这样一个简单的任务来说是过多的。 请参阅我的注释:

objs = MyModel.objects.filter(id=1) # This does not execute any SQL
if len(objs) == 1: # This executes SELECT COUNT(*) FROM XXX WHERE filter
    obj = objs[0]  # This executes SELECT x, y, z, .. FROM XXX WHERE filter
else: 
    # we have no object!  do something
    pass

执行单个查询的等效版本是:

items = [item for item in MyModel.objects.filter(id=1)] # executes SELECT x, y, z FROM XXX WHERE filter
count = len(items) # Does not execute any query, items is a standard list.
if count == 0:
   return None
return items[0]

通过切换到这种方法,我能够大大减少应用程序执行的查询数量。

I've played with this problem a bit and discovered that the option 2 executes two SQL queries, which for such a simple task is excessive. See my annotation:

objs = MyModel.objects.filter(id=1) # This does not execute any SQL
if len(objs) == 1: # This executes SELECT COUNT(*) FROM XXX WHERE filter
    obj = objs[0]  # This executes SELECT x, y, z, .. FROM XXX WHERE filter
else: 
    # we have no object!  do something
    pass

An equivalent version that executes a single query is:

items = [item for item in MyModel.objects.filter(id=1)] # executes SELECT x, y, z FROM XXX WHERE filter
count = len(items) # Does not execute any query, items is a standard list.
if count == 0:
   return None
return items[0]

By switching to this approach, I was able to substantially reduce number of queries my application executes.

国际总奸 2024-08-01 04:48:18

.get()

返回与给定查找参数匹配的对象,该对象应该
采用字段查找中描述的格式。

如果有多个对象被调用,get() 会引发 MultipleObjectsReturned
成立。 MultipleObjectsReturned 异常是
模型类。

如果找不到对象,get() 会引发DoesNotExist 异常
给定的参数。 这个异常也是模型的一个属性
类。

.filter()

返回一个新的查询集,其中包含与给定查找匹配的对象
参数。

注意

当你想要获取单个唯一对象时,使用 get() ,而 filter()
当您想要获取与查找参数匹配的所有对象时。

.get()

Returns the object matching the given lookup parameters, which should
be in the format described in Field lookups.

get() raises MultipleObjectsReturned if more than one object was
found. The MultipleObjectsReturned exception is an attribute of the
model class.

get() raises a DoesNotExist exception if an object wasn't found for
the given parameters. This exception is also an attribute of the model
class.

.filter()

Returns a new QuerySet containing objects that match the given lookup
parameters.

Note

use get() when you want to get a single unique object, and filter()
when you want to get all objects that match your lookup parameters.

情愿 2024-08-01 04:48:18

有趣的问题,但对我来说,选项#2 带有过早优化的味道。 我不确定哪个性能更高,但选项 #1 对我来说确实看起来和感觉更Pythonic。

Interesting question, but for me option #2 reeks of premature optimisation. I'm not sure which is more performant, but option #1 certainly looks and feels more pythonic to me.

数理化全能战士 2024-08-01 04:48:18

我建议采用不同的设计。

如果您想对可能的结果执行函数,您可以从 QuerySet 派生,如下所示: http://djangosnippets .org/snippets/734/

结果非常棒,您可以例如:

MyModel.objects.filter(id=1).yourFunction()

这里,过滤器返回一个空查询集或包含单个项目的查询集。 您的自定义查询集函数也是可链接和可重用的。 如果您想对所有条目执行此操作:MyModel.objects.all().yourFunction()

它们也非常适合用作管理界面中的操作:

def yourAction(self, request, queryset):
    queryset.yourFunction()

I suggest a different design.

If you want to perform a function on a possible result, you could derive from QuerySet, like this: http://djangosnippets.org/snippets/734/

The result is pretty awesome, you could for example:

MyModel.objects.filter(id=1).yourFunction()

Here, filter returns either an empty queryset or a queryset with a single item. Your custom queryset functions are also chainable and reusable. If you want to perform it for all your entries: MyModel.objects.all().yourFunction().

They are also ideal to be used as actions in the admin interface:

def yourAction(self, request, queryset):
    queryset.yourFunction()
|煩躁 2024-08-01 04:48:18

选项 1 更优雅,但一定要使用 try.. except。

根据我自己的经验,我可以告诉您,有时您确定数据库中不可能有多个匹配对象,但实际上会有两个......(当然,通过主键获取对象时除外)。

Option 1 is more elegant, but be sure to use try..except.

From my own experience I can tell you that sometimes you're sure there cannot possibly be more than one matching object in the database, and yet there will be two... (except of course when getting the object by its primary key).

冷夜 2024-08-01 04:48:18

很抱歉在这个问题上再添加一个,但我正在使用 django 分页器,并且在我的数据管理应用程序中,允许用户选择要查询的内容。 有时,这是一个文档的 ID,但除此之外,它是一个返回多个对象(即查询集)的通用查询。

如果用户查询 id,我可以运行:

Record.objects.get(pk=id)

这会在 django 的分页器中引发错误,因为它是记录而不是记录的查询集。

我需要运行:

Record.objects.filter(pk=id)

它返回一个包含一项的查询集。 然后分页器就可以正常工作了。

Sorry to add one more take on this issue, but I am using the django paginator, and in my data admin app, the user is allowed to pick what to query on. Sometimes that is the id of a document, but otherwise it is a general query returning more than one object, i.e., a Queryset.

If the user queries the id, I can run:

Record.objects.get(pk=id)

which throws an error in django's paginator, because it is a Record and not a Queryset of Records.

I need to run:

Record.objects.filter(pk=id)

Which returns a Queryset with one item in it. Then the paginator works just fine.

注定孤独终老 2024-08-01 04:48:18

get() 返回一个对象,如下所示:

{
  "name": "John",
  "age": "26",
  "gender": "Male"
}

< a href="https://docs.djangoproject.com/en/4.2/ref/models/querysets/#filter" rel="nofollow noreferrer">filter() 返回 QuerySet 具有一个或多个对象,如下所示:

[
  {
    "name": "John",
    "age": "26",
    "gender": "Male"
  },
  {
    "name": "Tom",
    "age": "18",
    "gender": "Male"
  },
  {
    "name": "Marry",
    "age": "22",
    "gender": "Female"
  }
]

get() returns one object as shown below:

{
  "name": "John",
  "age": "26",
  "gender": "Male"
}

filter() returns a QuerySet which has one or more objects as shown below:

[
  {
    "name": "John",
    "age": "26",
    "gender": "Male"
  },
  {
    "name": "Tom",
    "age": "18",
    "gender": "Male"
  },
  {
    "name": "Marry",
    "age": "22",
    "gender": "Female"
  }
]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文