在Python中使用`with`时,如何处理数据库类中的连接错误?

问题描述 投票:0回答:2

在以下Database类中,如果连接失败,则会打印出错误。但是,还会打印系统错误,因为__exit__代码会触发。

AttributeError:'Database'对象没有属性'_conn'

我应该如何处理这种情况? try/except还是有更好的方法吗?

import psycopg2
import mysecrets as ms
from time import time


class Database(object):
    def __init__(self, localhost=False):
        try:
            print(f"ATTEMPTING DATABASE CONNECTION")
            self._conn = psycopg2.connect(
                f"""
                dbname =    {ms.LOCALDB_NAME if localhost else ms.DB_NAME}
                port =      {ms.LOCALDB_PORT if localhost else ms.DB_PORT}
                user =      {ms.LOCALDB_USER if localhost else ms.DB_USER}
                password =  {ms.LOCALDB_PASS if localhost else ms.DB_PASS}
                host =      {ms.LOCALDB_HOST if localhost else ms.DB_HOST}
                """
            )
            self._cursor = self._conn.cursor()
            print(f"CONNECTED TO DATABASE\n")
        except Exception as error:
            print(f"UNABLE TO CONNECT TO DATABASE\n{error}")

    def __enter__(self):
        return self

    def __del__(self):
        self.connection.close()

    def __exit__(self, exc_type, exc_value, traceback):
        self.connection.close()

    @property
    def connection(self):
        return self._conn

    @property
    def cursor(self):
        return self._cursor

    def commit(self):
        self.connection.commit()

    def query(self, sql, params=None):
        self.cursor.execute(sql, params or ())

    def fetchall(self):
        return self.cursor.fetchall()

    def fetchone(self):
        return self.cursor.fetchone()


if __name__ == "__main__":
    start_time = time()

    with Database() as db:
        elapsed_time = round(time() - start_time, 2)
        print(f"Testing database connection took {elapsed_time} seconds.")
python-3.x class psycopg2
2个回答
2
投票

在这种情况下,您可以在self._conn之前首先初始化try,作为self._conn = None,这使它存在。然后,在__exit__close等保护它

if self._conn is not None:
    self._conn.close()
    self._conn = None

但是,我不认为这是一个很好的设计捕获并忽略一般Exception错误。它可能不是连接错误。即使它是,你最终得到一个未初始化的对象。这没用,会导致以后的错误。最好早点抓住它们,让它只是从__init__传递出来,并让任何实例化它处理它。


1
投票

首先,捕捉Exception而不是特定的错误是反模式。在Python中,您应该知道从函数中抛出哪些异常并相应地处理它们。

其次,如果你对psycopg2.connect_conn.cursor()的调用抛出任何错误,那么你的代码会捕获异常,你的实例变量_conn_cursor永远不会被设置。这是你的AttributeError的原因。

IMO,处理它的正确方法是引发错误,如果数据库类无法连接到数据库或者让任何代码使用此类处理错误,则不会继续执行。

class Database(object):
    def __init__(self, localhost=False):
        try:
            print(f"ATTEMPTING DATABASE CONNECTION")
            self._conn = psycopg2.connect(
                f"""
                dbname =    {ms.LOCALDB_NAME if localhost else ms.DB_NAME}
                port =      {ms.LOCALDB_PORT if localhost else ms.DB_PORT}
                user =      {ms.LOCALDB_USER if localhost else ms.DB_USER}
                password =  {ms.LOCALDB_PASSWORD if localhost else ms.DB_PASSWORD}
                host =      {ms.LOCALDB_HOST if localhost else ms.DB_HOST}
                """
            )
            self._cursor = self._conn.cursor()
            print(f"CONNECTED TO DATABASE\n")
        except psycopg2.Error as error:
            raise ValueError(f"UNABLE TO CONNECT TO DATABASE\n{error}") from None

可能有一个比ValueError更好的例外。

© www.soinside.com 2019 - 2024. All rights reserved.