如何在SQLAlchemy,flask,pyhon中处理唯一数据

时间:2018-08-29 10:50:46

标签: python sqlite flask sqlalchemy unique

通常如何在Flask中处理唯一的数据库条目?我的数据库模型中有以下列:

bank_address = db.Column(db.String(42), unique=True)

问题是,即使在我无法检查它是否已经存在于数据库中之前,我也会收到错误消息:

检查它是否唯一,然后将其写入db:

if request.method == 'POST':
    if user.bank_address != request.form['bank_address_field']:
        user.bank_address = request.form['bank_address_field']
        db.session.add(user)
        db.session.commit()

我得到的错误:

  

sqlalchemy.exc.IntegrityError:(sqlite3.IntegrityError)唯一   约束失败:user.bank_address_field [SQL:'UPDATE user SET   bank_address_field =?在哪里user.id =?']

2 个答案:

答案 0 :(得分:6)

您可以执行以下两项操作之一:

  • 对具有该字段的用户进行查询:

    if User.query.filter(User.bank_address == request.form['bank_address_field']).first():
        # error, there already is a user using this bank address
    

    这是一个大问题,但是,请参见下文。

  • 捕获异常:

    from sqlalchemy.exc import IntegrityError
    
    try:
        db.session.commit()
    except IntegrityError:
        db.session.rollback()
        # error, there already is a user using this bank address or other
        # constraint failed
    

    其中IntegrityError可从sqlalchemy.exc导入。一旦引发IntegrityError,无论您是否捕获到该错误,您正在使用的会话都会失效。要继续使用该会话,您需要发出db.session.rollback()

后者更好,因为它不受比赛条件的限制。想象两个用户试图同时注册相同的银行地址:

  • 用户A提交,User.query.filter().first()返回None,因为还没有人使用该地址。
  • 用户B几乎同时提交,User.query.filter().first()返回None,因为还没有人使用该地址。
  • 用户A的银行地址已成功写入数据库
  • 用户B的银行地址无法写入数据库,因为完整性检查失败,因为用户A刚刚记录了该地址。

所以只是捕获异常,因为数据库事务保证在测试约束以及添加或更新用户之前,数据库首先锁定表。

您也可以将整个表锁定在Flask中,但是Python与数据库的通信要慢得多。如果站点繁忙,则不希望数据库更新缓慢,最终将导致许多用户等待锁被清除。您希望将锁定保持在最小范围内,并且越短越好,并且越接近锁定的实际数据,就越早可以再次释放锁定。数据库非常擅长于这种锁定,并且(非常自然地)非常接近其数据,因此将锁定保留在数据库上,而是依靠异常。

答案 1 :(得分:2)

您不应该捕获并取消 IntegrityError,因为它可以在其他类型的约束失败时引发 - 例如外键约束。

如今,有更好的方法来处理此错误。 SQLite 和 PostgreSQL 都支持 ON CONFLICT DO NOTHINGON CONFLICT DO UPDATE。以下是它们各自的 SQLAlchemy 文档:

不要使用 session.add(),而是使用 SQLAlchemy 方言中的 insert() 函数。它应该大致如下:

# if you are using SQLite
from sqlalchemy.dialects.sqlite import insert

# if you are using PostgreSQL
from sqlalchemy.dialects.postgresql import insert

values = dict() # your values here

stmt = (
    insert(User)
    .values(**values)
    .on_conflict_do_nothing(index_elements=[User.bank_address])
)

db.session.execute(stmt)