幕后的 Django 查询集

Django queryset behind the scenes

**

为一致性和连接创建外键的区别

**

我可以在 Django 中使用外键和查询集 API。

我只是想更深入地了解它是如何在幕后工作的。

在 Django 手册中,它说

a database index is automatically created on the ForeignKey. You can disable this by setting db_index to False. You may want to avoid the overhead of an index if you are creating a foreign key for consistency rather than joins, or if you will be creating an alternative index like a partial of multiple column index.

为一致性而不是连接创建外键

这部分让我很困惑。

如果您像下面这样使用外键进行查询,我希望您使用 Join 关键字。

SELECT 
* 
FROM 
vehicles 
INNER JOIN users ON vehicles.car_owner = users.user_id

例如,

class Place(models.Model):
    name = models.Charfield(max_length=50)
    address = models.Charfield(max_length=50)

class Comment(models.Model):
    place = models.ForeignKeyField(Place)
    content = models.Charfield(max_length=50)

如果您使用像 Comment.objects.filter(place=1) 这样的查询集,我希望在低级别 SQL 命令中使用 Join 关键字。

但是,当我通过在控制台中打印 queryset.query 来检查它时,它显示如下。

(我用模型简化只是为了解释。下面,它显示了我模型中的所有属性。您可以忽略属性)

SELECT
 "bfm_comment"."id", "bfm_comment"."content", "bfm_comment"."user_id", "bfm_comment"."place_id", "bfm_comment"."created_at" 
FROM "bfm_comment" WHERE "bfm_comment"."place_id" = 1

为一致性创建外键与为连接创建外键

简单地说,我想如果你使用任何查询集,就意味着使用外键进行连接。因为你可以很容易地通过c = Comment.objects.get(id=1) c.place.name得到parent的table数据。我认为它在幕后加入了两个 table。但是 Print(queryset.query) 的结果不是 Join Keyword 而是 Find it by Where keyword.


我从回答中理解的方式

案例一:

Comment.objects.filter(place=1)

结果

SELECT 
"bfm_comment"."id", "bfm_comment"."content", "bfm_comment"."user_id", "bfm_comment"."place_id", "bfm_comment"."created_at" 
FROM "bfm_comment" 
WHERE "bfm_comment"."id" = 1

案例二:

Comment.objects.filter(place__name="df")

结果

SELECT "bfm_comment"."id", "bfm_comment"."content", "bfm_comment"."user_id", "bfm_comment"."place_id", "bfm_comment"."created_at" 
FROM "bfm_comment" INNER JOIN "bfm_place" ON ("bfm_comment"."place_id" = "bfm_place"."id") 
WHERE "bfm_place"."name" = df

案例 1 正在搜索 comment.id 列在评论 table 中为 1 的行。 但是在情况2中,它需要知道Place table的属性'name',所以它必须使用JOIN关键字来检查Place [=]列中的值94=]。 对吗?

因此,如果我使用像 Case2 这样的查询集,并且最好在外键上创建索引,是否可以认为我为连接创建了一个外键?

对于上面的问题,我想我可以从Django Manual

那里得到答案

Consider adding indexes to fields that you frequently query using filter(), exclude(), order_by(), etc. as indexes may help to speed up lookups. Note that determining the best indexes is a complex database-dependent topic that will depend on your particular application. The overhead of maintaining an index may outweigh any gains in query speed

总之,这实际上取决于我的应用程序如何使用它。

执行下面的命令就可以揭开谜底了

./manage.py sqlmigrate myapp 0001

注意将 myapp 替换为您的应用程序名称(bfm 我认为)并将 0001 替换为创建 Comment 模型的实际迁移。

生成的 sql 将显示实际的 table 是用 place_id int 创建的,而不是 place Place,这是因为 RDBMS 对模型一无所知,模型仅在应用程序级别。 django orm 的工作是从 RDBMS 中获取数据并将它们转换为模型实例。这就是为什么您总是在每个 Comment 实例中获得一个 place 成员,并且该 place 成员使您可以依次访问相关 Place 实例的成员。

那么当你这样做时会发生什么?

Comment.objects.filter(place=1)

Django 足够聪明,知道您指的是 place_id,因为 1 显然不是 Place 的实例。但是,如果您使用 Place 实例,结果将是相同的。所以这里没有join。上面的查询肯定会受益于 place_id 上的索引,但它不会受益于外键约束!!仅查询 Comment table。

如果你想加入,试试这个:

Comment.objects.filter(place__name='my home')

具有 __ 的这种性质的查询通常会导致联接,但有时会导致子查询。

查询集是惰性的。

https://docs.djangoproject.com/en/1.10/topics/db/queries/#querysets-are-lazy

QuerySets are lazy – the act of creating a QuerySet doesn’t involve any database activity. You can stack filters together all day long, and Django won’t actually run the query until the QuerySet is evaluated. Take a look at this example: