Django,我能在查询集中获取相关对象的引用吗?

6

假设我有以下这些模型:

models.py:

class Item(models.Model):
    ref_id = models.PositiveIntegerField()
    name = models.CharacterField(max_length=32)

class ItemDue(models.Model):
    item = models.ForeignKey(Item)
    due_date = models.DateField(null=True, blank=True)
    lots of other fields below
    .
    .
    .

我想查询ItemDue对象,但是也想在查询中包含Item

如果我得到了一组ItemDue,我可以像这样循环:

for item_due in ItemDue.objects.filter(some_criteria):
    print item_due.item.ref_id

然而,当我进行一些性能测试时,它会返回到数据库以获取引用的“Item”对象,因此我必须为每个“ItemDue”运行另一个查询来获取“Item.ref_id”。这在巨大的查询中会产生差异,因此我想在获取“ItemDue”的查询集时同时获取“Item.ref_id”。我可以使用“.values('id','item__ref_id')”来获取具有“id”和“item__ref_id”的“ItemDue”字典。因此,我可以使用“.values('id','item__ref_id',...)”获取“ItemDue”中的所有字段,但那将是很多工作。是否有一种简单的方法可以附加到查询集的值中以获取该引用对象,而无需拼出“ItemDue”中的所有字段以及仅一个额外字段“item__ref_id”?
谢谢
编辑:
以下是在manage.py shell中运行的一些代码:
def check():
    start = datetime.now()
    print "Starting {0}".format(datetime.now() - start)
    index = 0
    item_rows = dict()
    print "Getting Items for PG and Parents {0}".format(datetime.now() - start)

    # items due for PG
    items = pg.item_due.all().filter(disabled=False).select_related()

    # Loop the parents, and chain their items due to the PG items due.
    for p in parents:
        items = itertools.chain(items, p.item_due.all().filter(disabled=False).select_related())
        index += 1
    print "All Items Retrieved {0}".format(datetime.now() - start)
    for item in items:
        pass
    print "Loop Items Complete {0}".format(datetime.now() - start)
    return item_rows

>>> rows = check()
Starting 0:00:00.000008
Getting Items for PG and Parents 0:00:00.000032
All Items Retrieved 0:00:00.004669
Loop Items Complete 0:00:00.022597

注意循环项目所需的时间,仅使用pass大约需要0.018秒。

现在我只需将循环中的pass更改为item.item.ref_id,这将需要更长的时间。

def check():
    start = datetime.now()
    print "Starting {0}".format(datetime.now() - start)
    index = 0
    item_rows = dict()
    print "Getting Items for PG and Parents {0}".format(datetime.now() - start)

    # items due for PG
    items = pg.item_due.all().filter(disabled=False).select_related()

    # Loop the parents, and chain their items due to the PG items due.
    for p in parents:
        items = itertools.chain(items, p.item_due.all().filter(disabled=False).select_related())
        index += 1
    print "All Items Retrieved {0}".format(datetime.now() - start)
    for item in items:
        item.item.ref_id
    print "Loop Items Complete {0}".format(datetime.now() - start)
    return item_rows

>>> rows = check()
Starting 0:00:00.000007
Getting Items for PG and Parents 0:00:00.000031
All Items Retrieved 0:00:00.004712
Loop Items Complete 0:00:00.258209

从循环运行的0.018秒到0.25秒。如果item.item.ref_id已经从查询中获取,为什么处理它需要13倍的时间?


需要注意的是,如果我将循环for item in items:更改为仅使用item.id而不是item.item.ref_id,那么速度就会恢复到0.018秒。那么,如果它正在获取select_related()对象,为什么这个引用对象查找需要这么长时间呢? - Furbeenator
1个回答

9
使用select_related可以在一次查询中获取相关表的数据。
for item_due in ItemDue.objects.filter(some_criteria).select_related():
    print item_due.item.ref_id

哇,太简单了。我只是搜索方式不对。谢谢。让我附加我的问题,因为似乎还有其他东西在拖慢速度。 - Furbeenator
@Furbeenator,你会生成与p中的父项数量相同的查询(对于p in parents: items = ..)。在下一个循环中,查询将逐个执行以获取结果(以获取item.item.ref_id)。在实际尝试获取数据之前,Django不会执行查询...因此,在这里,您不会执行任何数据库查询:for item in items: pass - ndpu
啊,你的意思是因为它们是惰性查询集,只有在被访问时才会从数据库中获取数据? - Furbeenator
但是为什么在循环中使用item.id速度很快,而使用item.item.ref_id会使速度变慢呢? - Furbeenator
这很奇怪,不应该有区别。 - ndpu
1
也许我还有其他问题,但至少我知道如何指定获取相关内容。非常感谢! - Furbeenator

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接