Python 防止SQLAlchemy自动设置IDENTITY\u INSERT
我有一个带有自动递增主键的SQL Server表,我正在使用SQLAlchemy处理该表。如果我创建一个包含主键的表模型实例,而不是将其关闭,并调用Python 防止SQLAlchemy自动设置IDENTITY\u INSERT,python,sql-server,sqlalchemy,Python,Sql Server,Sqlalchemy,我有一个带有自动递增主键的SQL Server表,我正在使用SQLAlchemy处理该表。如果我创建一个包含主键的表模型实例,而不是将其关闭,并调用session.add(instance),SQLAlchemy会自动向数据库发出SET IDENTITY\u INSERT[table]ON,插入成功 有没有办法自己控制这一切?如果我试图插入一个特定的主键而不是让它自动递增,我宁愿出错。如果我真的需要设置主键,我宁愿显式地告诉它 模型如下: Base = declarative_base() c
session.add(instance)
,SQLAlchemy会自动向数据库发出SET IDENTITY\u INSERT[table]ON
,插入成功
有没有办法自己控制这一切?如果我试图插入一个特定的主键而不是让它自动递增,我宁愿出错。如果我真的需要设置主键,我宁愿显式地告诉它
模型如下:
Base = declarative_base()
class Tub(Base):
__tablename__ = 'Tub'
id = Column('ID', Integer, primary_key=True, autoincrement=True)
tare_weight = Column('TareWeight', Float(53), nullable=False)
下面是一个插入示例:
t = Tub(id=20, tare_weight=200)
session.add(t)
session.commit()
以下是生成的SQL:
BEGIN
SET IDENTITY_INSERT [Tub] ON
INSERT INTO [Tub] ([ID], [TareWeight]) VALUES (20, 200)
SET IDENTITY_INSERT [Tub] OFF
COMMIT
更新:
我意识到的另一个相关情况是:
t = Tub(id=20, tare_weight=200)
session.merge(t)
session.commit()
如果浴缸已经存在于数据库中,我希望更新其重量。如果没有,我希望插入失败,因为它包含一个显式主键。根据:
对于不需要此默认生成的IDENTITY
的情况,请为列指定False
。在第一个整数主键列上指定autoincrement
标志:
m=MetaData()
t=表('t',m,
列('id',整数,主键=True,自动递增=False),
列('x',整数))
m、 创建所有(引擎)
根据:
对于不需要此默认生成的IDENTITY
的情况,请为列指定False
。在第一个整数主键列上指定autoincrement
标志:
m=MetaData()
t=表('t',m,
列('id',整数,主键=True,自动递增=False),
列('x',整数))
m、 创建所有(引擎)
为什么不重写构造函数呢
class-Tub(基本):
__tablename_uuu='Tub'
id=列('id',整数,主键=True,自动递增=True)
皮重重量=列('TareWeight',浮动(53),可为空=假)
定义初始化(self,id=None,**kwargs):
如果id不是无:
raisevalueerror(“嘿,不要传入'id`!”)
#或者你甚至可以忽略它被传入并发出警告。
超级()
如果希望将相同的方法应用于许多类,可以实现一个mixin:
导入警告
类自身免疫素:
id=列('id',整数,主键=True,自动递增=True)
定义初始化(self,id=None,**kwargs):
如果id不是无:
warnings.warn(“忽略了提供的`id`值”)
超级()
类别桶(基础,自增霉素):
皮重=。。。
为什么不重写构造函数呢
class-Tub(基本):
__tablename_uuu='Tub'
id=列('id',整数,主键=True,自动递增=True)
皮重重量=列('TareWeight',浮动(53),可为空=假)
定义初始化(self,id=None,**kwargs):
如果id不是无:
raisevalueerror(“嘿,不要传入'id`!”)
#或者你甚至可以忽略它被传入并发出警告。
超级()
如果希望将相同的方法应用于许多类,可以实现一个mixin:
导入警告
类自身免疫素:
id=列('id',整数,主键=True,自动递增=True)
定义初始化(self,id=None,**kwargs):
如果id不是无:
warnings.warn(“忽略了提供的`id`值”)
超级()
类别桶(基础,自增霉素):
皮重=。。。
长远来看,您可以进行完整的猴子修补。我不认为它解决了所有问题,但它确实阻止了使用SET IDENTITY\u INSERT
。您可以使用monkey patchMSExecutionContext
s和。例如:
from sqlalchemy import engine
from sqlalchemy.dialects.mssql.base import MSExecutionContext
def pre_exec(self):
if self.isinsert:
tbl = self.compiled.statement.table
seq_column = tbl._autoincrement_column
insert_has_sequence = seq_column is not None
self._select_lastrowid = (
not self.compiled.inline
and insert_has_sequence
and not self.compiled.returning
and not self._enable_identity_insert
and not self.executemany
)
def post_exec(self):
conn = self.root_connection
if self._select_lastrowid:
if self.dialect.use_scope_identity:
conn._cursor_execute(
self.cursor,
"SELECT scope_identity() AS lastrowid",
(),
self,
)
else:
conn._cursor_execute(
self.cursor, "SELECT @@identity AS lastrowid", (), self
)
# fetchall() ensures the cursor is consumed without closing it
row = self.cursor.fetchall()[0]
self._lastrowid = int(row[0])
if (
self.isinsert or self.isupdate or self.isdelete
) and self.compiled.returning:
self._result_proxy = engine.FullyBufferedResultProxy(self)
MSExecutionContext.pre_exec = pre_exec
MSExecutionContext.post_exec = post_exec
我粗略的例子是,这只正在使用的猴子修补了使用\u enable\u identity\u insert
功能的函数,并删除了这些部分。现在,每次尝试插入ID时都会出现以下错误:
sqlalchemy.exc.IntegrityError:(pyodbc.IntegrityError)('23000',“[23000][Microsoft][SQL Server Native Client 11.0][SQL Server]在identity_insert设置为OFF时,无法在表'Tub'中为identity列插入显式值。(544)(SQLExecDirectW)”
您现在的问题可能是您完全依赖自动增量功能,但它可能对某些人有用,或者您可以找到其他方法来绕过此功能。从长远来看,您可以完全使用猴子补丁。我不认为它解决了所有问题,但它确实阻止了使用
SET IDENTITY\u INSERT
。您可以使用monkey patchMSExecutionContext
s和。例如:
from sqlalchemy import engine
from sqlalchemy.dialects.mssql.base import MSExecutionContext
def pre_exec(self):
if self.isinsert:
tbl = self.compiled.statement.table
seq_column = tbl._autoincrement_column
insert_has_sequence = seq_column is not None
self._select_lastrowid = (
not self.compiled.inline
and insert_has_sequence
and not self.compiled.returning
and not self._enable_identity_insert
and not self.executemany
)
def post_exec(self):
conn = self.root_connection
if self._select_lastrowid:
if self.dialect.use_scope_identity:
conn._cursor_execute(
self.cursor,
"SELECT scope_identity() AS lastrowid",
(),
self,
)
else:
conn._cursor_execute(
self.cursor, "SELECT @@identity AS lastrowid", (), self
)
# fetchall() ensures the cursor is consumed without closing it
row = self.cursor.fetchall()[0]
self._lastrowid = int(row[0])
if (
self.isinsert or self.isupdate or self.isdelete
) and self.compiled.returning:
self._result_proxy = engine.FullyBufferedResultProxy(self)
MSExecutionContext.pre_exec = pre_exec
MSExecutionContext.post_exec = post_exec
我粗略的例子是,这只正在使用的猴子修补了使用\u enable\u identity\u insert
功能的函数,并删除了这些部分。现在,每次尝试插入ID时都会出现以下错误:
sqlalchemy.exc.IntegrityError:(pyodbc.IntegrityError)('23000',“[23000][Microsoft][SQL Server Native Client 11.0][SQL Server]在identity_insert设置为OFF时,无法在表'Tub'中为identity列插入显式值。(544)(SQLExecDirectW)”
您现在的问题可能是您完全依赖于自动增量功能,但它可能对某些人有用,或者您可以找到其他方法来绕过此功能。我将添加我自己的答案,以详细说明我对@supershot答案的评论。我认为,如果要在一开始就阻止主键获取值,那么应该通过属性而不是构造函数来实现:
class Tub(Base):
__tablename__ = 'Tub'
__id = Column('ID', Integer, primary_key=True, autoincrement=True)
tare_weight = Column('TareWeight', Float(53), nullable=False)
@property
def id(self):
return self.__id
这会阻止您同时做这两件事,而不仅仅是第一件:
tub = Tub(id=5, tare_weight=100)
or
tub = Tub(tare_weight=100)
tub.id = 5
AttributeError:无法设置属性