为什么我收到的是 sqlalchemy.exc.ProgrammingError 而不是 sqlalchemy.exc.IntegrityError?

4
我正在编写一些账户创建代码,并尝试捕获特定的SQLAlchemy异常,以便在用户注册一个已经与现有账户关联的电子邮件时提供适当的错误消息。
我原本期望会出现IntegrityError,但实际上却出现了ProgrammingError。虽然我可以愉快地捕获ProgrammingError,但我试图理解为什么没有得到我所期望的结果。
为了清晰起见,我缩减了模型和代码,但模型看起来像这样:
from service import db
from sqlalchemy import Index

class UserProfile(db.Model):
user_id = db.Column(db.String(255), nullable=False, primary_key=True)
email = db.Column(db.String(255), nullable=False)

def __init__(self, account_data):
    self.user_id = account_data['userId']
    self.email = account_data['email'].lower()

def __repr__(self):
    return 'UserID-{}, Email-{}'.format(self.user_id,self.email)                                                                            

Index('idx_email', UserProfile.email, unique=True)

主要代码如下:

@app.route('/create_account', methods=['POST'])
def create_account():

account_data = request.get_json()

account_details = UserProfile(account_data)
try:
    db.session.add(account_details)
    db.session.flush()

    # do stuff

    db.session.commit()

except ProgrammingError as err:
    db.session.rollback()
    if "duplicate key value violates unique constraint \"idx_email\"" in str(err):
        LOGGER.error('Email address already in use!'

所以,如果我发布一些JSON,例如:
{
  "userId": "Fred", 
  "email": "a@b.com"
}

然后再用不同的userId但相同的电子邮件地址重新发布:

{
  "userId": "Bob", 
  "email": "a@b.com"
}

我期望第二个帖子会引发IntegrityError,但我看到它引发了ProgrammingError:
sqlalchemy.exc.ProgrammingError: (pg8000.core.ProgrammingError)
('ERROR', 
 '23505', 
 'duplicate key value violates unique constraint "idx_email"', 
 'Key (email)=(a@b.com) already exists.', 
 'public', 
 'user_profile', 
 'idx_email',  
 'nbtinsert.c', 
 '406', 
 '_bt_check_unique', '', '') 
[SQL: 'INSERT INTO user_profile (user_id, email) VALUES (%s, %s)']
[parameters: ('Bob', 'a@b.com')]

我错过了什么?

1个回答

5
很不幸,当涉及到DBAPI错误时,SQLAlchemy仅仅包装了底层dbapi兼容库引发的异常。
也就是说,SQLAlchemy之所以特别抛出ProgrammingError,只是因为pg8000选择抛出ProgrammingError
如果您使用psycopg2来管理底层连接,那么您的错误将会表现为IntegrityError(如预期)。
根据pg8000文档,它永远不会引发IntegrityError。实际上,它不会引发以下任何一种:
  • IntegrityError;
  • DataError;
  • DatabaseError;
  • OperationalError;
这里的教训是,在涉及到数据库级别的错误时,您无法保证在不同的dbapi连接器中SQLAlchemy将抛出的类型。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接