python - 如何防止使用相同值创建新 SQLAlchemy 对象的 UUID 主键

标签 python postgresql sqlalchemy

我有一个 for 循环,它创建一个新的“行”对象,在将对象提交到 Postgres 数据库中的表之前用数据填充属性。我要插入的表(以及对象)采用 UUID 的主键,在第一次提交后所有新创建的行对象中循环的第一次迭代后只有这个值保持不变。

我对解决方案有点迷茫,但是我认为这可能与我处理数据库 session 的方式有关。在撰写本文时,我还注意到我在 invite_usersinvite_user 函数中使用了相同的变量名 (new_user)。虽然 Python 会认为它们在不同的范围内(我认为),但我想知道 SQLALchemy session 是否会这样?

请注意,我已经删除了很多我认为在问题上下文中多余的代码 - 主要是更多的列等。此外,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)

这是我用来创建和销毁 session 的函数:

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

这是将行添加到 session 并尝试提交它的 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

所以我循环遍历字典中的电子邮件地址 (json_dict['emails'])。然后,我传递每封电子邮件以邀请用户以及当前数据库 session。我这样做是为了避免每次 invite_user 调用都创建一个 session ,因为从性能的角度来看,这似乎比在 invite_user 函数中创建一个新的 session 更明智,因为这将导致创建和销毁其中的许多内容。

我认为我的 Column 定义足以处理每次提交 User 行对象时新 UUID 的生成。但是,如果我将多个电子邮件地址传递给 invite_users 函数,第一个用户将获得一个新的 UUID,第二个用户将获得相同的 UUID。如果我给它一个电子邮件地址,一切都很好

我不想依赖查询数据库中的现有行。我完全依靠数据库约束来防止重复,异常处理用于向用户报告错误。

最佳答案

sqlalchemy docs对于 Column 的 default 参数状态:

A scalar, Python callable, or ColumnElement expression representing the default value for this column, which will be invoked upon insert if this column is otherwise not specified in the VALUES clause of the insert.

因此,与其提供将创建标量(常量)值的 uuid.uuid4(),不如仅提供可调用的 uuid.uuid4,以便调用它对于每个插入。

Column Insert/Update Defaults 中有对默认值的进一步讨论。

关于python - 如何防止使用相同值创建新 SQLAlchemy 对象的 UUID 主键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55917056/

相关文章:

python - 软链接(soft link)和硬链接(hard link)

sql - 行向最大值(n 列)- 优雅的方法

mysql - 更新表中的列以等于同一表中的另一列 WHERE NOT 语法

postgresql - PL/pgSQL 匿名代码块

python - 单个表上的 SQLAlchemy 多对多关系错误 : NoReferencedTableError

python - SQLAlchemy 模型变量是类还是对象类型?

Python:用列表中的随机元素填充 Pandas 列中的 'na'

Python - 将两字节字符串作为单字节十六进制字符写入二进制文件

python - 与 SQLAlchemy 关系的 SELECT 条件

python - 如何从 Python 中的字符串列表制作直方图?