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

时间:2019-02-21 00:13:50

标签: mysql python-3.x flask flask-restful pymysql

我正在创建一个测试Flask API,并创建了一个Database类,该类可以从主应用程序中使用。我正在使用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(),但是下次我调用函数说游标已关闭时,或者在执行完游标后,我得到一个错误一个insert语句,即使它已正确插入MySQL,它也不会显示新值。我如何知道何时关闭游标,以及如何在每次调用方法时正确启动游标?

1 个答案:

答案 0 :(得分:1)

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

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

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类中,您可以使用上下文管理器,方法是先导入文件,然后像以前一样使用它:

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

希望有帮助!