Правильное управление ресурсами базы данных: курсор и соединение - PullRequest
0 голосов
/ 21 февраля 2019

Я создаю тестовый API Flask и создал класс 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() после каждого выполнения курсора в функциях, но затем я получаю ошибку при следующем вызове функции, говорящей, что курсорзакрыто, или после того, как я сделаю оператор вставки, он не покажет новое значение, даже если он был правильно вставлен в MySQL.Как узнать, когда закрывать курсор, и как правильно запустить его резервное копирование при каждом вызове метода?

1 Ответ

0 голосов
/ 24 февраля 2019

Это звучит как отличный пример использования Python менеджер контекста .Менеджеры контекста позволяют правильно управлять ресурсами , такими как соединение с базой данных, с помощью , позволяя вам указать, как методы установки и разрыва вашего ресурса должны работать .Вы можете создать свой собственный менеджер контекста одним из двух способов: во-первых, упаковав класс базы данных и реализовав необходимые методы для менеджера контекста: __init__(), __enter__() и __exit__().Во-вторых, используя декоратор @contextmanager для определения функции и создавая генератор для ресурса базы данных в пределах указанного определения функции.Я покажу оба подхода и позволю вам решить, какой из них является вашим предпочтением.Метод __init__() - это метод инициализации для вашего собственного менеджера контекста , аналогичный методу инициализации, используемому для пользовательских классов Python.Метод __enter__() - это ваш код установки для вашего диспетчера пользовательских контекстов.И, наконец, метод __exit()__ - это ваш код teardown для вашего собственного менеджера контекста. Оба подхода используют эти методы с основным отличием в том, что первый метод будет явно указывать эти методы в определении вашего класса.Где, как и во втором подходе, весь код вплоть до оператора 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)

Надеюсь, это поможет!

...