Python 在另一个脚本上引用数据库连接

Posted

技术标签:

【中文标题】Python 在另一个脚本上引用数据库连接【英文标题】:Python referencing database connection on another script 【发布时间】:2017-09-10 14:16:54 【问题描述】:

我正在深入学习 Python,同时尝试使用存储在 mysql/MariaDB 上的数据创建应用程序,并且我几乎处于可以在项目中取得一些良好进展的阶段。我可以通过 SSH 连接到我的数据库,并从 python 脚本中检索数据,但我现在希望在 GUI 框中显示该数据。我面临的挑战之一是我有两个单独的脚本来处理连接,一个打开一个关闭,我的理论是只有数据访问需要连接。我使用 PyQT5 创建了各种 GUI 和窗口,特别是我希望填充 QtTableWidget。我拥有的脚本目前没有给我任何错误,但它也没有在表格小部件中显示数据。我的预感是它没有正确引用打开连接脚本上的数据库,因此没有数据要传递,但我正在努力确定有效的谷歌搜索所需的术语。

我的 OpenConn.py 如下:

import MySQLdb
from sshtunnel import SSHTunnelForwarder

def Open_Conn():
    with SSHTunnelForwarder(
             ('192.168.0.10', 22),
             ssh_password="xxx",
             ssh_username="xxx",
             remote_bind_address=('localhost', 3306)) as server:

        db = MySQLdb.connect(host='localhost',
                               port=server.local_bind_port,
                               user='xxx',
                               passwd='xxx',
                               db='DBNAME')

        cursor = db.cursor()
if __name__ == '__main__':
    Open_Conn()

而我的main.py如下:

from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from ViewClientsUI import Ui_ViewClients
from OpenConn import Open_Conn

class ViewClientsWindow(QtWidgets.QDialog, Ui_ViewClients):
    def __init__(self):
        super(ViewClientsWindow, self).__init__()
        self._new_window = None
        self.setupUi(self)

    def data_load():
        with OpenConn.Open_Conn:
            connection = OpenConn.Open_Conn()
            query = "SELECT * FROM Clients"
            result = connection.execute(query)
            self.tableWidget.setRowCount(0)
            for row_number, row_data in enumerate(result):
                self.tableWidget.insertRow(row_number)
                for column_number, data in enumerate(row_data):
                    self.tableWidget.setItem(row_number, column_number, QtWidgets.QTableWidgetItem(str(data)))

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    gui = ViewClientsWindow()
    gui.show()
    app.exec_()
    Open_Conn()

谁能帮助我确定为什么我没有在表格小部件中获取数据? 非常感谢提前

【问题讨论】:

代码有点乱,app.exec_()之后再调用Open_Conn()是没有意义的。然后,您导入Open_Conn,但您在从未调用过的data_load() 中调用OpenConn.Open_Conn,并且会抛出错误(参数中没有self)。顺便说一句,如果您使用上下文管理器 (with statement),连接会自动关闭。 我知道这很令人困惑,我远不是一个经验丰富的程序员:/我想我明白你在说什么,并且建议在 'app.exec_( )' 与 'data_load()' ? RE:上下文管理器,这是否意味着我不需要调用 CloseConn,因为一旦“with”完成,它将关闭连接? 刚刚尝试切换,我收到一个错误,提示“data_load”未定义 是的,关于 CloseConn:doc。那么也许你可以修复data_load 并在__init__ 方法中调用它? (在类内用self.data_load() 调用它,或在类外用gui.data_load() 调用它)并删除Open_Conn() 但是您想要打开连接的方式使连接仅在函数Open_Conn() 内打开,您将无法通过调用函数将它与上下文管理器一起使用。您的模式可能需要一个函数来打开和另一个函数来关闭,但没有上下文管理器 【参考方案1】:

功能方式:

这种方式显示了您需要设置的函数才能在另一个模块中调用它们。我删除了不能与此功能模式一起使用的上下文管理器,因为它在函数 Open_Conn 的末尾被关闭。所以open_conn 函数创建了一个server 对象和数据库对象db,它们将在close_conn 中被调用以在必要时关闭。

#OpenConn.py
import MySQLdb
from sshtunnel import SSHTunnelForwarder

def open_conn():
    server = SSHTunnelForwarder(
         ('192.168.0.10', 22),
         ssh_password="xxx",
         ssh_username="xxx",
         remote_bind_address=('localhost', 3306))

    server.start()
    print('opening server : OK')

    db = MySQLdb.connect(host='localhost',
                         port=server.local_bind_port,
                         user='xxx',
                         passwd='xxx',
                         db='DBNAME')
    print('opening database : OK')
    return (server, db)

def close_conn(server, db):
    db.close()
    server.stop()
    print('closing connection : OK')

from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from ViewClientsUI import Ui_ViewClients
from OpenConn import open_conn, close_conn

class ViewClientsWindow(QtWidgets.QDialog, Ui_ViewClients):
    def __init__(self):
        super(ViewClientsWindow, self).__init__()
        self._new_window = None
        self.setupUi(self)
        self.data_load()

    def data_load(self):
        server, db = open_conn()
        cursor = db.cursor()
        query = "SELECT * FROM Clients"
        cursor.execute(query)
        results = cursor.fetchall()
        self.tableWidget.setRowCount(0)
        for row_number, row_data in enumerate(results):
            self.tableWidget.insertRow(row_number)
            for column_number, data in enumerate(row_data):
                self.tableWidget.setItem(row_number, column_number, QtWidgets.QTableWidgetItem(str(data)))
        close_conn(server, db)

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    gui = ViewClientsWindow()
    gui.show()
    sys.exit(app.exec_())

上下文管理器方式:

可以通过使用上下文管理器类自动处理打开和关闭部分来改进功能模式。管理器只能返回db.cursor 来执行查询,服务器留在管理器内。要获取cursor,您可以使用 as 捕获上下文管理器在方法 __enter__ 中返回的值:with OpenManager() as cursor:

要创建它,基本上,您可以将 opening 代码移动到方法 __enter__(在调用上下文管理器时执行)和其中的 closure 部分方法__exit__(在with statement块的末尾调用)

#OpenConn.py
import MySQLdb
from sshtunnel import SSHTunnelForwarder

class OpenManager(object):
    def __init__(self):
        self.server =None
        self.db = None
        # here you could define some parameters and call them next

    def __enter__(self):
        self.server = SSHTunnelForwarder(
             ('192.168.0.10', 22),
             ssh_password="xxx",
             ssh_username="xxx",
             remote_bind_address=('localhost', 3306))
        self.server.start()
        print('opening server : OK')

        self.db = MySQLdb.connect(host='localhost',
                             port=self.server.local_bind_port,
                             user='xxx',
                             passwd='xxx',
                             db='DBNAME')
        print('opening database : OK')

        return self.db.cursor() # 

    def __exit__(self, type, value, traceback):
        self.db.close()
        self.server.stop()
        print('closing connection : OK')

此模式允许您在小部件中调用上下文管理器,在 with statement 中,如下所示:

from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from ViewClientsUI import Ui_ViewClients
from OpenConn import OpenManager

class ViewClientsWindow(QtWidgets.QDialog, Ui_ViewClients):
    def __init__(self):
        super(ViewClientsWindow, self).__init__()
        self._new_window = None
        self.setupUi(self)
        self.data_load()

    def data_load(self):
        with OpenManager() as cursor:  
            query = "SELECT * FROM Clients"
            cursor.execute(query)
            results = cursor.fetchall()
            self.tableWidget.setRowCount(0)
            for row_number, row_data in enumerate(results):
                self.tableWidget.insertRow(row_number)
                for column_number, data in enumerate(row_data):
                    self.tableWidget.setItem(row_number, column_number, QtWidgets.QTableWidgetItem(str(data)))


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    gui = ViewClientsWindow()
    gui.show()
    sys.exit(app.exec_())

您也可以直接在小部件中创建与SSHTunnelForwarder 的连接以避免这种情况,并使用类提供的上下文管理器,然后在内部创建数据库连接。

上面显示的自定义类只是在一个上下文中混合与服务器和数据库的连接的一种方式,如果您在代码中的许多地方都需要这些连接,则很容易。

【讨论】:

@JusticeEmpire 我刚刚更正了答案中的错别字,我希望你没有因为它们而被卡住 嗨,对不起,它在工作中很生气。我还没有机会查看上下文管理器选项,但是第一个“功能”选项需要进行一些调整,例如拼写错误,并引用表格小部件。除此之外,我有一个单独的问题试图获取列数据,但我会将这个问题标记为已回答,如果我卡在列数据上,我会发布另一个问题。非常感谢您的帮助@PRMoureu 感谢您的反馈!不要担心上下文管理器,它可能有点想多了……我希望你从第一个解决方案中得到了主要思想。如果您遇到的问题与我提供的有关,请告诉我如何提供帮助,否则可能值得提出另一个问题 ***.com/questions/46222391/qt5-qtableview-with-qtsql我已经发布了另一个问题,有什么想法吗?

以上是关于Python 在另一个脚本上引用数据库连接的主要内容,如果未能解决你的问题,请参考以下文章

Android UI 在 JDBC 连接上挂起 - 即使连接在另一个线程上

停止在另一个 Python 脚本中运行的一个 Python 脚本

CISCO连接上了,但是上不了网,在另一台电脑都可以上,求大神

我在 docker 容器中有 mongo db,我想连接在另一个容器上运行的应用程序

如何在连接到局域网的其他电脑上使用SQL Server数据库运行桌面应用程序?

mssql可以远程连接吗?