- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
当使用 SQLAlchemy 时,我希望在传入相关对象时在 Python 对象上填写外键字段。例如,假设您有带端口的网络设备,并假设该设备在数据库中有一个复合主键。
如果我已经有一个“设备”实例的引用,并且想创建一个链接到该设备的新“端口”实例,而不知道它是否已经存在于数据库中,我将使用 merge
在南非运营。但是,仅在 port
实例上设置 device
属性是不够的。复合外键的字段将不会传播到 port
实例,SA 将无法确定数据库中是否存在该行并无条件地发出 INSERT
语句而不是 UPDATE
。
以下代码示例演示了该问题。它们应该作为一个 .py
文件运行,这样我们就有了相同的内存中 SQLite 实例!它们只是为了便于阅读而拆分。
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Unicode, ForeignKeyConstraint, create_engine
from sqlalchemy.orm import sessionmaker, relation
from textwrap import dedent
Base = declarative_base()
class Device(Base):
__tablename__ = 'device'
hostname = Column(Unicode, primary_key=True)
scope = Column(Unicode, primary_key=True)
poll_ip = Column(Unicode, primary_key=True)
notes = Column(Unicode)
ports = relation('Port', backref='device')
class Port(Base):
__tablename__ = 'port'
__table_args__ = (
ForeignKeyConstraint(
['hostname', 'scope', 'poll_ip'],
['device.hostname', 'device.scope', 'device.poll_ip'],
onupdate='CASCADE', ondelete='CASCADE'
),
)
hostname = Column(Unicode, primary_key=True)
scope = Column(Unicode, primary_key=True)
poll_ip = Column(Unicode, primary_key=True)
name = Column(Unicode, primary_key=True)
engine = create_engine('sqlite://', echo=True)
Base.metadata.bind = engine
Base.metadata.create_all()
Session = sessionmaker(bind=engine)
该模型定义了一个 Device
类,该类具有包含三个字段的复合 PK。 Port
类通过这三列上的复合 FK 引用 Device
。 Device
也与将使用该 FK 的 Port
有关系。
首先,我们添加一个新的设备和端口。由于我们正在使用内存中的 SQLite 数据库,因此这将是数据库中仅有的两个条目。通过将一个设备插入数据库,我们在设备中有了一些东西我们希望在 session “sess2”中的后续合并中加载的表
sess1 = Session()
d1 = Device(hostname='d1', scope='s1', poll_ip='pi1')
p1 = Port(device=d1, name='port1')
sess1.add(d1)
sess1.commit()
sess1.close()
这个 block 可以工作,但它不是以我期望的方式编写的。更准确地说,实例“d1”是用“hostname”、“scope”和“poll_ip”实例化的,并且该实例被传递给“Port”实例“p2”。我希望“p2”会通过外键“接收”这 3 个值。但事实并非如此。在调用“合并”之前,我被迫手动将值分配给“p2”。如果未分配值,SA 将找不到身份并尝试运行“p2”的“插入”查询,这将与现有实例发生冲突。
sess2 = Session()
d1 = Device(hostname='d1', scope='s1', poll_ip='pi1')
p2 = Port(device=d1, name='port1')
p2.hostname=d1.hostname
p2.poll_ip=d1.poll_ip
p2.scope = d1.scope
p2 = sess2.merge(p2)
sess2.commit()
sess2.close()
此 block 显示了我希望它如何工作。我希望在创建 Port 实例时为“device”赋值就足够了。
sess3 = Session()
d1 = Device(hostname='d1', scope='s1', poll_ip='pi1')
p2 = Port(device=d1, name='port1')
p2 = sess3.merge(p2)
sess3.commit()
sess3.close()
我怎样才能使最后一个 block 起作用?
最佳答案
在您显式或通过 commit()
发出 flush()
之前,子对象的 FK 不会更新。我认为这样做的原因是,如果关系的父对象也是一个具有自动增量 PK 的新实例,SQLAlchemy 需要从数据库中获取 PK,然后才能更新子对象上的 FK(但我支持予以更正!)。
根据docs , 一个 merge()
:
examines the primary key of the instance. If it’s present, it attempts to locate that instance in the local identity map. If the load=True flag is left at its default, it also checks the database for this primary key if not located locally.
If the given instance has no primary key, or if no instance can be found with the primary key given, a new instance is created.
由于您在 flushing
之前合并
,您的 p2
实例上的 PK 数据不完整,所以这一行 p2 = sess3 .merge(p2)
返回一个新的 Port
实例,其属性值与您之前创建的 p2
相同,由 session< 跟踪
。然后,sess3.commit()
最终发出将 FK 数据填充到 p2
的刷新,然后在尝试写入 时引发完整性错误端口
表。虽然,插入 sess3.flush()
只会更早地引发完整性错误,而不是避免它。
像这样的东西会起作用:
def existing_or_new(sess, kls, **kwargs):
inst = sess.query(kls).filter_by(**kwargs).one_or_none()
if not inst:
inst = kls(**kwargs)
return inst
id_data = dict(hostname='d1', scope='s1', poll_ip='pi1')
sess3 = Session()
d1 = Device(**id_data)
p2 = existing_or_new(sess3, Port, name='port1', **id_data)
d1.ports.append(p2)
sess3.commit()
sess3.close()
This question有更详尽的 SQLAlchemy existing_or_new
样式函数示例。
关于python - 如何通过在构造函数中传递相关实体来使 SQLAlchemy 为外键设置值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52903625/
C语言sscanf()函数:从字符串中读取指定格式的数据 头文件: ?
最近,我有一个关于工作预评估的问题,即使查询了每个功能的工作原理,我也不知道如何解决。这是一个伪代码。 下面是一个名为foo()的函数,该函数将被传递一个值并返回一个值。如果将以下值传递给foo函数,
CStr 函数 返回表达式,该表达式已被转换为 String 子类型的 Variant。 CStr(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CSng 函数 返回表达式,该表达式已被转换为 Single 子类型的 Variant。 CSng(expression) expression 参数是任意有效的表达式。 说明 通常,可
CreateObject 函数 创建并返回对 Automation 对象的引用。 CreateObject(servername.typename [, location]) 参数 serv
Cos 函数 返回某个角的余弦值。 Cos(number) number 参数可以是任何将某个角表示为弧度的有效数值表达式。 说明 Cos 函数取某个角并返回直角三角形两边的比值。此比值是
CLng 函数 返回表达式,此表达式已被转换为 Long 子类型的 Variant。 CLng(expression) expression 参数是任意有效的表达式。 说明 通常,您可以使
CInt 函数 返回表达式,此表达式已被转换为 Integer 子类型的 Variant。 CInt(expression) expression 参数是任意有效的表达式。 说明 通常,可
Chr 函数 返回与指定的 ANSI 字符代码相对应的字符。 Chr(charcode) charcode 参数是可以标识字符的数字。 说明 从 0 到 31 的数字表示标准的不可打印的
CDbl 函数 返回表达式,此表达式已被转换为 Double 子类型的 Variant。 CDbl(expression) expression 参数是任意有效的表达式。 说明 通常,您可
CDate 函数 返回表达式,此表达式已被转换为 Date 子类型的 Variant。 CDate(date) date 参数是任意有效的日期表达式。 说明 IsDate 函数用于判断 d
CCur 函数 返回表达式,此表达式已被转换为 Currency 子类型的 Variant。 CCur(expression) expression 参数是任意有效的表达式。 说明 通常,
CByte 函数 返回表达式,此表达式已被转换为 Byte 子类型的 Variant。 CByte(expression) expression 参数是任意有效的表达式。 说明 通常,可以
CBool 函数 返回表达式,此表达式已转换为 Boolean 子类型的 Variant。 CBool(expression) expression 是任意有效的表达式。 说明 如果 ex
Atn 函数 返回数值的反正切值。 Atn(number) number 参数可以是任意有效的数值表达式。 说明 Atn 函数计算直角三角形两个边的比值 (number) 并返回对应角的弧
Asc 函数 返回与字符串的第一个字母对应的 ANSI 字符代码。 Asc(string) string 参数是任意有效的字符串表达式。如果 string 参数未包含字符,则将发生运行时错误。
Array 函数 返回包含数组的 Variant。 Array(arglist) arglist 参数是赋给包含在 Variant 中的数组元素的值的列表(用逗号分隔)。如果没有指定此参数,则
Abs 函数 返回数字的绝对值。 Abs(number) number 参数可以是任意有效的数值表达式。如果 number 包含 Null,则返回 Null;如果是未初始化变量,则返回 0。
FormatPercent 函数 返回表达式,此表达式已被格式化为尾随有 % 符号的百分比(乘以 100 )。 FormatPercent(expression[,NumDigitsAfterD
FormatNumber 函数 返回表达式,此表达式已被格式化为数值。 FormatNumber( expression [,NumDigitsAfterDecimal [,Inc
我是一名优秀的程序员,十分优秀!