为什么我们需要 MySQL 数据库?
但首先,让我们讨论一下我们“为什么”需要 MySQL 数据库。当我们需要将数据永久存储在某个地方时,就需要数据库。较大的应用程序(如 Reporting Software、Graphing 应用程序等)需要一些位置来存储相关数据,以便以后可以从中检索数据。
一种常见的替代方法是使用 “text files” 来存储数据。尽管文本文件方法更简单(在短期内),但数据库有几个优点。
- 可扩展性
- 性能
- 安全
- 备份
先决条件
我们假设您已经有一个有效的 MySQL 安装设置。请务必记住在安装过程中使用的用户名和密码。别忘了。
安装 MySQL 后,我们需要做的下一件事是在 Python 中安装“mysql-connector-python”库。
pip show mysql-connector-python
此库将允许我们从 Python 代码连接 MySQL 数据库并执行 SQL 查询。
初始化 Tkinter 项目的 MySQL 连接
由于这是一个大型应用程序,我将采取一种比平时更合适的方法。我们将创建两个文件,一个名为database.py,一个称为UI.py。首先,在继续处理UI.py文件之前,我们将在database.py文件中创建和定义一些基本函数。
这是第一个函数,用于初始化与MySQL数据库的连接。
def initialize_connection():
conn = mysql.connector.connect(
host = "localhost",
user = "root",
password = "db1963"
)
cursor = conn.cursor()
return conn, cursor
在这里,我们用三个参数调用“connect”方法——主机、用户和密码。
“host”参数指定MySQL数据库服务器的位置。在这段代码中,位置设置为“localhost”,这意味着MySQL服务器运行在执行Python代码的同一台机器上。
“user”参数指定用于登录MySQL数据库的用户名。“password”参数用于指定与用户帐户关联的密码。在此处输入您用于设置MySQL数据库的用户名和密码。
创建连接对象后,下一行代码将创建一个游标对象。游标对象用于执行SQL查询并与MySQL数据库交互。然后,我们从该函数返回游标对象和连接对象。
我们稍后将从UI.py文件调用此函数。
创建 MySQL 数据库
虽然我们已经初始化了MySQL连接,但我们仍然需要为我们的项目创建一个数据库。已经创建了一个默认数据库,名为“sys”,但我们将创建一个名为“tutorial”的新数据库。对于这个应用程序。
下面的代码以安全的方式创建了一个新的数据库。它首先检查以确保没有名为“tutorial”的数据库。如果它不存在,它将执行“CREATE DATABASE tutorial”命令来创建它。
def create_database(cursor):
cursor.execute("SHOW DATABASES")
temp = cursor.fetchall()
databases = [item[0] for item in temp]
if "tutorial" not in databases:
cursor.execute("CREATE DATABASE tutorial")
cursor.execute("USE tutorial")
最后,它调用“USE-tutorial”命令,从默认数据库切换到新数据库。无论我们的数据库是否已经存在,都必须调用此行。
我们还没有完成我们的设置。
接下来,我们必须创建一个“表”。表基本上是我们为特定实体/目的存储数据的地方。在较大的应用程序中,数据库由多个表组成。例如,在游戏中,可能有一张“玩家”的桌子,一张“NPC”的桌子、一张“敌人”的桌子和一张“任务”的桌子等等。
在我们的应用程序中,我们将为用户存储记录。因此,我们将只有一个“用户”表。下面的代码以与创建数据库相同的方式创建了此表。
def create_table(cursor):
cursor.execute("SHOW TABLES")
temp = cursor.fetchall()
tables = [item[0] for item in temp]
if "users" not in tables:
cursor.execute("""CREATE TABLE users(
id INT AUTO_INCREMENT PRIMARY KEY,
firstName VARCHAR(100),
lastName VARCHAR(100),
password VARCHAR(30),
email VARCHAR(100) UNIQUE,
gender VARCHAR(1),
age INT,
address VARCHAR(200)
)""")
由于表很大,有几个字段,上面的代码有点SQL繁重。每个字段都有一个名称、一个数据类型和一些可选约束。我们在这里使用了三个这样的约束。首先,我们有“PRIMARY KEY”,它将“id”字段指定为唯一标识符,我们可以使用它来查询特定用户。
同样,我们也将“电子邮件”声明为唯一列。这些约束将自动防止输入重复项。我们还有用于“id”的AUTO_INCREMENT,这意味着我不会指定id,而是会自动生成数字序列。
我们还可以为每个基于文本的字段定义长度。密码限制为30,姓名和电子邮件限制为100,性别限制为1个字符。
最后,在所有这些之后,我们需要在某个地方调用这两个方法。
我们将把它们都放入我们之前创建的initialize_connection()函数中。
def initialize_connection():
conn = mysql.connector.connect(
host = "localhost",
user = "root",
password = "db1963"
)
cursor = conn.cursor()
create_database(cursor)
create_table(cursor)
return conn, cursor
我们的设置现已完成!
创建 Tkinter 项目
下面的代码展示了我们组合在一起的一个小型Tkinter项目,我们将MySQL数据库代码集成到其中。应用程序中没有额外的东西,只有一个注册和登录窗口。主窗口是空白的,因为它与本教程没有任何关系。我们唯一关心的是将用户注册到我们的数据库中,然后用他们的凭据稍后登录。
其中大部分只是标准的tkinter代码,所以慢慢来。现在唯一值得注意的是登录和注册窗口,以及我们从database.py文件(第二行)中导入的内容。我们调用了initialize_connection()方法,该方法返回了连接和游标对象。稍后调用登录、注册和函数时,我们将需要这些。
(以下代码后显示的应用程序截图)
import tkinter as tk
from database import *
conn, cursor = initialize_connection()
def center_window(width, height):
x = (root.winfo_screenwidth() // 2) - (width // 2)
y = (root.winfo_screenheight() // 2) - (height // 2)
root.geometry(f'{width}x{height}+{x}+{y}')
class WelcomeWindow(tk.Frame):
def __init__(self, master):
super().__init__()
self.master = master
self.master.title("Welcome")
center_window(240, 120)
login_button = tk.Button(self, text="Login", width=10,
command=self.open_login_window)
login_button.pack(padx=20, pady=(20, 10))
register_button = tk.Button(self, text="Register", width=10,
command=self.open_register_window)
register_button.pack(pady=10)
self.pack()
def open_login_window(self):
for widget in self.winfo_children():
widget.destroy()
self.destroy()
LoginWindow(self.master)
def open_register_window(self):
for widget in self.winfo_children():
widget.destroy()
self.destroy()
RegisterWindow(self.master)
class LoginWindow(tk.Frame):
def __init__(self, master):
super().__init__()
self.master = master
self.master.title("Login")
self.master.resizable(False, False)
center_window(240, 150)
tk.Label(self, text="Username:").grid(row=0, column=0)
self.username_entry = tk.Entry(self)
self.username_entry.grid(row=0, column=1, padx=10, pady=10)
tk.Label(self, text="Password:").grid(row=1, column=0)
self.password_entry = tk.Entry(self, show="*")
self.password_entry.grid(row=1, column=1, padx=10, pady=10)
submit_button = tk.Button(self, text="Submit", width=8, command=self.submit)
submit_button.grid(row=2, column=1, sticky="e", padx=10, pady=(10, 0))
submit_button = tk.Button(self, text="Back", width=8, command=self.back)
submit_button.grid(row=2, column=0, sticky="w", padx=10, pady=(10, 0))
self.pack()
def submit(self):
pass
def back(self):
for widget in self.winfo_children():
widget.destroy()
self.destroy()
WelcomeWindow(self.master)
class RegisterWindow(tk.Frame):
def __init__(self, master):
super().__init__()
self.master = master
self.master.title("Register")
self.master.resizable(False, False)
center_window(320, 350)
tk.Label(self, text="First Name:").grid(row=0, column=0, sticky="w")
self.first_name_entry = tk.Entry(self, width=26)
self.first_name_entry.grid(row=0, column=1, padx=10, pady=10, sticky="e")
tk.Label(self, text="Last Name:").grid(row=1, column=0, sticky="w")
self.last_name_entry = tk.Entry(self, width=26)
self.last_name_entry.grid(row=1, column=1, padx=10, pady=10, sticky="e")
tk.Label(self, text="Password:").grid(row=2, column=0, sticky="w")
self.password_entry = tk.Entry(self, show="*", width=26)
self.password_entry.grid(row=2, column=1, padx=10, pady=10, sticky="e")
tk.Label(self, text="Email:").grid(row=3, column=0, sticky="w")
self.email_entry = tk.Entry(self, width=26)
self.email_entry.grid(row=3, column=1, padx=10, pady=10, sticky="e")
tk.Label(self, text="Gender:").grid(row=4, column=0, sticky="w")
self.gender_entry = tk.Entry(self, width=10)
self.gender_entry.grid(row=4, column=1, padx=10, pady=10, sticky="e")
tk.Label(self, text="Age:").grid(row=5, column=0, sticky="w")
self.age_entry = tk.Entry(self, width=10)
self.age_entry.grid(row=5, column=1, padx=10, pady=10, sticky="e")
tk.Label(self, text="Address:").grid(row=6, column=0, sticky="w")
self.address_entry = tk.Text(self, width=20, height=3)
self.address_entry.grid(row=6, column=1, padx=10, pady=10, sticky="e")
submit_button = tk.Button(self, text="Submit", width=8, command=self.submit)
submit_button.grid(row=7, column=1, padx=10, pady=10, sticky="e")
submit_button = tk.Button(self, text="Back", width=8, command=self.back)
submit_button.grid(row=7, column=0, sticky="w", padx=10, pady=(10, 10))
self.pack()
def submit(self):
pass
def back(self):
for widget in self.winfo_children():
widget.destroy()
self.destroy()
WelcomeWindow(self.master)
class MainWindow(tk.Frame):
def __init__(self, master):
super().__init__()
self.master = master
center_window(600, 400)
self.pack()
root = tk.Tk()
root.eval('tk::PlaceWindow . center')
WelcomeWindow(root)
root.mainloop()
欢迎页面:
登录页面:
注册页面:
需要注意的是,在上面的代码中,Login 和 Register 窗口的 “submit” 函数都是空的。我们将在接下来的两节中开发它们。
注册用户
让我们在代码中实现注册功能。我们需要做的第一件事是从tkinter小部件(在注册表窗口中)中提取所需的数据,然后将这些数据传递给我们即将创建的database.py文件中的“register”函数。
这是我们在注册窗口中填写的提交函数。我们只需将所有数据提取到字典中,然后将其与之前使用initialize_connection()函数创建的连接和游标一起传递给寄存器函数。
def submit(self):
data = {}
data["firstName"]= self.first_name_entry.get()
data["lastName"]= self.last_name_entry.get()
data["password"]= self.password_entry.get()
data["email"]= self.email_entry.get()
data["gender"]= self.gender_entry.get()
data["age"]= self.age_entry.get()
data["address"]= self.address_entry.get(1.0, tk.END)
register(cursor, conn, data)
接下来,我们将在database.py文件中实现此注册函数。
我们在这里使用了字符串格式,将字典中的所有值插入到SQL insert into命令中,该命令将值插入到表中。这些值必须按照我们定义表的顺序排列。(第一个值是NULL,因为这是id,它将被自动分配)
def register(cursor, conn, data):
cursor.execute(f"""INSERT INTO users values(
NULL,
'{data["firstName"]}',
'{data["lastName"]}',
'{data["password"]}',
'{data["email"]}',
'{data["gender"]}',
'{data["age"]}',
'{data["address"]}'
)""")
conn.commit()
最后,我们需要调用“commit”方法,它基本上实现了我们所做的更改。把它想象成“保存”对数据库的更改。当涉及到事务控制和数据库中的恢复时,提交的概念很重要,这是一个完全独立的主题。
我们的注册功能现已完成!我们可以开始注册用户,但让我们等到实现了登录功能。
用户登录
接下来,我们将研究登录功能的两个函数,登录窗口中的submit()函数和数据库.py文件中的login()函数。
这是submit()函数。我们还没有定义login()函数,但我们已经事先决定,根据登录是否成功,它将返回True或False。
def submit(self):
data = {}
data["email"] = self.username_entry.get()
data["password"] = self.password_entry.get()
if login(cursor, data) == True:
print("successful login")
for widget in self.winfo_children():
widget.destroy()
self.destroy()
MainWindow(self.master)
else:
print("unsuccessful login")
这是login()函数,我们在其中执行“SELECT”命令,通过使用“where”子句根据给定的“电子邮件”和“密码”过滤用户。如果登录成功,我们将销毁当前窗口,并打开主窗口。
def login(cursor, data):
cursor.execute(f"""SELECT * FROM users WHERE email = '{data["email"]}'
AND password = '{data["password"]}' """)
if cursor.fetchone() != None:
return True
return False
然后,根据记录是否成功返回,我们返回True或False值。“无”结果表示未找到记录。
我们决定使用“email”作为“用户名”,因为这是一个独特的字段。期望用户使用数字作为登录名并不是一个好主意,而且没有其他字段是唯一可用作用户名的。如果需要,您可能希望在注册过程中为“用户名”创建一个单独的字段。
测试项目
现在让我们启动应用程序并试用一下。首先要做的是在 register 窗口中注册一个新用户。
接下来,我将点击提交以注册用户,然后导航到登录页面以尝试登录。如果登录成功,我们应该被重定向到主窗口。
完整的代码
UI.py 文件
import tkinter as tk
from database import *
conn, cursor = initialize_connection()
def center_window(width, height):
x = (root.winfo_screenwidth() // 2) - (width // 2)
y = (root.winfo_screenheight() // 2) - (height // 2)
root.geometry(f'{width}x{height}+{x}+{y}')
class WelcomeWindow(tk.Frame):
def __init__(self, master):
super().__init__()
self.master = master
self.master.title("Welcome")
center_window(240, 120)
login_button = tk.Button(self, text="Login", width=10, command=self.open_login_window)
login_button.pack(padx=20, pady=(20, 10))
register_button = tk.Button(self, text="Register", width=10, command=self.open_register_window)
register_button.pack(pady=10)
self.pack()
def open_login_window(self):
for widget in self.winfo_children():
widget.destroy()
self.destroy()
LoginWindow(self.master)
def open_register_window(self):
for widget in self.winfo_children():
widget.destroy()
self.destroy()
RegisterWindow(self.master)
class LoginWindow(tk.Frame):
def __init__(self, master):
super().__init__()
self.master = master
self.master.title("Login")
self.master.resizable(False, False)
center_window(240, 150)
tk.Label(self, text="Username:").grid(row=0, column=0)
self.username_entry = tk.Entry(self)
self.username_entry.grid(row=0, column=1, padx=10, pady=10)
tk.Label(self, text="Password:").grid(row=1, column=0)
self.password_entry = tk.Entry(self, show="*")
self.password_entry.grid(row=1, column=1, padx=10, pady=10)
submit_button = tk.Button(self, text="Submit", width=8, command=self.submit)
submit_button.grid(row=2, column=1, sticky="e", padx=10, pady=(10, 0))
submit_button = tk.Button(self, text="Back", width=8, command=self.back)
submit_button.grid(row=2, column=0, sticky="w", padx=10, pady=(10, 0))
self.pack()
def submit(self):
data = {}
data["email"] = self.username_entry.get()
data["password"] = self.password_entry.get()
if login(cursor, data) == True:
print("successful login")
for widget in self.winfo_children():
widget.destroy()
self.destroy()
MainWindow(self.master)
else:
print("unsuccessful login")
def back(self):
for widget in self.winfo_children():
widget.destroy()
self.destroy()
WelcomeWindow(self.master)
class RegisterWindow(tk.Frame):
def __init__(self, master):
super().__init__()
self.master = master
self.master.title("Register")
self.master.resizable(False, False)
center_window(320, 350)
tk.Label(self, text="First Name:").grid(row=0, column=0, sticky="w")
self.first_name_entry = tk.Entry(self, width=26)
self.first_name_entry.grid(row=0, column=1, padx=10, pady=10, sticky="e")
tk.Label(self, text="Last Name:").grid(row=1, column=0, sticky="w")
self.last_name_entry = tk.Entry(self, width=26)
self.last_name_entry.grid(row=1, column=1, padx=10, pady=10, sticky="e")
tk.Label(self, text="Password:").grid(row=2, column=0, sticky="w")
self.password_entry = tk.Entry(self, show="*", width=26)
self.password_entry.grid(row=2, column=1, padx=10, pady=10, sticky="e")
tk.Label(self, text="Email:").grid(row=3, column=0, sticky="w")
self.email_entry = tk.Entry(self, width=26)
self.email_entry.grid(row=3, column=1, padx=10, pady=10, sticky="e")
tk.Label(self, text="Gender:").grid(row=4, column=0, sticky="w")
self.gender_entry = tk.Entry(self, width=10)
self.gender_entry.grid(row=4, column=1, padx=10, pady=10, sticky="e")
tk.Label(self, text="Age:").grid(row=5, column=0, sticky="w")
self.age_entry = tk.Entry(self, width=10)
self.age_entry.grid(row=5, column=1, padx=10, pady=10, sticky="e")
tk.Label(self, text="Address:").grid(row=6, column=0, sticky="w")
self.address_entry = tk.Text(self, width=20, height=3)
self.address_entry.grid(row=6, column=1, padx=10, pady=10, sticky="e")
submit_button = tk.Button(self, text="Submit", width=8, command=self.submit)
submit_button.grid(row=7, column=1, padx=10, pady=10, sticky="e")
submit_button = tk.Button(self, text="Back", width=8, command=self.back)
submit_button.grid(row=7, column=0, sticky="w", padx=10, pady=(10, 10))
self.pack()
def submit(self):
data = {}
data["firstName"] = self.first_name_entry.get()
data["lastName"] = self.last_name_entry.get()
data["password"] = self.password_entry.get()
data["email"] = self.email_entry.get()
data["gender"] = self.gender_entry.get()
data["age"] = self.age_entry.get()
data["address"] = self.address_entry.get(1.0, tk.END)
register(cursor, conn, data)
def back(self):
for widget in self.winfo_children():
widget.destroy()
self.destroy()
WelcomeWindow(self.master)
class MainWindow(tk.Frame):
def __init__(self, master):
super().__init__()
self.master = master
center_window(600, 400)
self.pack()
root = tk.Tk()
root.eval('tk::PlaceWindow . center')
WelcomeWindow(root)
root.mainloop()
database.py 文件。
mport mysql.connector
def initialize_connection():
conn = mysql.connector.connect(
host = "localhost",
user = "root",
password = "db1963"
)
cursor = conn.cursor()
create_database(cursor)
create_table(cursor)
return conn, cursor
def create_database(cursor):
cursor.execute("SHOW DATABASES")
temp = cursor.fetchall()
databases = [item[0] for item in temp]
if "tutorial" not in databases:
cursor.execute("CREATE DATABASE tutorial")
cursor.execute("USE tutorial")
def create_table(cursor):
cursor.execute("SHOW TABLES")
temp = cursor.fetchall()
tables = [item[0] for item in temp]
if "users" not in tables:
cursor.execute("""CREATE TABLE users(
id INT AUTO_INCREMENT PRIMARY KEY,
firstName VARCHAR(100),
lastName VARCHAR(100),
password VARCHAR(30),
email VARCHAR(100) UNIQUE,
gender VARCHAR(1),
age INT,
address VARCHAR(200)
)""")
def login(cursor, data):
cursor.execute(f"""SELECT * FROM users WHERE email = '{data["email"]}'
AND password = '{data["password"]}' """)
if cursor.fetchone() != None:
return True
return False
def register(cursor, conn, data):
print(data)
cursor.execute(f"""INSERT INTO users values(
NULL,
'{data["firstName"]}',
'{data["lastName"]}',
'{data["password"]}',
'{data["email"]}',
'{data["gender"]}',
'{data["age"]}',
'{data["address"]}'
)""")
conn.commit()