2

django orm/ 其他 orm 中 create 一条数据,返回的 model id 为什么一定是正确的?

 2 years ago
source link: https://www.v2ex.com/t/842931
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

V2EX  ›  Python

django orm/ 其他 orm 中 create 一条数据,返回的 model id 为什么一定是正确的?

  chaleaochexist · 14 小时 14 分钟前 · 358 次点击
就以 django orm 举例子

user = UserModel.objects.create(name="haha")

print(user.id)
>>> 666


我的问题是 这个 user.id 为什么能保证是正确的。 当有并发插入的时候,django/数据库是如何保证事物约束的?

我翻了 django 的源码,django 是利用两条语句实现的
https://github.com/django/django/blob/main/django/db/models/sql/compiler.py

1. insert # cursor.execute(sql, params)
2. select id from user # self.connection.ops.last_insert_id(

两条 sql 语句中间如何插入了另一条数据, 获取最新的 id 就是不正确的了。

不知道是不是这句话起作用了:with self.connection.cursor() as cursor:


另一个问题也困扰我好多年: 什么是 cursor ?
8 条回复    2022-03-26 00:12:17 +08:00

adoal

adoal      13 小时 1 分钟前   ❤️ 1

https://en.wikipedia.org/wiki/Insert_(SQL)#Retrieving_the_key

用子句方式的自然不存在这个问题。用存储过程方式的,是通过数据库内部的实现保存了当前会话里最近一次的数据变更操作结果,而不是再写一条 SQL 到表里去查。注意,你所提到的“self.connection.ops.last_insert_id”实际上是在一个 else:分支里,前面还有 if 和 elif 两个分支,处理的就是用子句方式的情况。

所以其实答案的关键不是 Django 怎么做,而是底层的 SQL 怎么做。这个功能的正确性是 RDBMS 本身保证的。如果某个 RDBMS 不能保证,那 Django 怎么写也没用。

CEBBCAT

CEBBCAT      12 小时 56 分钟前   ❤️ 1

楼主对代码的阅读还是有一些偏差,比如“self.connection.ops.last_insert_id”在做的应该不是“select id from user”,我头说起吧,假定你使用的是 MySQL 。

MySQL 提供了 LAST_INSERT_ID() [0]函数,调用它将会返回当前回话上次插入的第一个生成的值,一般就是 AUTO_INCREMENT 。所以 Django 是有能力获取正确的用户 ID 的。而这个函数的运作方式从文档也可以看到,是不受其他 client 影响的,所以不用担心并发。


本来还打了一大堆 InnoDB 、X 锁之类的,重新审题发现好像是我想多了,放在参考资料吧,你再用里面提到的关键词搜索,应该能搜到不少资料。

忘了说,你可以看看 “self.connection.ops.last_insert_id” ,再向下找应该就可以找到 LAST_INSERT_ID() 了。另外虽然我好久没写 Python 了,但 with 显然不是做这个用的。再另外,想问一下楼主是怎么学习 Python 的?感觉基础还需要再扎实一点呀。再另外,我觉得可以把 cursor 理解为一个会话,当然了,最好找专门的文章。


0. https://dev.mysql.com/doc/refman/8.0/en/information-functions.html#function_last-insert-id
参考资料:
https://segmentfault.com/a/1190000023869573
https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-insert-intention-locks
https://dev.mysql.com/doc/refman/8.0/en/innodb-auto-increment-handling.html

https://github.com/django/django/blob/379bb201ed346a76e322fe7c6cbd13cb4a6e68a1/django/db/models/sql/compiler.py#L1662-L1666

xhzhang

xhzhang      12 小时 28 分钟前

with 语句只是一个语法糖,自动打开一个游标,在 with 的代码块内使用完成后,会自动调用 cursor.close()关闭游标。

user = UserModel.objects.create(name="haha") 这句是在数据库写入了一条数据,user 就代表新写入的数据,在 orm 中映射成 User 的一个类实例。只要数据库能保证生产的 id 是正确的,返回类的 id 必然就是正确的。

chaleaochexist

chaleaochexist      10 小时 46 分钟前

@xhzhang 为什么必然是正确的? 如果 orm 是你自己写的, 你如何保证是正确的?
这里是通过两个 sql 实现的功能。

chaleaochexist

chaleaochexist      10 小时 37 分钟前

@CEBBCAT 我问的是 orm 的问题和你说的 MySQL LAST_INSERT_ID()无关。
不过你说的 LAST_INSERT_ID() 这个函数, 确实是对的。 学到了。

CEBBCAT

CEBBCAT      10 小时 36 分钟前 via iPhone

@chaleaochexist 无关……无可救药

CEBBCAT

CEBBCAT      10 小时 26 分钟前 via iPhone

@CEBBCAT 修正一下,话可能说重了,但你一句话真的是把我憋死了。

ORM 没什么了不起的,无非就是代码多一点的 SQL 生成器,楼上说得很有道理,DBMS 不支持的,ORM 写出花来也没用。

你还是多看看 ORM 代码吧,或者找一些七天 ORM 的博客看看

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK