@@ -1,5 +0,0 @@
|
||||
FLASK_APP=main.py
|
||||
FLASK_ENV=development
|
||||
FLASK_DEBUG=1
|
||||
FLASK_RUN_HOST = 127.0.0.1
|
||||
FLASK_RUN_PORT = 5000
|
||||
@@ -57,90 +57,23 @@ Pear Admin Flask 分为 Common / Simple 两个版本:
|
||||
|
||||
```
|
||||
Pear Admin Flask
|
||||
│
|
||||
├─applications
|
||||
│ │
|
||||
│ ├─config
|
||||
│ │ │
|
||||
│ │ ├─ common.py #普通配置
|
||||
│ │ │
|
||||
│ │ └─ database.py #数据库配置
|
||||
│ │
|
||||
│ ├─models
|
||||
│ │ │
|
||||
│ │ └─admin.py #基本模型
|
||||
│ │
|
||||
│ ├─service
|
||||
│ │ │
|
||||
│ │ ├─admin
|
||||
│ │ │ │
|
||||
│ │ │ ├─ file.py #file视图的数据操作
|
||||
│ │ │ │
|
||||
│ │ │ ├─ index.py #index视图的数据操作
|
||||
│ │ │ │
|
||||
│ │ │ ├─ power.py #power视图的数据操作
|
||||
│ │ │ │
|
||||
│ │ │ ├─ role.py #role视图的数据操作
|
||||
│ │ │ │
|
||||
│ │ │ └─ user.py #user视图的数据操作
|
||||
│ │ │
|
||||
│ │ ├─ admin_log.py #存储日志
|
||||
│ │ │
|
||||
│ │ ├─ deBug.py #deBug工具栏初始化
|
||||
│ │ │
|
||||
│ │ ├─ login.py #flask_login初始化
|
||||
│ │ │
|
||||
│ │ ├─ CaptchaTool.py #验证码
|
||||
│ │ │
|
||||
│ │ ├─ OriginalDb.py #原生sql查询封装
|
||||
│ │ │
|
||||
│ │ ├─ route_auth.py #权限
|
||||
│ │ │
|
||||
│ │ └─ upload.py #上传
|
||||
│ │
|
||||
│ └─views
|
||||
│ │
|
||||
│ ├─admin #前台视图
|
||||
│ │ │
|
||||
│ │ ├─index.py #主视图
|
||||
│ │ │
|
||||
│ │ ├─user.py #用户视图
|
||||
│ │ │
|
||||
│ │ ├─role.py #角色视图
|
||||
│ │ │
|
||||
│ │ ├─power.py #权限视图
|
||||
│ │ │
|
||||
│ │ ├─monitor.py #系统监控
|
||||
│ │ │
|
||||
│ │ ├─file.py #文件上传
|
||||
│ │ │
|
||||
│ │ ├─admin_log.py #系统日志
|
||||
│ │ │
|
||||
│ │ ├─context_processor.py #全局模板函数注册
|
||||
│ │ │
|
||||
│ │ ├─error.py #错误处理
|
||||
│ │ │
|
||||
│ │ └─init.py #蓝图注册
|
||||
│ │
|
||||
│ └─index #前台视图
|
||||
│
|
||||
├─dev #数据库初始化
|
||||
│
|
||||
├─migrations
|
||||
│
|
||||
├─readmes
|
||||
│
|
||||
├─static #静态资源
|
||||
│ │
|
||||
│ └─upload #文件上传地址
|
||||
│
|
||||
└─templates
|
||||
│
|
||||
├─admin #前台模板
|
||||
│
|
||||
├─errors #错误模板
|
||||
│
|
||||
└─index #前台模板
|
||||
├─applications # 应用
|
||||
│ ├─configs # 配置文件
|
||||
│ │ ├─ common.py # 普通配置
|
||||
│ │ └─ config.py # 配置文件对象
|
||||
│ ├─extensions # 注册插件
|
||||
│ ├─models # 数据模型
|
||||
│ ├─static # 静态资源文件
|
||||
│ ├─templates # 静态模板文件
|
||||
│ └─views # 视图部分
|
||||
│ ├─admin # 后台管理视图模块
|
||||
│ └─index # 前台视图模块
|
||||
├─docs # 文档说明(占坑)
|
||||
├─migrations # 迁移文件记录
|
||||
├─requirement # 依赖文件
|
||||
├─test # 测试文件夹(占坑)
|
||||
└─.env # 项目的配置文件
|
||||
|
||||
```
|
||||
|
||||
|
||||
@@ -152,10 +85,10 @@ Pear Admin Flask
|
||||
git clone https://gitee.com/pear-admin/pear-admin-flask
|
||||
|
||||
# 安 装
|
||||
pip install -r requirement.txt
|
||||
pip install -r requirement\requirement-dev.txt
|
||||
|
||||
# 配 置
|
||||
applications\config\database.py
|
||||
.env
|
||||
|
||||
```
|
||||
|
||||
@@ -189,14 +122,8 @@ python -m venv venv
|
||||
#### 运行项目
|
||||
|
||||
```bash
|
||||
# 进 入 目 录
|
||||
cd dev
|
||||
|
||||
# 初 始 化 数 据 库
|
||||
python initDb.py
|
||||
|
||||
# 如 果 报 模 块 路 径 错 误
|
||||
python dev/initDb.py
|
||||
python test\init_databases.py
|
||||
|
||||
```
|
||||
|
||||
@@ -207,6 +134,6 @@ python dev/initDb.py
|
||||
|
||||
| | |
|
||||
|---------------------|---------------------|
|
||||
|  |  |
|
||||
| |  |
|
||||
|  |  |
|
||||
|  |  |
|
||||
| |  |
|
||||
|  |  |
|
||||
@@ -0,0 +1,6 @@
|
||||
from applications import create_app
|
||||
|
||||
app = create_app()
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
@@ -0,0 +1,46 @@
|
||||
import os
|
||||
|
||||
from flask import Flask
|
||||
from flask_uploads import configure_uploads
|
||||
|
||||
from applications.common.utils.upload import photos
|
||||
from applications.configs import common
|
||||
from applications.extensions import init_plugs
|
||||
from applications.view import init_view
|
||||
from applications.configs import config
|
||||
|
||||
|
||||
def create_app(config_name=None):
|
||||
app = Flask(__name__)
|
||||
|
||||
if not config_name:
|
||||
# 尝试从本地环境中读取
|
||||
config_name = os.getenv('FLASK_CONFIG', 'development')
|
||||
|
||||
# 引入数据库配置
|
||||
app.config.from_object(common)
|
||||
app.config.from_object(config[config_name])
|
||||
# 注册各种插件
|
||||
init_plugs(app)
|
||||
|
||||
# 注册路由
|
||||
init_view(app)
|
||||
|
||||
# 文件上传
|
||||
configure_uploads(app, photos)
|
||||
|
||||
logo()
|
||||
|
||||
return app
|
||||
|
||||
|
||||
def logo():
|
||||
print('''
|
||||
_____ _ _ ______ _ _
|
||||
| __ \ /\ | | (_) | ____| | | |
|
||||
| |__) |__ __ _ _ __ / \ __| |_ __ ___ _ _ __ | |__ | | __ _ ___| | __
|
||||
| ___/ _ \/ _` | '__| / /\ \ / _` | '_ ` _ \| | '_ \ | __| | |/ _` / __| |/ /
|
||||
| | | __/ (_| | | / ____ \ (_| | | | | | | | | | | | | | | (_| \__ \ <
|
||||
|_| \___|\__,_|_| /_/ \_\__,_|_| |_| |_|_|_| |_| |_| |_|\__,_|___/_|\_\\
|
||||
|
||||
''')
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from applications.models import db
|
||||
from applications.models.admin_dept import Dept, DeptSchema
|
||||
from applications.models.admin_user import User
|
||||
from applications.service.common.curd import model_to_dicts
|
||||
from applications.service.common.validate import xss_escape
|
||||
from applications.common.utils.validate import xss_escape
|
||||
from applications.extensions import db
|
||||
from applications.models import Dept, DeptSchema
|
||||
from applications.models import User
|
||||
from applications.common.curd import model_to_dicts
|
||||
|
||||
|
||||
def get_dept_dict():
|
||||
@@ -12,8 +12,6 @@ def get_dept_dict():
|
||||
|
||||
|
||||
def save_dept(req):
|
||||
|
||||
|
||||
address = xss_escape(req.get("address"))
|
||||
deptName = xss_escape(req.get("deptName"))
|
||||
email = xss_escape(req.get("email"))
|
||||
@@ -1,27 +1,19 @@
|
||||
# 字典管理
|
||||
# 查询字典类型
|
||||
|
||||
|
||||
from applications.models import db
|
||||
from applications.models.admin_dict import DictType, DictData, DictTypeSchema, DictDataSchema
|
||||
from applications.service.common.curd import model_to_dicts
|
||||
from applications.service.common.validate import xss_escape
|
||||
|
||||
# 通过type_code获取字典dict
|
||||
# 例:get_dict('user_sex')
|
||||
# [{'key': '男', 'value': 'boy'}, {'key': '女', 'value': 'girl'}]
|
||||
from applications.common.utils.validate import xss_escape
|
||||
from applications.extensions import db
|
||||
from applications.models import DictType, DictData, DictTypeSchema, DictDataSchema
|
||||
from applications.common.curd import model_to_dicts
|
||||
|
||||
|
||||
def get_dict(typecode: str):
|
||||
Dict_list = []
|
||||
dict_list = []
|
||||
if DictType.query.filter_by(type_code=typecode, enable=1).first():
|
||||
Dicts = DictData.query.filter_by(type_code=typecode, enable=1).all()
|
||||
for d in Dicts:
|
||||
Dict_Dict = {"key": d.data_label, "value": d.data_value}
|
||||
Dict_list.append(Dict_Dict)
|
||||
dicts = DictData.query.filter_by(type_code=typecode, enable=1).all()
|
||||
for d in dicts:
|
||||
dict_dict = {"key": d.data_label, "value": d.data_value}
|
||||
dict_list.append(dict_dict)
|
||||
else:
|
||||
return None
|
||||
return Dict_list
|
||||
return dict_list
|
||||
|
||||
|
||||
def get_dict_type(page, limit, type_name):
|
||||
@@ -45,7 +37,7 @@ def get_dict_data(page, limit, type_code):
|
||||
return dict_dict, count
|
||||
|
||||
|
||||
# 增加dicttype
|
||||
# 增加 dict type
|
||||
def save_dict_type(req_json):
|
||||
description = xss_escape(req_json.get("description"))
|
||||
enable = xss_escape(req_json.get("enable"))
|
||||
@@ -1,16 +1,17 @@
|
||||
import os
|
||||
from flask import current_app
|
||||
from sqlalchemy import desc
|
||||
from applications.models import db
|
||||
from applications.models.admin_photo import Photo, PhotoSchema
|
||||
from applications.service.common.curd import model_to_dicts
|
||||
from applications.service.upload import photos
|
||||
|
||||
from applications.common.utils.upload import photos
|
||||
from applications.extensions import db
|
||||
from applications.models import Photo, PhotoSchema
|
||||
from applications.common.curd import model_to_dicts
|
||||
|
||||
|
||||
def get_photo(page, limit):
|
||||
photo = Photo.query.order_by(desc(Photo.create_time)).paginate(page=page, per_page=limit, error_out=False)
|
||||
count = Photo.query.count()
|
||||
data = model_to_dicts(Schema=PhotoSchema,model=photo.items)
|
||||
data = model_to_dicts(Schema=PhotoSchema, model=photo.items)
|
||||
return data, count
|
||||
|
||||
|
||||
@@ -26,9 +27,9 @@ def upload_one(photo, mime):
|
||||
return file_url
|
||||
|
||||
|
||||
def delete_photo_by_id(id):
|
||||
photo_name = Photo.query.filter_by(id=id).first().name
|
||||
photo = Photo.query.filter_by(id=id).delete()
|
||||
def delete_photo_by_id(_id):
|
||||
photo_name = Photo.query.filter_by(id=_id).first().name
|
||||
photo = Photo.query.filter_by(id=_id).delete()
|
||||
db.session.commit()
|
||||
upload_url = current_app.config.get("UPLOADED_PHOTOS_DEST")
|
||||
os.remove(upload_url + '/' + photo_name)
|
||||
@@ -1,8 +1,9 @@
|
||||
from io import BytesIO
|
||||
from flask import session, make_response, current_app
|
||||
from flask_login import current_user
|
||||
|
||||
from applications.common.utils.gen_captcha import gen_captcha
|
||||
from applications.models.admin_power import PowerSchema
|
||||
from applications.service.captcha import gen_captcha
|
||||
|
||||
|
||||
# 授权路由存入session
|
||||
@@ -1,8 +1,8 @@
|
||||
from applications.models import db
|
||||
from applications.common.utils.validate import xss_escape
|
||||
from applications.extensions import db
|
||||
from applications.models.admin_power import Power, PowerSchema2
|
||||
from applications.models.admin_role import Role
|
||||
from applications.service.common.curd import model_to_dicts
|
||||
from applications.service.common.validate import xss_escape
|
||||
from applications.models import Role
|
||||
from applications.common.curd import model_to_dicts
|
||||
|
||||
|
||||
def get_power_dict():
|
||||
@@ -10,6 +10,7 @@ def get_power_dict():
|
||||
res = model_to_dicts(Schema=PowerSchema2, model=power)
|
||||
return res
|
||||
|
||||
|
||||
# 选择父节点
|
||||
def select_parent():
|
||||
power = Power.query.all()
|
||||
@@ -1,12 +1,13 @@
|
||||
from sqlalchemy import and_
|
||||
from applications.models import db
|
||||
from applications.models.admin_role import Role, RoleSchema
|
||||
|
||||
from applications.common.utils.validate import xss_escape
|
||||
from applications.extensions import db
|
||||
from applications.models import Role, RoleSchema
|
||||
from applications.models.admin_power import Power, PowerSchema2
|
||||
from applications.models.admin_user import User
|
||||
from applications.models import User
|
||||
|
||||
# 获取角色对象
|
||||
from applications.service.common.curd import model_to_dicts
|
||||
from applications.service.common.validate import xss_escape
|
||||
from applications.common.curd import model_to_dicts
|
||||
|
||||
|
||||
def get_role_data(page, limit, filters):
|
||||
@@ -1,33 +1,33 @@
|
||||
from flask import jsonify
|
||||
from flask_login import current_user
|
||||
from sqlalchemy import and_, desc
|
||||
from applications.models import db
|
||||
from applications.models.admin_user import User, UserSchema
|
||||
from applications.models.admin_role import Role
|
||||
from applications.models.admin_log import AdminLog
|
||||
from applications.extensions import db
|
||||
from applications.models import User, UserSchema
|
||||
from applications.models import Role
|
||||
from applications.models import AdminLog
|
||||
|
||||
# 获取用户的 sqlalchemy 对象分页器
|
||||
from applications.common.curd import model_to_dicts
|
||||
|
||||
|
||||
# 获取用户的sqlalchemy对象分页器
|
||||
from applications.service.common.curd import model_to_dicts
|
||||
|
||||
|
||||
def get_user_data(page, limit, filters,deptId):
|
||||
def get_user_data(page, limit, filters, deptId):
|
||||
if deptId:
|
||||
user = User.query.filter_by(dept_id=deptId).filter(and_(*[getattr(User, k).like(v) for k, v in filters.items()])).paginate(page=page,
|
||||
per_page=limit,
|
||||
error_out=False)
|
||||
user = User.query.filter_by(dept_id=deptId).filter(
|
||||
and_(*[getattr(User, k).like(v) for k, v in filters.items()])).paginate(page=page,
|
||||
per_page=limit,
|
||||
error_out=False)
|
||||
else:
|
||||
user = User.query.filter(and_(*[getattr(User, k).like(v) for k, v in filters.items()])).paginate(page=page,
|
||||
per_page=limit,
|
||||
error_out=False)
|
||||
per_page=limit,
|
||||
error_out=False)
|
||||
count = User.query.count()
|
||||
return user, count
|
||||
|
||||
|
||||
# 获取用户的dict数据分页器
|
||||
def get_user_data_dict(page, limit, filters,deptId):
|
||||
user, count = get_user_data(page, limit, filters,deptId)
|
||||
data = model_to_dicts(Schema=UserSchema,model=user.items)
|
||||
def get_user_data_dict(page, limit, filters, deptId):
|
||||
user, count = get_user_data(page, limit, filters, deptId)
|
||||
data = model_to_dicts(Schema=UserSchema, model=user.items)
|
||||
return data, count
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ def update_avatar(url):
|
||||
|
||||
# 更新用户信息
|
||||
def update_user(id, username, realname, deptId):
|
||||
user = User.query.filter_by(id=id).update({'username': username, 'realname': realname,'dept_id':deptId})
|
||||
user = User.query.filter_by(id=id).update({'username': username, 'realname': realname, 'dept_id': deptId})
|
||||
db.session.commit()
|
||||
return user
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from flask_login import current_user
|
||||
|
||||
from applications.models import db
|
||||
from applications.models.admin_log import AdminLog
|
||||
from applications.extensions import db
|
||||
from applications.models import AdminLog
|
||||
|
||||
|
||||
def login_log(request,uid, is_access):
|
||||
def login_log(request, uid, is_access):
|
||||
info = {
|
||||
'method': request.method,
|
||||
'url': request.path,
|
||||
@@ -22,7 +22,7 @@ def login_log(request,uid, is_access):
|
||||
desc=info.get('desc'),
|
||||
uid=info.get('uid'),
|
||||
method=info.get('method'),
|
||||
success = info.get('success')
|
||||
success=info.get('success')
|
||||
)
|
||||
db.session.add(log)
|
||||
db.session.flush()
|
||||
@@ -38,7 +38,7 @@ def admin_log(request, is_access):
|
||||
'user_agent': request.headers.get('User-Agent'),
|
||||
'desc': str(dict(request.values)),
|
||||
'uid': current_user.id,
|
||||
'success':int(is_access)
|
||||
'success': int(is_access)
|
||||
|
||||
}
|
||||
log = AdminLog(
|
||||
@@ -0,0 +1,17 @@
|
||||
#
|
||||
MYSQL_HOST = '127.0.0.1'
|
||||
MYSQL_PORT = '3306'
|
||||
MYSQL_DATABASE = 'PearAdminFlask'
|
||||
MYSQL_USERNAME = 'root'
|
||||
MYSQL_PASSWORD = 'root'
|
||||
|
||||
MYSQL_DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{db}?charset=utf8".format(username=USERNAME,
|
||||
password=PASSWORD,
|
||||
host=HOST,
|
||||
port=PORT,
|
||||
db=DATABASE)
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = MYSQL_DB_URI
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = True
|
||||
SQLALCHEMY_ECHO = False
|
||||
SQLALCHEMY_POOL_RECYCLE = 8
|
||||
@@ -0,0 +1,14 @@
|
||||
from captcha.image import ImageCaptcha
|
||||
|
||||
from PIL import Image
|
||||
from random import choices
|
||||
|
||||
|
||||
def gen_captcha(content='0123456789'):
|
||||
""" 生成验证码 """
|
||||
image = ImageCaptcha()
|
||||
# 获取字符串
|
||||
captcha_text = "".join(choices(content, k=4))
|
||||
# 生成图像
|
||||
captcha_image = Image.open(image.generate(captcha_text))
|
||||
return captcha_text, captcha_image
|
||||
@@ -1,38 +1,20 @@
|
||||
from flask import jsonify
|
||||
|
||||
'''
|
||||
成功响应
|
||||
|
||||
默认值”成功“
|
||||
|
||||
'''
|
||||
|
||||
|
||||
def success_api(msg: str = "成功"):
|
||||
return jsonify(success=True, msg=msg)
|
||||
|
||||
|
||||
'''
|
||||
|
||||
失败响应
|
||||
默认值“失败”
|
||||
|
||||
'''
|
||||
""" 成功响应 默认值”成功“ """
|
||||
return jsonify(success=True, message=msg)
|
||||
|
||||
|
||||
def fail_api(msg: str = "失败"):
|
||||
return jsonify(success=False, msg=msg)
|
||||
|
||||
'''
|
||||
|
||||
动态表格渲染响应
|
||||
|
||||
'''
|
||||
""" 失败响应 默认值“失败” """
|
||||
return jsonify(success=False, message=msg)
|
||||
|
||||
|
||||
def table_api(msg: str = "", count=0, data=None, limit=10):
|
||||
""" 动态表格渲染响应 """
|
||||
res = {
|
||||
'msg': msg,
|
||||
'message': msg,
|
||||
'code': 0,
|
||||
'data': data,
|
||||
'count': count,
|
||||
@@ -1,7 +1,7 @@
|
||||
from functools import wraps
|
||||
from flask import abort, request, jsonify, session
|
||||
from flask_login import login_required
|
||||
from applications.service.admin_log import admin_log
|
||||
from applications.common.admin_log import admin_log
|
||||
|
||||
|
||||
def authorize(power: str, log: bool = False):
|
||||
@@ -1,13 +0,0 @@
|
||||
# 数据库连接
|
||||
HOST = '127.0.0.1'
|
||||
PORT = '3306'
|
||||
DATABASE = 'PearAdminFlask'
|
||||
USERNAME = 'root'
|
||||
PASSWORD = 'root'
|
||||
|
||||
DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{db}?charset=utf8".format(username=USERNAME,password=PASSWORD, host=HOST,port=PORT, db=DATABASE)
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = DB_URI
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = True
|
||||
SQLALCHEMY_ECHO = False
|
||||
SQLALCHEMY_POOL_RECYCLE=8
|
||||
@@ -0,0 +1 @@
|
||||
from .config import config
|
||||
@@ -17,9 +17,6 @@ SYSTEM_PANEL_LINKS = [
|
||||
"href": "https://gitee.com/Jmysy/Pear-Admin-Layui"
|
||||
}
|
||||
]
|
||||
# 密钥
|
||||
# 密钥配置(flask session需要) 记得修改
|
||||
SECRET_KEY = 'pear-admin-flask'
|
||||
|
||||
UPLOADED_PHOTOS_DEST = 'static/upload'
|
||||
UPLOADED_FILES_ALLOW = ['gif', 'jpg']
|
||||
@@ -0,0 +1,49 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
|
||||
class BaseConfig:
|
||||
SECRET_KEY = os.getenv('SECRET_KEY', 'dev key')
|
||||
|
||||
# redis配置
|
||||
REDIS_HOST = os.getenv('REDIS_HOST') or "127.0.0.1"
|
||||
REDIS_PORT = int(os.getenv('REDIS_PORT') or 6379)
|
||||
|
||||
# mysql 配置
|
||||
MYSQL_USERNAME = os.getenv('MYSQL_USERNAME') or "root"
|
||||
MYSQL_PASSWORD = os.getenv('MYSQL_PASSWORD') or "123456"
|
||||
MYSQL_HOST = os.getenv('MYSQL_HOST') or "127.0.0.1"
|
||||
MYSQL_PORT = int(os.getenv('MYSQL_PORT') or 3306)
|
||||
MYSQL_DATABASE = os.getenv('MYSQL_DATABASE') or "PearAdminFlask"
|
||||
|
||||
# mysql 数据库的配置信息
|
||||
SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{MYSQL_USERNAME}:{MYSQL_PASSWORD}@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DATABASE}"
|
||||
# 默认日志等级
|
||||
LOG_LEVEL = logging.WARN
|
||||
|
||||
|
||||
class TestingConfig(BaseConfig):
|
||||
""" 测试配置 """
|
||||
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:' # 内存数据库
|
||||
|
||||
|
||||
class DevelopmentConfig(BaseConfig):
|
||||
""" 开发配置 """
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = True
|
||||
SQLALCHEMY_ECHO = False
|
||||
|
||||
|
||||
class ProductionConfig(BaseConfig):
|
||||
"""生成环境配置"""
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
SQLALCHEMY_ECHO = False
|
||||
SQLALCHEMY_POOL_RECYCLE = 8
|
||||
|
||||
LOG_LEVEL = logging.ERROR
|
||||
|
||||
|
||||
config = {
|
||||
'development': DevelopmentConfig,
|
||||
'testing': TestingConfig,
|
||||
'production': ProductionConfig
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
from flask import Flask
|
||||
|
||||
from .init_sqlalchemy import db, ma, init_databases
|
||||
from .init_login import init_login_manager
|
||||
from .init_debug_tool import init_debug_tool
|
||||
from .init_template_directives import init_template_directives
|
||||
from .init_error_views import init_error_views
|
||||
|
||||
|
||||
def init_plugs(app: Flask) -> None:
|
||||
init_debug_tool(app)
|
||||
init_login_manager(app)
|
||||
init_databases(app)
|
||||
init_template_directives(app)
|
||||
init_error_views(app)
|
||||
@@ -1,6 +1,7 @@
|
||||
from flask import Flask
|
||||
from flask_debugtoolbar import DebugToolbarExtension
|
||||
|
||||
|
||||
def open_debug_tool(app):
|
||||
def init_debug_tool(app: Flask):
|
||||
toolbar = DebugToolbarExtension()
|
||||
toolbar.init_app(app)
|
||||
@@ -0,0 +1,12 @@
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
root_path = os.path.abspath(os.path.dirname(__file__)).split('applications')[0]
|
||||
dot_env_path = os.path.join(root_path, '.env')
|
||||
flask_env_path = os.path.join(root_path, '.flaskenv')
|
||||
|
||||
if os.path.exists(dot_env_path):
|
||||
load_dotenv(dot_env_path)
|
||||
|
||||
if os.path.exists(flask_env_path):
|
||||
load_dotenv(flask_env_path)
|
||||
@@ -1,8 +1,7 @@
|
||||
from flask import render_template
|
||||
|
||||
|
||||
def init_error(app):
|
||||
|
||||
def init_error_views(app):
|
||||
@app.errorhandler(403)
|
||||
def page_not_found(e):
|
||||
return render_template('errors/403.html'), 403
|
||||
@@ -1,7 +1,7 @@
|
||||
from flask_login import LoginManager
|
||||
|
||||
|
||||
def init_flask_login(app):
|
||||
def init_login_manager(app):
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
|
||||
@@ -10,6 +10,6 @@ def init_flask_login(app):
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
from applications.models.admin_user import User
|
||||
from applications.models import User
|
||||
user = User.query.get(int(user_id))
|
||||
return user
|
||||
@@ -0,0 +1,54 @@
|
||||
from flask import Flask
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_marshmallow import Marshmallow
|
||||
from marshmallow import fields
|
||||
from marshmallow.validate import (
|
||||
URL, Email, Range, Length, Equal, Regexp,
|
||||
Predicate, NoneOf, OneOf, ContainsOnly
|
||||
)
|
||||
|
||||
URL.default_message = '无效的链接'
|
||||
Email.default_message = '无效的邮箱地址'
|
||||
Range.message_min = '不能小于{min}'
|
||||
Range.message_max = '不能小于{max}'
|
||||
Range.message_all = '不能超过{min}和{max}这个范围'
|
||||
Length.message_min = '长度不得小于{min}位'
|
||||
Length.message_max = '长度不得大于{max}位'
|
||||
Length.message_all = '长度不能超过{min}和{max}这个范围'
|
||||
Length.message_equal = '长度必须等于{equal}位'
|
||||
Equal.default_message = '必须等于{other}'
|
||||
Regexp.default_message = '非法输入'
|
||||
Predicate.default_message = '非法输入'
|
||||
NoneOf.default_message = '非法输入'
|
||||
OneOf.default_message = '无效的选择'
|
||||
ContainsOnly.default_message = '一个或多个无效的选择'
|
||||
|
||||
fields.Field.default_error_messages = {
|
||||
"required": "缺少必要数据",
|
||||
"null": "数据不能为空",
|
||||
"validator_failed": "非法数据",
|
||||
}
|
||||
|
||||
fields.Str.default_error_messages = {
|
||||
'invalid': "不是合法文本"
|
||||
}
|
||||
|
||||
fields.Int.default_error_messages = {
|
||||
"invalid": "不是合法整数"
|
||||
}
|
||||
|
||||
fields.Number.default_error_messages = {
|
||||
"invalid": "不是合法数字"
|
||||
}
|
||||
|
||||
fields.Boolean.default_error_messages = {
|
||||
"invalid": "不是合法布尔值"
|
||||
}
|
||||
|
||||
db = SQLAlchemy()
|
||||
ma = Marshmallow()
|
||||
|
||||
|
||||
def init_databases(app: Flask):
|
||||
db.init_app(app)
|
||||
ma.init_app(app)
|
||||
@@ -1,7 +1,7 @@
|
||||
from flask import session
|
||||
|
||||
|
||||
def init_template_global(app):
|
||||
def init_template_directives(app):
|
||||
@app.template_global()
|
||||
def authorize(power):
|
||||
return bool(power in session.get('permissions'))
|
||||
@@ -1,47 +1,9 @@
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_marshmallow import Marshmallow
|
||||
from marshmallow import fields
|
||||
from marshmallow.validate import (
|
||||
URL, Email, Range, Length, Equal, Regexp,
|
||||
Predicate, NoneOf, OneOf, ContainsOnly
|
||||
)
|
||||
|
||||
URL.default_message = '无效的链接'
|
||||
Email.default_message = '无效的邮箱地址'
|
||||
Range.message_min = '不能小于{min}'
|
||||
Range.message_max = '不能小于{max}'
|
||||
Range.message_all = '不能超过{min}和{max}这个范围'
|
||||
Length.message_min = '长度不得小于{min}位'
|
||||
Length.message_max = '长度不得大于{max}位'
|
||||
Length.message_all = '长度不能超过{min}和{max}这个范围'
|
||||
Length.message_equal = '长度必须等于{equal}位'
|
||||
Equal.default_message = '必须等于{other}'
|
||||
Regexp.default_message = '非法输入'
|
||||
Predicate.default_message = '非法输入'
|
||||
NoneOf.default_message = '非法输入'
|
||||
OneOf.default_message = '无效的选择'
|
||||
ContainsOnly.default_message = '一个或多个无效的选择'
|
||||
|
||||
fields.Field.default_error_messages = {
|
||||
"required": "缺少必要数据",
|
||||
"null": "数据不能为空",
|
||||
"validator_failed": "非法数据",
|
||||
}
|
||||
|
||||
fields.Str.default_error_messages = {
|
||||
'invalid': "不是合法文本"
|
||||
}
|
||||
|
||||
fields.Int.default_error_messages = {
|
||||
"invalid": "不是合法整数"
|
||||
}
|
||||
|
||||
fields.Number.default_error_messages = {
|
||||
"invalid": "不是合法数字"
|
||||
}
|
||||
|
||||
fields.Boolean.default_error_messages = {
|
||||
"invalid": "不是合法布尔值"
|
||||
}
|
||||
db = SQLAlchemy()
|
||||
ma = Marshmallow()
|
||||
from .admin_dept import Dept, DeptSchema
|
||||
from .admin_dict import DictType, DictData, DictTypeSchema, DictDataSchema
|
||||
from .admin_log import AdminLog, LogSchema
|
||||
from .admin_photo import Photo, PhotoSchema
|
||||
from .admin_power import Power, PowerSchema, PowerSchema2
|
||||
from .admin_role import Role, RoleSchema
|
||||
from .admin_role_power import role_power
|
||||
from .admin_user import User, UserSchema
|
||||
from .admin_user_role import user_role
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import datetime
|
||||
from applications.models import db, ma
|
||||
from marshmallow import fields,validate
|
||||
from applications.extensions import db, ma
|
||||
from marshmallow import fields, validate
|
||||
|
||||
|
||||
class Dept(db.Model):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import datetime
|
||||
|
||||
from applications.models import db, ma
|
||||
from applications.extensions import db, ma
|
||||
from marshmallow import fields
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import datetime
|
||||
from applications.models import db, ma
|
||||
from applications.extensions import db, ma
|
||||
from marshmallow import fields
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import datetime
|
||||
|
||||
from applications.models import db, ma
|
||||
from applications.extensions import db, ma
|
||||
from marshmallow import fields
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import datetime
|
||||
from applications.models import db, ma
|
||||
from applications.extensions import db, ma
|
||||
from marshmallow import fields
|
||||
|
||||
|
||||
@@ -47,4 +47,3 @@ class PowerSchema2(ma.Schema): # 序列化类
|
||||
create_time = fields.DateTime()
|
||||
update_time = fields.DateTime()
|
||||
enable = fields.Integer()
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import datetime
|
||||
from applications.models import db, ma
|
||||
from applications.extensions import db, ma
|
||||
from marshmallow import fields
|
||||
from applications.models import admin_role_power
|
||||
|
||||
|
||||
class Role(db.Model):
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from applications.models import db
|
||||
from applications.extensions import db
|
||||
|
||||
# 创建中间表
|
||||
role_power = db.Table(
|
||||
"admin_role_power", # 中间表名称
|
||||
db.Column("id", db.Integer, primary_key=True, autoincrement=True, comment='标识'), # 主键
|
||||
db.Column("power_id", db.Integer, db.ForeignKey("admin_power.id"), comment='用户编号'), # 属性 外键
|
||||
db.Column("role_id", db.Integer, db.ForeignKey("admin_role.id"), comment='角色编号'), # 属性 外键
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import datetime
|
||||
from flask_login import UserMixin
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
from applications.models import db, ma
|
||||
from applications.extensions import db, ma
|
||||
from marshmallow import fields
|
||||
from applications.models import admin_user_role
|
||||
from applications.models.admin_dept import Dept
|
||||
from applications.models import Dept
|
||||
|
||||
|
||||
class User(db.Model, UserMixin):
|
||||
@@ -43,4 +42,3 @@ class UserSchema(ma.Schema):
|
||||
return Dept.query.filter_by(id=obj.dept_id).first().dept_name
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from applications.models import db
|
||||
from applications.extensions import db
|
||||
|
||||
# 创建中间表
|
||||
user_role = db.Table(
|
||||
"admin_user_role", # 中间表名称
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
from captcha.image import ImageCaptcha
|
||||
from PIL import Image
|
||||
import random
|
||||
|
||||
|
||||
# 定义随机方法
|
||||
def random_captcha():
|
||||
# 定义一个容器
|
||||
captcha_text = []
|
||||
for i in range(4):
|
||||
# 定义验证码字符
|
||||
c = random.choice(['0', '1', '2', '4', '6', '8'])
|
||||
captcha_text.append(c)
|
||||
# 返回一个随机生成的字符串
|
||||
return ''.join(captcha_text)
|
||||
|
||||
|
||||
# 生成验证码方法
|
||||
def gen_captcha():
|
||||
# 定义图片对象
|
||||
image = ImageCaptcha()
|
||||
# 获取字符串
|
||||
captcha_text = random_captcha()
|
||||
# 生成图像
|
||||
captcha_image = Image.open(image.generate(captcha_text))
|
||||
return captcha_text, captcha_image
|
||||
@@ -1,106 +0,0 @@
|
||||
import datetime
|
||||
import json
|
||||
|
||||
import pymysql
|
||||
from applications.config.database import HOST, USERNAME, PASSWORD, DATABASE, PORT
|
||||
|
||||
DB_CONFIG = {
|
||||
"host": HOST,
|
||||
"port": int(PORT),
|
||||
"user": USERNAME,
|
||||
"password": PASSWORD,
|
||||
"db": DATABASE,
|
||||
"charset": "utf8"
|
||||
}
|
||||
|
||||
|
||||
class SQLManager(object):
|
||||
|
||||
# 初始化实例方法
|
||||
def __init__(self):
|
||||
self.conn = None
|
||||
self.cursor = None
|
||||
self.connect()
|
||||
|
||||
# 连接数据库
|
||||
def connect(self):
|
||||
self.conn = pymysql.connect(
|
||||
host=DB_CONFIG.get('host'),
|
||||
port=DB_CONFIG.get('port'),
|
||||
user=DB_CONFIG.get('user'),
|
||||
passwd=DB_CONFIG.get('password'),
|
||||
db=DB_CONFIG.get('db'),
|
||||
)
|
||||
self.cursor = self.conn.cursor(cursor=pymysql.cursors.DictCursor)
|
||||
|
||||
# 查询多条数据
|
||||
|
||||
def get_list(self, sql, args=None):
|
||||
self.cursor.execute(sql, args)
|
||||
result = self.cursor.fetchall()
|
||||
return result
|
||||
|
||||
# 查询多条数据后关闭
|
||||
def get_list_close(self, sql, args=None):
|
||||
self.cursor.execute(sql, args)
|
||||
result = self.cursor.fetchall()
|
||||
self.close()
|
||||
return result
|
||||
|
||||
# 查询单条数据
|
||||
def get_one(self, sql, args=None):
|
||||
self.cursor.execute(sql, args)
|
||||
result = self.cursor.fetchone()
|
||||
return result
|
||||
|
||||
# 查询单条数据后关闭
|
||||
def get_one_close(self, sql, args=None):
|
||||
self.cursor.execute(sql, args)
|
||||
result = self.cursor.fetchone()
|
||||
self.close()
|
||||
return result
|
||||
|
||||
# 执行单条SQL语句
|
||||
def moddify(self, sql, args=None):
|
||||
self.cursor.execute(sql, args)
|
||||
self.conn.commit()
|
||||
|
||||
# 执行多条SQL语句
|
||||
def multi_modify(self, sql, args=None):
|
||||
self.cursor.executemany(sql, args)
|
||||
self.conn.commit()
|
||||
|
||||
# 创建单条记录的语句
|
||||
def create(self, sql, args=None):
|
||||
self.cursor.execute(sql, args)
|
||||
self.conn.commit()
|
||||
last_id = self.cursor.lastrowid
|
||||
return last_id
|
||||
|
||||
# 关闭数据库cursor和连接
|
||||
def close(self):
|
||||
self.cursor.close()
|
||||
self.conn.close()
|
||||
|
||||
# 进入with语句自动执行
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
# 退出with语句块自动执行
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.close()
|
||||
|
||||
|
||||
"""
|
||||
原生查询dict的datetime解析器
|
||||
用法
|
||||
json.dumps(dictxxx, cls=DateEncoder)
|
||||
"""
|
||||
|
||||
|
||||
class DateEncoder(json.JSONEncoder):
|
||||
def default(self, obj):
|
||||
if isinstance(obj, datetime.datetime):
|
||||
return obj.strftime("%Y-%m-%d %H:%M:%S")
|
||||
else:
|
||||
return json.JSONEncoder.default(self, obj)
|
||||
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 197 KiB After Width: | Height: | Size: 197 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 701 B After Width: | Height: | Size: 701 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 299 KiB After Width: | Height: | Size: 299 KiB |