python - 如何防止使用相同值创建新 SQLAlchemy 对象的 UUID 主键
问题描述
我有一个 for 循环,它创建一个新的“行”对象,在将对象提交到 Postgres 数据库中的表之前用数据填充属性。我要插入的表(以及因此对象)采用 UUID 的主键,只有在第一次提交后所有新创建的行对象中的循环的第一次迭代后,这个值保持不变。
我对解决方案有点迷茫,但是我认为这可能与我处理数据库会话的方式有关。在写这篇文章时,我还注意到我在and函数中使用了相同的变量名 ( new_user
) 。虽然 Python 会认为它们在不同的范围内(我认为),但我想知道 SQLALchemy 会话是否会?invite_users
invite_user
请注意,我已经删除了很多我认为在问题上下文中多余的代码 - 主要是更多列等。此外,该invite_user
函数在其他地方使用,因此invite_users
仅用于批量“邀请”。
这是一个显示表类定义的开头和Column
定义的片段:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.dialects import postgresql
import uuid
Base = declarative_base()
class User(Base):
__tablename__ = "users"
id = Column(postgresql.UUID(as_uuid=True), default=uuid.uuid4(), primary_key=True)
email = Column(String, unique=True)
这是我用来创建和销毁会话的函数:
from contextlib import contextmanager
from sqlalchemy import create_engine
@contextmanager
def db_session(db_url):
engine = create_engine(db_url, convert_unicode=True)
connection = engine.connect()
db_session = scoped_session(sessionmaker(autocommit=False, autoflush=True, bind=engine))
yield db_session
db_session.close()
connection.close()
下面是遍历 JSON 对象数组的函数的一部分:
def invite_users(json_dict):
exceptions = {}
with db_session(environ['CONNECTION_STRING']) as session:
for new_user in json_dict['users']:
try:
invite_user(
session,
info['email_address']
)
# I'm catching exceptions and storing them to handle them later
except Exception as e:
exceptions[user_info['email_address']] = e
pass
这是invite_user
将行添加到会话并尝试提交它的函数:
from project.database import * # this contains the User table class above
from project.exceptions import *
def invite_user(session, email):
new_user = User(
email=email
)
session.add(new_user)
try:
session.commit()
except exc.IntegrityError as e:
session.rollback()
raise DuplicateViolation(f"User already invited") from None
因此,我遍历 dict ( json_dict['emails']
) 中的电子邮件地址。然后,我将每封电子邮件连同当前数据库一起传递给邀请用户session
。我这样做是为了避免每次invite_user
调用都创建一个会话,因为从性能的角度来看,这似乎比session
在invite_user
函数中创建一个新的更明智,因为这将导致它们中的很多被创建和销毁。
我认为我的Column
定义足以处理每次提交User
行对象时新 UUID 的生成。但是,如果我向函数传递invite_users
多个电子邮件地址,第一个用户会被添加一个新的 UUID,而第二个用户会被分配相同的 UUID。如果我通过它一个电子邮件地址一切都很好。
我不想依赖查询数据库中的现有行。我完全依靠数据库约束来防止重复,异常处理用于向用户报告错误。
解决方案
Column参数状态的 sqlalchemy文档:default
表示此列的默认值的标量、Python 可调用或 ColumnElement 表达式,如果在插入的 VALUES 子句中未指定此列,则将在插入时调用。
因此,与其提供uuid.uuid4()
将创建一个标量(常量)值的方法,不如只提供 callable uuid.uuid4
,以便为每个插入调用它。
推荐阅读
- numpy - 您传递给模型的 Numpy 数组列表不是模型预期的大小
- node.js - 如何解决此“发送到客户端后无法设置标头”
- python - Python Excel 程序每次运行都会更新
- .net - 在启用 SSL 的基于 WebAPI 的服务中启用基于令牌的身份验证
- node.js - Strimzi - 连接外部客户端
- xcode - 如何将由 electron-builder 构建的签名 .pkg 上传到 Apple App Store Connect with Xcode 11.0,没有应用程序加载器
- ruby-on-rails - 如何在 Ruby on Rails 中的模型之间创建一对多关系?
- html - Twitter JS文件问题
- r - 如何修复加载 tidyverse 库的错误
- python - SVM Scikit,是否可以像在 LinearSVC 中那样更改 SVC 的惩罚?