Менеджер контекста для Python MySQLdb - PullRequest
13 голосов
/ 09 ноября 2011

Я привык (избалован?) Интерфейсом Python SQLite для работы с базами данных SQL.Одна приятная особенность API SQLite в Python - «менеджер контекста», то есть оператор Python with.Обычно я выполняю запросы следующим образом:

import as sqlite

with sqlite.connect(db_filename) as conn:
    query = "INSERT OR IGNORE INTO shapes VALUES (?,?);"
    results = conn.execute(query, ("ID1","triangle"))

Используя приведенный выше код, если мой запрос изменяет базу данных, и я забываю запустить conn.commit(), диспетчер контекста запускает его для меня автоматически при выходе из with заявление.Он также хорошо обрабатывает исключения: если исключение возникает до того, как я что-то зафиксировал, тогда база данных откатывается.

Сейчас я использую интерфейс MySQLdb, который, похоже, не поддерживает аналогичный менеджер контекста.из коробки.Как мне создать свой собственный?Здесь есть связанный вопрос здесь , но он не предлагает полного решения.

Ответы [ 2 ]

19 голосов
/ 10 ноября 2011

Ранее , соединения MySQLdb были контекстными менеджерами. Начиная с этот коммит 2018-12-04 , однако соединения MySQLdb больше не являются контекстными менеджерами, и пользователи должны явно вызвать conn.commit () или conn.rollback () или написать свой собственный менеджер контекста, такой как приведенный ниже.


Вы можете использовать что-то вроде этого:

import config
import MySQLdb
import MySQLdb.cursors as mc
import _mysql_exceptions
import contextlib
DictCursor = mc.DictCursor
SSCursor = mc.SSCursor
SSDictCursor = mc.SSDictCursor
Cursor = mc.Cursor

@contextlib.contextmanager
def connection(cursorclass=Cursor,
               host=config.HOST, user=config.USER,
               passwd=config.PASS, dbname=config.MYDB,
               driver=MySQLdb):
    connection = driver.connect(
            host=host, user=user, passwd=passwd, db=dbname,
            cursorclass=cursorclass)
    try:
        yield connection
    except Exception:
        connection.rollback()
        raise
    else:
        connection.commit()
    finally:
        connection.close()

@contextlib.contextmanager
def cursor(cursorclass=Cursor, host=config.HOST, user=config.USER,
           passwd=config.PASS, dbname=config.MYDB):
    with connection(cursorclass, host, user, passwd, dbname) as conn:
        cursor = conn.cursor()
        try:
            yield cursor
        finally:
            cursor.close()


with cursor(SSDictCursor) as cur:
    print(cur)
    connection = cur.connection
    print(connection)
    sql = 'select * from table'
    cur.execute(sql)
    for row in cur:
        print(row)

Чтобы использовать его, вы должны поместить config.py в PYTHONPATH и определить там переменные HOST, USER, PASS, MYDB.

18 голосов
/ 03 апреля 2014

Думаю, что все изменилось с тех пор, как этот вопрос был задан изначально. Несколько сбивает с толку (по крайней мере, с моей точки зрения), для последних версий MySQLdb, если вы используете соединение в контексте, вы получаете курсор (согласно примеру oursql), а не то, что закрывается автоматически (как вы например, если вы открыли файл).

Вот что я делаю:

from contextlib import closing
with closing(getConnection()) as conn: #ensure that the connection is closed
    with conn as cursor:               #cursor will now auto-commit
        cursor.execute('SELECT * FROM tablename')
...