【问题标题】:Calling database object from different python modules从不同的python模块调用数据库对象
【发布时间】:2018-04-16 03:39:18
【问题描述】:

我在 Python 2.7 中使用 MySQLdb 模块和 MariaDB 数据库。我想知道如何在多个模块中使用数据库的正确方法。我有这个例子,我尝试将另一个函数中的元素插入数据库。我收到OperationalError: (1046, 'No database selected')

编辑:这是更正后的代码(12)。谢谢。知道我想要一个像这样的带有依赖注入的工作示例。

这是我要在其中处理数据库的模块。

# test.py

import MySQLdb
import traceback
import test2 as insert_module
from warnings import filterwarnings

# Ignore MySQL `table already exists` warnings
filterwarnings ('ignore', category = MySQLdb.Warning)

# Create database object to keep it open as global variable
class  MyDB (object):
    connection = None
    cursor = None

    # Open database connection
    def  connect (self):
        self.connection = MySQLdb.connect ()
        self.cursor = self.connection.cursor ()

    def  execute (self, sql, *args):
        try:
            self.cursor.execute (sql, *args)
            self.connection.commit ()
        except  MySQLdb.Error, e:
            print (traceback.format_exc())
            self.connection.rollback ()

    def  query (self, sql, *args):
        try:
            self.execute (sql, *args)
        # Reopen database connection
        except  ( AttributeError, MySQLdb.OperationalError ):
            self.connect ()
            self.execute (sql, *args)

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

db = MyDB ()

def  createDatabase ():
    db.query ("CREATE DATABASE IF NOT EXISTS Product")
    db.query ("USE Product")
    sql = """CREATE TABLE IF NOT EXISTS `ProductColor` (
                `ProductID` VARCHAR(20) PRIMARY KEY,
                `ColorID` VARCHAR(20)
            )"""
    db.query (sql)

def  insertProductColor (*args):
    sql = """INSERT INTO `ProductColor` VALUE (%s, %s)"""
    db.query (sql, args)

def  main ():
    createDatabase ()
    insert_module.processListOfProductColors ()

if __name__ == "__main__":
    main ()
    db.disconnect ()

这是我要从中插入产品的模块。

#test2.py

import test as db_module

def  processListOfProductColors ():
    db_module.insertProductColor ("cup", "blue")

【问题讨论】:

    标签: python mysql database mariadb


    【解决方案1】:

    理想情况下,您应该通过依赖注入将您的 DB 对象传递到 insert_module - 因为它看起来不像您已经设置了这样的代码,您可以(作为快速破解,不是最好的解决方案)做:

    insert_module.processListOfProductColors (self.db)

    然后在processListOfProductColors(db) 内部使用db.insertProductColor - 而不是db_module.insert...

    再一次,据我所知,这不是最好的解决方案没有一些重构

    更新

    这是一个依赖注入的通用示例,您可以根据自己的使用进行调整:

    # note that each class can be in a different module (file), 
    # just make sure you import 
    
    class db_class(object):
        def __init__(self):
            self.db = create_db()
    
        @staticmethod
        def create_db():
            db = # create the db
            return db
    
    class main(object):
        db = db_class()
        first_class = first_class(db) # dependency injection part 1
        first_class.whatever()
        # you can pass db to however many classes you want now 
    
    class first_class(object):
        def __init__(self, db):
            self._db = db # dependency injection part 2
    
        def whatever(self):
            # do whatever with self._db
    

    【讨论】:

    • 您能否展示一个正确的工作示例?
    【解决方案2】:

    大部分不相关,但:

    # Create database object to keep it open as global variable
    class  MyDB (object):
        connection = None
        cursor = None
    

    那些类属性是无用的 - 将它们定义为实例属性:

        def __init__(self):
            self.connection = None
            self.cursor = None
    

    当我们这样做时:不要对所有查询重复使用相同的游标。否则,在迭代第一个查询的结果时尝试执行另一个查询时会遇到麻烦(我们不讨论多线程......)

        # Open database connection
        def  connect (self):
            self.connection = MySQLdb.connect ()
    

    您当然应该检查您是否已经打开了连接,如果是,请先关闭它或跳过MySQLdb.connect() 调用。

    此外,FWIW,您必须将连接信息(用户名、密码、数据库名称等)传递给 MySQLdb.connect() 调用。您的 OperationalError 来自未在此处传递数据库名称。

            self.cursor = self.connection.cursor ()
    

    cf 上面:这只会给你带来麻烦。每次使用一个新游标,这就是 dbapi 的预期用途。

        def  execute (self, sql, *args):
            try:
                self.cursor.execute (sql, *args)
    

    上图

                self.connection.commit ()
    

    您应该为调用者提供一种在每次调用后不提交的方法 - 他可能希望在单个事务中执行多个语句(实际上这是进行事务的一部分......)

            except  MySQLdb.Error, e:
    

    你想要的:

            except  MySQLdb.Error as e:
    
                print (traceback.format_exc())
    

    设置一个记录器(python 的 logging 模块)并改为调用 logger.exception(...)

                self.connection.rollback ()
    

    和?假装没事???您显然想在这里重新提出异常。

        def  query (self, sql, *args):
            try:
                self.execute (sql, *args)
    

    您确定要为读取查询提交事务吗???

            # Reopen database connection
            except  ( AttributeError, MySQLdb.OperationalError ):
                self.connect ()
                self.execute (sql, *args)
    

    Err... 如果关键是懒惰地处理连接,那么你失败了。请查看 python 对计算属性的支持(property 等)。

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

    更一般地说:尝试将连接部分包装在可以优雅(并最终懒惰地)处理连接问题的东西中可能是一个好主意(但这也不是那么容易......),但无论如何让调用者处理手动光标和事务。这里的解决方案可能是使execute()query() context managers 关闭游标并提交(或回滚)事务。还要记住,糟糕的异常处理比没有异常处理更糟糕......

    【讨论】:

    • 我想看看正确的课程。我需要返回一些值来了解select 是否找到了一个元素以及query 是否成功。 Here is the changed code。我已经向异常处理程序添加了引发,但我不知道如何处理它们。我添加了一个提交方法,以便调用者可以手动处理光标和事务。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-15
    • 1970-01-01
    • 2017-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-12
    相关资源
    最近更新 更多