使用 PyQt5(PySide2)+SQLAlchemy 做一个登录注册页(四)
本文将介绍自己用 PyQt5+SQLAlchemy 做的一个登录注册页,使用邮箱接收验证码,本文介绍是前后端未分离的实现方式,后续将出一个前后端分离的,你可以将 PyQt5 改为 PySide2 以获得更宽松的开源协议
本文由于涉及到的代码较多,将会是一个系列,会有多篇文章
系列文章索引
必要说明
- 使用的环境
requirements.txt
# Python3.8.10 x32
# Windows10 x64
PyQt5
pyqt5-tools
PyMySQL~=1.1.0
sqlalchemy~=2.0.25
bcrypt~=4.1.2
email-validator # new add
- 项目结构(显示变化的部分)
--- QtLoginRegistration
|--- lib
|--- __init__.py
|--- basic_function.py # 存放公共方法
|--- crud
|--- __init__.py
|--- crud.py # 数据库操作
|--- requirements.txt # 添加 email-validator
为登录页,添加登录逻辑代码,实现登录
第1步
添加必填校验
- 新建
lib/basic_function.py
文件,保存基础公共方法,增加以下内容
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ Project : QtLoginRegistration
@ File : basic_function.py
@ Author : yqbao
@ Version : V1.0.0
@ Description : 基础公共方法类
"""
from PyQt5.QtCore import QObject
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtWidgets import QMessageBox
class BasicFunction(QObject):
"""一个基础公共方法类"""
def __init__(self, parent=None):
super().__init__(parent=parent)
@staticmethod
def info_message(msg):
"""公共的消息提示"""
message = QMessageBox(QMessageBox.Information, "提示", msg, QMessageBox.Yes)
icon = QIcon()
icon.addPixmap(QPixmap(":/home/images/testing_x48.ico"), QIcon.Normal, QIcon.Off)
message.setWindowIcon(icon)
message.button(QMessageBox.Yes).setText('确定')
message.exec() # 模态显示
- 在
core/login_register.py
中添加如下代码 GitHub完整代码
# 导入
from lib.basic_function import BasicFunction
# 在 构造方法 __init__ 中实例公共方法类
self.basic_function = BasicFunction(self)
# 在 init_ui 方法中 添加登录页需要绑定的信号
self.pushButtonLogin.clicked.connect(self.login)
# 在类中添加 2个新方法
def login(self):
"""登录动作"""
if not self.required_login(): # 必填校验未通过
return
def required_login(self):
"""登录必填校验"""
self.account = self.lineEditUsername.text()
self.password = self.lineEditPassword.text()
if not self.account.strip():
self.basic_function.info_message("用户账号不能为空")
return
if not self.password.strip():
self.basic_function.info_message("用户密码不能为空")
return
return True
- 运行效果
第2步
完善登录校验逻辑
- 新建
crud/crud.py
文件,增加以下内容
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ Project : QtLoginRegistration
@ File : crud.py
@ Author : yqbao
@ Version : V1.0.0
@ Description :
"""
from typing import Union
from sqlalchemy import and_
from sqlalchemy.orm import Session
from db import models
class CRUD(object):
@staticmethod
def _commit(db: Session, db_obj):
try:
db.commit()
return db_obj
except Exception:
db.rollback()
return
class CRUDUser(CRUD):
@staticmethod
def get_user_by_email(db: Session, email: str) -> Union[models.User, None]:
"""根据email查询用户"""
return db.query(models.User).filter(and_(models.User.email == email, models.User.deleted == '0')).first()
@staticmethod
def get_user_by_username(db: Session, username: str) -> Union[models.User, None]:
"""根据username查询用户"""
return db.query(models.User).filter(and_(models.User.username == username, models.User.deleted == '0')).first()
- 在
core/login_register.py
中添加如下代码 GitHub完整代码
# 导入
import bcrypt
from email_validator import validate_email, EmailNotValidError
from crud.crud import CRUDUser
from db import SessionLocal
# 在 构造方法 __init__ 中实例公共方法类
self.user = CRUDUser()
# 修改 login 方法
def login(self):
"""登录动作"""
if not self.required_login(): # 必填校验未通过
return
try:
info = validate_email(self.account, check_deliverability=False)
email = info.normalized
with SessionLocal() as db:
user = self.user.get_user_by_email(db, email)
except EmailNotValidError: # 输入的是否是邮箱,不是将报错
with SessionLocal() as db:
user = self.user.get_user_by_username(db, self.account)
if not user: # 查询空,无此用户
self.basic_function.info_message("用户名或密码错误")
return
if user.disabled == '0':
self.basic_function.info_message("此用户账号已被禁用")
return
bytes_my_password = bytes(self.password, encoding="utf-8")
str_my_hash_password = user.password
bytes_my_hash_password = bytes(str_my_hash_password, encoding='utf-8')
check = bcrypt.checkpw(bytes_my_password, bytes_my_hash_password)
if not check: # 校验通过,设置QDialog对象状态为允许
self.basic_function.info_message("用户名或密码错误")
return
self.accept()
- 运行效果