正确管理数据库资源:游标和连接

Proper management of database resources: cursor and connection

我正在创建一个测试 Flask API,并创建了一个 Database class 我在主应用程序中使用的。我正在使用 pymysql 访问我的 MySQL 数据库,但我无法确定何时关闭游标和连接。现在我有

import pymysql

class Database:
    def __init__(self):
        host = '127.0.0.1'
        user = 'root'
        password = ''
        db = 'API'

        self.con = pymysql.connect(host=host, user=user, password=password, db=db, cursorclass=pymysql.cursors.DictCursor, autocommit=True)
        self.cur = self.con.cursor()

    def getUser(self, id):
        sql = 'SELECT * from users where id = %d'
        self.cur.execute(sql, (id))
        result = self.cur.fetchall()
        return result

    def getAllUsers(self):
        sql = 'SELECT * from users'
        self.cur.execute(sql)
        result = self.cur.fetchall()
        return result

    def AddUser(self, firstName, lastName, email):
        sql = "INSERT INTO `users` (`firstName`, `lastName`, `email`) VALUES (%s, %s, %s)"
        self.cur.execute(sql, (firstName, lastName, email))

我曾尝试在函数中每次执行游标后添加 self.cur.close()self.con.close(),但在下次调用函数时出现错误,提示游标已关闭,或者在我做了一个插入语句,它不会显示新值,即使它被正确插入 MySQL。我如何知道何时关闭游标,以及如何在每次调用方法时正确启动它?

这听起来像是 python context manager 的一个很好的用例。上下文管理器允许您正确管理资源,例如数据库连接,方法是允许您指定资源的设置和拆卸方法应该如何工作.您可以通过以下两种方式之一创建您自己的自定义上下文管理器:首先,包装您的数据库 class,并为上下文管理器实现所需的方法:__init__()__enter__()__exit__()。其次,通过在函数定义上使用 @contextmanager 装饰器并在所述函数定义内为您的数据库资源创建一个生成器。我将展示这两种方法,让您决定您喜欢哪一种。 __init__() 方法是自定义上下文管理器 的 初始化方法,类似于自定义python 所使用的初始化方法classes。 __enter__() 方法是您的自定义上下文管理器的 设置代码 。最后,__exit()__ 方法是您的自定义上下文管理器的 teardown 代码。 两种方法都使用这些方法,主要区别在于第一种方法将在您的 class 定义中明确说明这些方法。在第二种方法中,生成器的 yield 语句之前的所有代码都是您的 初始化和设置代码 以及 [=21] 之后的所有代码 =] 语句是您的拆卸代码。 我也会考虑将您基于用户的数据库操作提取到用户模型中 class。大致如下:

自定义上下文管理器:(class 基于方法):

import pymysql

class MyDatabase():
    def __init__(self):
        self.host = '127.0.0.1'
        self.user = 'root'
        self.password = ''
        self.db = 'API'

        self.con = None
        self.cur = None

    def __enter__(self):
        # connect to database
        self.con = pymysql.connect(host=self.host, user=self.user, password=self.password, db=self.db, cursorclass=pymysql.cursors.DictCursor, autocommit=True)
        self.cur = self.con.cursor()
        return self.cur

    def __exit__(self, exc_type, exc_val, traceback):
        # params after self are for dealing with exceptions
        self.con.close()

user.py(重构):'

# import your custom context manager created from the step above
# if you called your custom context manager file my_database.py: from my_database import MyDatabase

import <custom_context_manager>

class User:
    def getUser(self, id):
        sql = 'SELECT * from users where id = %d'
        with MyDatabase() as db: 
            db.execute(sql, (id))
            result = db.fetchall()

        return result

    def getAllUsers(self):
        sql = 'SELECT * from users'
        with MyDatabase() as db: 
            db.execute(sql)
            result = db.fetchall()
        return result

    def AddUser(self, firstName, lastName, email):
        sql = "INSERT INTO `users` (`firstName`, `lastName`, `email`) VALUES (%s, %s, %s)"
        with MyDatabase() as db:
            db.execute(sql, (firstName, lastName, email))

上下文管理器(装饰器方法):

from contextlib import contextmanager
import pymysql


@contextmanager
def my_database():
    try:
        host = '127.0.0.1'
        user = 'root'
        password = ''
        db = 'API'
        con = pymysql.connect(host=host, user=user, password=password, db=db, cursorclass=pymysql.cursors.DictCursor, autocommit=True)
        cur = con.cursor()
        yield cur
    finally:
        con.close()

然后在您的 User class 中,您可以通过首先导入文件然后像以前一样使用它来使用上下文管理器:

with my_database() as db:
   sql = <whatever sql stmt you wish to execute>
   #db action 
   db.execute(sql)

希望对您有所帮助!