Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/283.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Python(Flask)应用程序中捕获psycopg2.errors.UniqueViolation错误?_Python_Sqlalchemy - Fatal编程技术网

如何在Python(Flask)应用程序中捕获psycopg2.errors.UniqueViolation错误?

如何在Python(Flask)应用程序中捕获psycopg2.errors.UniqueViolation错误?,python,sqlalchemy,Python,Sqlalchemy,我有一个小型Python web应用程序(用Flask编写),它使用sqlalchemy将数据持久化到数据库中。当我尝试插入重复的行时,会引发异常,如下所示: (psycopg2.errors.UniqueViolation) duplicate key value violates unique constraint "uix_my_column" 我想包装异常并重新引发我自己的异常,这样我就可以添加特定于该特定错误的日志和消息。这是我尝试的(简化): 从db导入DbApi 从my_例外导入

我有一个小型Python web应用程序(用Flask编写),它使用sqlalchemy将数据持久化到数据库中。当我尝试插入重复的行时,会引发异常,如下所示:

(psycopg2.errors.UniqueViolation) duplicate key value violates unique constraint "uix_my_column"
我想包装异常并重新引发我自己的异常,这样我就可以添加特定于该特定错误的日志和消息。这是我尝试的(简化):

从db导入DbApi
从my_例外导入请求
从psycopg2.errors导入UniqueViolation#MyRecord:
尝试:
self.db.session\u local(expire\u on\u commit=False)作为会话:
my_rec=MyRecord(**数据)
会话.添加(我的记录)
session.commit()
会话.刷新(我的记录)
归还我的记录
除作为e的唯一违反外:
raise BADDREQUEST('已存在重复记录')
但这无法捕获错误,因为
psycopg2.errors.UniqueViolation
实际上不是类名(!)

在PHP中,这与捕获复制/粘贴异常的类名一样简单,但在Python中,这要模糊得多

有一个类似的问题,但它没有处理这个特定的用例,而且(重要的是),它没有澄清如何识别根异常类名

如何发现实际提出的异常?为什么Python会隐藏这一点?

根据psycopg2:

根据DB API 2.0,模块通过以下例外情况提供有关错误的信息:

异常psycopg2.错误

异常,它是所有其他错误异常的基类。您可以使用它来捕获一条except语句中的所有错误。警告不被视为错误,因此不使用此类作为基础。它是PythonStandardError的一个子类(Python3上的例外)

因此,捕捉异常的正确方法是:

试试看:
#你的东西在这里
除psycopg2外。错误为e:
#获取错误代码
错误=e.pgcode
#然后做点什么。

特别是您的错误23505根据

您在问题中发布的错误不是提出的错误。完整的错误消息是:

sqlalchemy.exc.IntegrityError:(psycopg2.errors.UniqueViolation)重复键值违反唯一约束“model\u name\u key”
关键部分是SQLAlchemy错误,您出于某种原因选择忽略它。SQLAlchemy捕获原始错误,将其封装在自己的错误中并引发该错误

但在Python中,这是更加模糊的。。。为什么Python会隐藏这一点

这不是混淆,没有任何东西是隐藏的,行为是文档化的,特定于您正在使用的框架,并且不是由Python语言强制执行的。SQLAlchemy是一个抽象库,如果它引发特定于底层dpapi适配器的异常,它将显著降低其中编写的代码的可移植性

从:

SQLAlchemy不会直接生成这些异常。相反,他们 从数据库驱动程序截取并由 SQLAlchemy提供了异常DBAPIError,但是 异常是由驱动程序生成的,而不是SQLAlchemy

dbapi层引发的异常被包装在的子类中,注意:

包装异常对象在
orig
属性中可用

因此,捕获SQLAlchemy异常并检查原始异常非常简单,正如您所期望的那样,原始异常是
psycopg2.errors.UniqueViolation
的一个实例。但是,除非您的错误处理非常特定于dbapi层提出的类型,否则我建议检查底层类型可能是不必要的,因为提出的SQLAlchemy异常将提供足够的运行时信息来执行您必须执行的操作

下面是一个示例脚本,它会引发
sqlalchemy.exc.IntegrityError
,捕获它,通过
orig
属性检查底层异常,并引发另一个本地定义的异常

从sqlalchemy导入创建引擎、列、整数、字符串
从sqlalchemy.exc导入IntegrityError
从sqlalchemy.ext.declarative导入声明性基础
从sqlalchemy.orm导入sessionmaker
从psycopg2.errors导入唯一冲突
引擎=创建引擎(“postgresql+psycopg2://某些用户:mysecretpassword@localhost:5432/某些用户)
Base=声明性_Base()
会话=会话生成器(绑定=引擎)
类请求(异常):
通过
类别模型(基本):
__tablename_=“model”
id=列(整数,主键=True)
名称=列(字符串,unique=True)
如果名称=“\uuuuu main\uuuuuuuu”:
Base.metadata.drop_all(引擎)
Base.metadata.create_all(引擎)
s=会话()
s、 添加(型号(name=“a”))
s、 提交()
s、 添加(型号(name=“a”))
尝试:
s、 提交()
除完整性错误外,如e:
assert isinstance(例如orig、UniqueViolation)#证明原始异常
向e提出请求
这引起了:

sqlalchemy.exc.IntegrityError:(psycopg2.errors.UniqueViolation)重复键值违反唯一约束“model\u name\u key”
详细信息:键(名称)=(a)已存在。
[SQL:插入模型(名称)值(%(名称)s)返回model.id]
[参数:{'name':'a'}]
(有关此错误的背景信息,请访问:http://sqlalche.me/e/gkpj)
上述异常是以下异常的直接原因:
回溯(最近一次呼叫最后一次):
文件“\main.py”,第36行,在
向e提出请求
__主错误请求
从db导入DbApi
从my_例外导入请求
从psycopg2导入错误
类别MyClass:
定义初始化(自):
self.db=DbApi()
def create(self,data:dict)->MyRecord:
from db import DbApi
from my_exceptions import BadRequest
from psycopg2.errors import UniqueViolation # <-- this does not exist!

class MyClass:
    def __init__(self):
        self.db = DbApi() 

    def create(self, data: dict) -> MyRecord:
        try:
            with self.db.session_local(expire_on_commit=False) as session:
                my_rec = MyRecord(**data)
                session.add(my_rec)
                session.commit()
                session.refresh(my_rec)
                return my_rec
        except UniqueViolation as e:
            raise BadRequest('A duplicate record already exists')
   import traceback   # Used for printing the full traceback | Better for debug.
   from psycopg2 import errors

   UniqueViolation = errors.lookup('23505')  # Correct way to Import the psycopg2 errors

   # ...... Code ....

    try:
        db.commit()           
    except UniqueViolation as err:
        traceback.print_exc()
        db.rollback()            

    # ...... Code ....
   import traceback   # Used for printing the full traceback | Better for debug.
   from psycopg2._psycopg import IntegrityError

   # ...... Code ....

    try:
        db.commit()           
    except IntegrityError as err:
        traceback.print_exc()
        db.rollback()            

    # ...... Code ....
    #
    # NOTE: the exceptions are injected into this module by the C extention.
    #
    
    
    def lookup(code):
        """Lookup an error code and return its exception class.
    
        Raise `!KeyError` if the code is not found.
        """
        from psycopg2._psycopg import sqlstate_errors   # avoid circular import
        return sqlstate_errors[code]
    sqlstate_errors = {
        '02000': None, # (!) real value is "<class 'psycopg2.errors.NoData'>"
        '02001': None, # (!) real value is "<class 'psycopg2.errors.NoAdditionalDynamicResultSetsReturned'>"
        '03000': None, # (!) real value is "<class 'psycopg2.errors.SqlStatementNotYetComplete'>"
        '08000': None, # (!) real value is "<class 'psycopg2.errors.ConnectionException'>"
        '08001': None, # (!) real value is "<class 'psycopg2.errors.SqlclientUnableToEstablishSqlconnection'>"
        '08003': None, # (!) real value is "<class 'psycopg2.errors.ConnectionDoesNotExist'>"
        '08004': None, # (!) real value is "<class 'psycopg2.errors.SqlserverRejectedEstablishmentOfSqlconnection'>"
        '08006': None, # (!) real value is "<class 'psycopg2.errors.ConnectionFailure'>"
        '08007': None, # (!) real value is "<class 'psycopg2.errors.TransactionResolutionUnknown'>"
        '08P01': None, # (!) real value is "<class 'psycopg2.errors.ProtocolViolation'>"
 # -------- Lots of lines ---------- # 
        '23503': None, # (!) real value is "<class 'psycopg2.errors.ForeignKeyViolation'>"
        # There you are!!!
        '23505': None, # (!) real value is "<class 'psycopg2.errors.UniqueViolation'>" 
        # ---------------- 
        '23514': None, # (!) real value is "<class 'psycopg2.errors.CheckViolation'>"
        '23P01': None, # (!) real value is "<class 'psycopg2.errors.ExclusionViolation'>"