refactor(api):移除marshal序列化
This commit is contained in:
@@ -40,7 +40,7 @@ Pear Admin Flask 基于 Flask 生态的后台管理系统,该项目旨在为 p
|
||||
+ [flask](https://dormousehole.readthedocs.io/en/latest/)
|
||||
+ [flask-login](https://flask-login.readthedocs.io/en/latest/)
|
||||
+ [flask-sqlalchemy](http://www.pythondoc.com/flask-sqlalchemy/quickstart.html)
|
||||
+ [flask-restful](https://flask-restful.readthedocs.io/en/latest/)
|
||||
+ [flask-restful](https://flask-restful.readthedocs.io/en/latest/)
|
||||
|
||||
|
||||
## 预览
|
||||
@@ -50,9 +50,9 @@ Pear Admin Flask 有以下几个版本:
|
||||
|
||||
>flask 2.x + flask-sqlalchemy + 权限验证 + Flask-APScheduler 定时任务 + marshmallow 序列化与数据验证
|
||||
|
||||
[Mini 分支版本 ](https://gitee.com/pear-admin/pear-admin-flask/tree/mini/)
|
||||
[Mini 分支版本 ](https://gitee.com/pear-admin/pear-admin-flask/tree/mini/)
|
||||
|
||||
>flask 2.x + flask-sqlalchemy + 权限验证 + Flask-restful 序列化与数据验证
|
||||
>flask 2.x + flask-sqlalchemy + Flask-restful + 基于角色的权限管理
|
||||
|
||||
| | |
|
||||
|---------------------|---------------------|
|
||||
@@ -89,8 +89,9 @@ git checkout mini
|
||||
# 创建虚拟环境
|
||||
python -m venv venv
|
||||
|
||||
# 然后使虚拟环境生效(windows,Linux自行解决)
|
||||
# 然后使虚拟环境生效(windows)
|
||||
venv\Scripts\activate
|
||||
# source venv/bin/activate # (Linux激活虚拟环境)
|
||||
|
||||
# 安装依赖
|
||||
pip install -r requirement\requirement-dev.txt
|
||||
@@ -115,16 +116,60 @@ flask init-db
|
||||
|
||||
## 服务器部署
|
||||
### wsgi
|
||||
采用 gunicorn ,配置文件请查看 `gunicorn.conf.py` 。暂时未使用 nginx 作为反向代理,需要的可以自行配置。
|
||||
默认的 flask 程序启动一个应用,一个应用只能用于测试环境下。生成环境采用 gunicorn 开启多进行+多线程启动程序,可以提升程序的并发量。详细配置可以查看 gunicorn 的官方文档,本项目的配置文件请查看 `gunicorn.conf.py` 。
|
||||
|
||||
```python
|
||||
# filename: gunicorn.conf.py
|
||||
import os
|
||||
import multiprocessing
|
||||
|
||||
bind = '0.0.0.0:8000' # 默认部署地址,如果用了nginx的反向代理,建议改成 127.0.0.1:8000
|
||||
backlog = 512
|
||||
chdir = os.path.dirname(os.path.abspath(__file__))
|
||||
timeout = 30
|
||||
worker_class = 'sync'
|
||||
|
||||
workers = multiprocessing.cpu_count() * 2 + 1 # 开启进程数为 CPU核心数 * 2 + 1
|
||||
threads = 2 # 每个进程开启两个线程
|
||||
loglevel = 'info' # 日志的等级
|
||||
access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"'
|
||||
# 日志存放位置
|
||||
if not os.path.exists('logs'):
|
||||
os.mkdir('logs')
|
||||
|
||||
accesslog = os.path.join(chdir, "logs/gunicorn_access.log")
|
||||
errorlog = os.path.join(chdir, "logs/gunicorn_error.log")
|
||||
|
||||
```
|
||||
|
||||
因为是在服务器部署,而且一般做服务器部署的时候都会采用虚拟环境 + 命令行启动。为了能在命令行模式下从虚拟环境启动程序,所以需要专门编写一个 shell 文件进行启动。详细看看 `start.sh`
|
||||
|
||||
```shell
|
||||
cd /home/ubuntu/pear-admin-flask # 进入到项目的根目录
|
||||
source venv/bin/activate # 激活虚拟环境
|
||||
exec gunicorn -c gunicorn.conf.py "applications:create_app('development')" # 运行 gunicorn 指令启动程序
|
||||
```
|
||||
|
||||
注意:如果使用的是全局环境,就可以不用激活虚拟环境。
|
||||
|
||||
|
||||
|
||||
### 守护进程
|
||||
|
||||
程序在服务器环境下,一般是 7*24 小时不间断运行。有时候服务器会因为一些特殊原因宕机自动重启,或者是晚上定时重启电脑让内存处于最佳状态下。在这种情况下如果想要只要是电脑在正常运行,程序就提供服务,就需要开启守护进程。
|
||||
|
||||
|
||||
|
||||
使用下面的命令安装Supervisor:
|
||||
```shell script
|
||||
$ sudo apt install supervisor
|
||||
```
|
||||
|
||||
编辑配置文件
|
||||
|
||||
```shell
|
||||
$ sudo vim /etc/supervisor/conf.d/pear.conf
|
||||
```
|
||||
|
||||
写入项目配置
|
||||
```shell script
|
||||
@@ -141,3 +186,68 @@ killasgroup=true
|
||||
```shell script
|
||||
$ sudo supervisorctl
|
||||
```
|
||||
|
||||
|
||||
|
||||
注意:关于 supervisor 的具体功法参考 [官方文档](http://supervisord.org/index.html)
|
||||
|
||||
|
||||
|
||||
### Nginx
|
||||
|
||||
gunicorn 虽然提供了 Web服务器的功能,但是功能比较局限,拓展性也不强。而 Nginx 就能很好的弥补这部分的不足。所以在服务器一般采用 Nginx + gunicorn 的模式做项目部署。
|
||||
|
||||
Nginx 是一个高性能的 HTTP 和 反向代理 web服务器。在后期的项目性能优化中,可以提供非常多的帮助。例如可以对返回的网页、静态文件进行压缩提升页面的加载速度,当性能达到瓶颈之后可以通过负载均衡让后端直接可以通过加机器解决问题。
|
||||
|
||||
|
||||
|
||||
使用下面的命令安装Nginx
|
||||
|
||||
```shell
|
||||
$ sudo apt install nginx
|
||||
```
|
||||
|
||||
可以直接在 Nginx 的默认配置文件(/etc/nginx/nginx.conf)中写入程序配置,但通常情况下,为了便于组织,我们可以在/etc/nginx/sites-enabled/或是/etc/nginx/conf.d/目录下为我们的Flask程序创建单独的Nginx配置文件。
|
||||
|
||||
```shell
|
||||
$ sudo rm /etc/nginx/sites-enabled/default # 删除默认的示例
|
||||
$ sudo vi /etc/nginx/sites-enabled/pear # 添加自己的示例
|
||||
```
|
||||
|
||||
|
||||
|
||||
> /etc/nginx/sites-enabled/pear
|
||||
|
||||
```shell
|
||||
server {
|
||||
listen 80 default_server; # 监听 80 端口
|
||||
# server_name example.com; # 域名解析
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8000; # 转发的地址,即Gunicorn运行的地址
|
||||
proxy_redirect off;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
更新配置文件后,我们可以通过下面的命令来测试语法正确性:
|
||||
|
||||
```shell
|
||||
$ sudo nginx -t
|
||||
```
|
||||
|
||||
如果一切正常,那么现在可以重启Nginx让配置生效:
|
||||
|
||||
```shell
|
||||
$ sudo service nginx restart
|
||||
```
|
||||
|
||||
当使用反向代理服务器后,Gunicorn不需要再监听外部请求,而是直接监听本地机的某个端口。我们可以使用默认值,即本地机的8000端口,默认绑定 `127.0.0.1:8000` 。
|
||||
|
||||
|
||||
@@ -21,16 +21,22 @@ def create_app(config_name=None):
|
||||
# 引入数据库配置
|
||||
app.config.from_object(common)
|
||||
app.config.from_object(config[config_name])
|
||||
|
||||
# 注册各种插件
|
||||
init_plugs(app)
|
||||
|
||||
# 注册路由
|
||||
init_view(app)
|
||||
|
||||
# 注册接口(restful api)
|
||||
init_api(app)
|
||||
|
||||
# 文件上传
|
||||
configure_uploads(app, photos)
|
||||
|
||||
logo()
|
||||
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
|
||||
logo()
|
||||
|
||||
return app
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from flask import jsonify
|
||||
from flask_restful import Resource
|
||||
from flask_restful import marshal, reqparse
|
||||
from flask_restful import Resource, reqparse
|
||||
|
||||
from applications.common.utils.http import success_api, fail_api
|
||||
from applications.extensions import db
|
||||
@@ -14,9 +13,23 @@ class DepartmentsResource(Resource):
|
||||
# TODO dtree 需要返回状态信息
|
||||
res = {
|
||||
"status": {"code": 200, "message": "默认"},
|
||||
"data": marshal(dept_data, CompanyDepartment.fields())
|
||||
"data": [
|
||||
|
||||
{
|
||||
'deptId': item.id,
|
||||
'parentId': item.parent_id,
|
||||
'deptName': item.dept_name,
|
||||
'sort': item.sort,
|
||||
'leader': item.leader,
|
||||
'phone': item.phone,
|
||||
'email': item.email,
|
||||
'status': item.status,
|
||||
'comment': item.comment,
|
||||
'address': item.address,
|
||||
'create_at': item.create_at.strftime('%Y-%m-%d %H:%M:%S')
|
||||
} for item in dept_data
|
||||
]
|
||||
}
|
||||
print(dept_data)
|
||||
return jsonify(res)
|
||||
|
||||
def post(self):
|
||||
|
||||
@@ -3,7 +3,7 @@ from collections import OrderedDict
|
||||
|
||||
from flask import request, jsonify, current_app
|
||||
from flask_login import current_user
|
||||
from flask_restful import Resource, reqparse, marshal
|
||||
from flask_restful import Resource, reqparse
|
||||
|
||||
from applications.common.utils.http import success_api, fail_api
|
||||
from applications.extensions import db
|
||||
@@ -12,58 +12,61 @@ from applications.models import RightsPower, RightsRole
|
||||
|
||||
def get_render_config():
|
||||
# 网站配置
|
||||
config = dict(logo={
|
||||
# 网站名称
|
||||
"title": current_app.config.get("SYSTEM_NAME"),
|
||||
# 网站图标
|
||||
"image": "/static/admin/admin/images/logo.png"
|
||||
# 菜单配置
|
||||
}, menu={
|
||||
# 菜单数据来源
|
||||
"data": "/api/v1/rights/menu",
|
||||
"collaspe": True,
|
||||
# 是否同时只打开一个菜单目录
|
||||
"accordion": True,
|
||||
"method": "GET",
|
||||
# 是否开启多系统菜单模式
|
||||
"control": False,
|
||||
# 默认选中的菜单项
|
||||
"select": "0",
|
||||
# 是否开启异步菜单,false 时 data 属性设置为菜单数据,false 时为 json 文件或后端接口
|
||||
"async": True
|
||||
}, tab={
|
||||
# 是否开启多选项卡
|
||||
"muiltTab": True,
|
||||
# 切换选项卡时,是否刷新页面状态
|
||||
"keepState": True,
|
||||
# 是否开启 Tab 记忆
|
||||
"session": True,
|
||||
# 最大可打开的选项卡数量
|
||||
"tabMax": 30,
|
||||
"index": {
|
||||
# 标识 ID , 建议与菜单项中的 ID 一致
|
||||
"id": "10",
|
||||
# 页面地址
|
||||
"href": "/admin/welcome",
|
||||
# 标题
|
||||
"title": "首页"
|
||||
}
|
||||
}, theme={
|
||||
# 默认主题色,对应 colors 配置中的 ID 标识
|
||||
"defaultColor": "2",
|
||||
# 默认的菜单主题 dark-theme 黑 / light-theme 白
|
||||
"defaultMenu": "dark-theme",
|
||||
# 是否允许用户切换主题,false 时关闭自定义主题面板
|
||||
"allowCustom": True
|
||||
}, colors=[{
|
||||
"id": "1",
|
||||
"color": "#2d8cf0"
|
||||
},
|
||||
{
|
||||
config = {
|
||||
'logo': {
|
||||
# 网站名称
|
||||
"title": current_app.config.get("SYSTEM_NAME"),
|
||||
# 网站图标
|
||||
"image": "/static/admin/admin/images/logo.png"
|
||||
# 菜单配置
|
||||
},
|
||||
'menu': {
|
||||
# 菜单数据来源
|
||||
"data": "/api/v1/rights/menu",
|
||||
"collaspe": True,
|
||||
# 是否同时只打开一个菜单目录
|
||||
"accordion": True,
|
||||
"method": "GET",
|
||||
# 是否开启多系统菜单模式
|
||||
"control": False,
|
||||
# 默认选中的菜单项
|
||||
"select": "0",
|
||||
# 是否开启异步菜单,false 时 data 属性设置为菜单数据,false 时为 json 文件或后端接口
|
||||
"async": True
|
||||
},
|
||||
'tab': {
|
||||
# 是否开启多选项卡
|
||||
"muiltTab": True,
|
||||
# 切换选项卡时,是否刷新页面状态
|
||||
"keepState": True,
|
||||
# 是否开启 Tab 记忆
|
||||
"session": True,
|
||||
# 最大可打开的选项卡数量
|
||||
"tabMax": 30,
|
||||
"index": {
|
||||
# 标识 ID , 建议与菜单项中的 ID 一致
|
||||
"id": "10",
|
||||
# 页面地址
|
||||
"href": "/admin/welcome",
|
||||
# 标题
|
||||
"title": "首页"
|
||||
}
|
||||
},
|
||||
'theme': {
|
||||
# 默认主题色,对应 colors 配置中的 ID 标识
|
||||
"defaultColor": "2",
|
||||
# 默认的菜单主题 dark-theme 黑 / light-theme 白
|
||||
"defaultMenu": "dark-theme",
|
||||
# 是否允许用户切换主题,false 时关闭自定义主题面板
|
||||
"allowCustom": True
|
||||
},
|
||||
'colors': [{
|
||||
"id": "1",
|
||||
"color": "#2d8cf0"
|
||||
}, {
|
||||
"id": "2",
|
||||
"color": "#5FB878"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
"id": "3",
|
||||
"color": "#1E9FFF"
|
||||
}, {
|
||||
@@ -72,13 +75,16 @@ def get_render_config():
|
||||
}, {
|
||||
"id": "5",
|
||||
"color": "darkgray"
|
||||
}
|
||||
], links=current_app.config.get("SYSTEM_PANEL_LINKS"), other={
|
||||
# 主页动画时长
|
||||
"keepLoad": 1200,
|
||||
# 布局顶部主题
|
||||
"autoHead": False
|
||||
}, header=False)
|
||||
}],
|
||||
'links': current_app.config.get("SYSTEM_PANEL_LINKS"),
|
||||
'other': {
|
||||
# 主页动画时长
|
||||
"keepLoad": 1200,
|
||||
# 布局顶部主题
|
||||
"autoHead": False
|
||||
},
|
||||
'header': False
|
||||
}
|
||||
return config
|
||||
|
||||
|
||||
@@ -99,7 +105,23 @@ def make_menu_tree():
|
||||
if p.type == 0 or p.type == 1:
|
||||
powers.append(p)
|
||||
|
||||
power_dict = marshal(powers, RightsPower.fields2()) # 生成可序列化对象
|
||||
# power_dict = marshal(powers, RightsPower.fields2()) # 生成可序列化对象
|
||||
power_dict = [
|
||||
{
|
||||
'id': item.id,
|
||||
'title': item.name,
|
||||
'type': item.type,
|
||||
'code': item.code,
|
||||
'href': item.url,
|
||||
'openType': item.open_type,
|
||||
'parent_id': item.parent_id,
|
||||
'icon': item.icon,
|
||||
'sort': item.sort,
|
||||
'enable': item.enable,
|
||||
'update_at': item.update_at.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'create_at': item.create_at.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
} for item in powers
|
||||
]
|
||||
power_dict.sort(key=lambda x: x['id'], reverse=True)
|
||||
|
||||
menu_dict = OrderedDict()
|
||||
@@ -156,12 +178,26 @@ class RightRightsResource(Resource):
|
||||
"""获取选择父节点"""
|
||||
|
||||
power = RightsPower.query.all()
|
||||
power_data = marshal(power, RightsPower.fields())
|
||||
# power_data = marshal(power, RightsPower.fields())
|
||||
power_data = [
|
||||
{
|
||||
'powerId': item.id,
|
||||
'powerName': item.name,
|
||||
'powerType': item.type,
|
||||
'powerUrl': item.url,
|
||||
'openType': item.open_type,
|
||||
'parentId': item.parent_id,
|
||||
'icon': item.icon,
|
||||
'sort': item.sort,
|
||||
'create_at': item.create_at.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'update_at': item.update_at.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'enable': item.enable,
|
||||
} for item in power
|
||||
]
|
||||
power_data.append({"powerId": 0, "powerName": "顶级权限", "parentId": -1})
|
||||
res = {
|
||||
"status": {"code": 200, "message": "默认"},
|
||||
"data": power_data
|
||||
|
||||
}
|
||||
return res
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import os
|
||||
|
||||
from flask import request, jsonify, current_app
|
||||
from flask_restful import Resource, marshal
|
||||
from flask_restful import Resource
|
||||
from sqlalchemy import desc
|
||||
|
||||
from applications.common.utils.http import fail_api, success_api, table_api
|
||||
@@ -19,7 +19,18 @@ class FilePhotosResource(Resource):
|
||||
).paginate(page=page,
|
||||
per_page=limit,
|
||||
error_out=False)
|
||||
data = marshal(photo_paginate.items, FilePhoto.fields())
|
||||
# data = marshal(photo_paginate.items, FilePhoto.fields())
|
||||
data = [
|
||||
{
|
||||
'id': item.id,
|
||||
'name': item.name,
|
||||
'href': item.href,
|
||||
'mime': item.mime,
|
||||
'size': item.size,
|
||||
'ext': item.ext,
|
||||
'create_at': item.create_at,
|
||||
} for item in photo_paginate.items
|
||||
]
|
||||
return table_api(result={'items': data,
|
||||
'total': photo_paginate.total, },
|
||||
code=0)
|
||||
|
||||
@@ -24,6 +24,7 @@ class LoginResource(Resource):
|
||||
req = login_req.parse_args()
|
||||
|
||||
s_code = session.get("code", None)
|
||||
session["code"] = None
|
||||
|
||||
if req.captcha != s_code:
|
||||
return fail_api(message="验证码错误")
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from flask_restful import fields
|
||||
|
||||
from applications.extensions import db
|
||||
|
||||
from ..base import BaseModel
|
||||
@@ -12,15 +10,3 @@ class FilePhoto(db.Model, BaseModel):
|
||||
href = db.Column(db.String(255))
|
||||
mime = db.Column(db.CHAR(50), nullable=False)
|
||||
size = db.Column(db.CHAR(30), nullable=False)
|
||||
|
||||
@staticmethod
|
||||
def fields():
|
||||
return {
|
||||
'id': fields.Integer,
|
||||
'name': fields.String,
|
||||
'href': fields.String,
|
||||
'mime': fields.String,
|
||||
'size': fields.String,
|
||||
'ext': fields.String,
|
||||
'create_at': fields.DateTime,
|
||||
}
|
||||
|
||||
@@ -14,17 +14,3 @@ class LoggingModel(db.Model, BaseModel):
|
||||
ip = db.Column(db.String(255))
|
||||
success = db.Column(db.Boolean, default=True)
|
||||
user_agent = db.Column(db.Text)
|
||||
|
||||
@staticmethod
|
||||
def fields():
|
||||
return {
|
||||
'id': fields.Integer,
|
||||
'method': fields.String,
|
||||
'uid': fields.String,
|
||||
'url': fields.Url,
|
||||
'desc': fields.String,
|
||||
'ip': fields.String,
|
||||
'success': fields.Boolean,
|
||||
'user_agent': fields.String,
|
||||
'create_at': fields.DateTime,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from flask_restful import fields
|
||||
|
||||
from applications.extensions import db
|
||||
from ..base import BaseModel
|
||||
|
||||
@@ -18,37 +16,3 @@ class RightsPower(db.Model, BaseModel):
|
||||
enable = db.Column(db.Boolean, comment='是否开启')
|
||||
|
||||
parent = db.relationship("RightsPower", remote_side=[id]) # 自关联
|
||||
|
||||
@staticmethod
|
||||
def fields():
|
||||
return {
|
||||
'powerId': fields.String(attribute="id"),
|
||||
'powerName': fields.String(attribute="name"),
|
||||
'powerType': fields.String(attribute="type"),
|
||||
'powerUrl': fields.String(attribute="url"),
|
||||
'openType': fields.String(attribute="open_type"),
|
||||
'parentId': fields.String(attribute="parent_id"),
|
||||
'icon': fields.String,
|
||||
'sort': fields.Integer,
|
||||
'create_at': fields.DateTime,
|
||||
'update_at': fields.DateTime,
|
||||
'enable': fields.Integer,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def fields2():
|
||||
return {
|
||||
'id': fields.Integer,
|
||||
'title': fields.String(attribute="name"),
|
||||
'type': fields.String,
|
||||
'code': fields.String,
|
||||
'href': fields.String(attribute="url"),
|
||||
'openType': fields.String(attribute="open_type"),
|
||||
'parent_id': fields.Integer,
|
||||
'icon': fields.String,
|
||||
'sort': fields.Integer,
|
||||
'enable': fields.Boolean,
|
||||
'update_at': fields.DateTime,
|
||||
'create_at': fields.DateTime,
|
||||
|
||||
}
|
||||
|
||||
@@ -15,17 +15,3 @@ class RightsRole(db.Model, BaseModel):
|
||||
sort = db.Column(db.Integer, comment='排序')
|
||||
|
||||
power = db.relationship('RightsPower', secondary="rt_role_power", backref=db.backref('role'))
|
||||
|
||||
@staticmethod
|
||||
def fields():
|
||||
return {
|
||||
'id': fields.Integer,
|
||||
'roleName': fields.String(attribute="name"),
|
||||
'roleCode': fields.String(attribute="code"),
|
||||
'enable': fields.Boolean,
|
||||
'comment': fields.String,
|
||||
'details': fields.String,
|
||||
'sort': fields.Integer,
|
||||
'create_at': fields.DateTime,
|
||||
'update_at': fields.DateTime,
|
||||
}
|
||||
|
||||
@@ -18,6 +18,10 @@ class CompanyDepartment(db.Model, BaseModel):
|
||||
|
||||
@staticmethod
|
||||
def fields():
|
||||
"""
|
||||
定义模型的常用输出字段,新手请忽略。可以简化字段序列化操作,
|
||||
详细操作请查看 flask-restful marshal 的用法
|
||||
"""
|
||||
return {
|
||||
'deptId': fields.Integer(attribute="id"),
|
||||
'parentId': fields.Integer(attribute="parent_id"),
|
||||
|
||||
@@ -21,25 +21,9 @@ class CompanyUser(db.Model, UserMixin, BaseModel):
|
||||
role = db.relationship('RightsRole', secondary="rt_user_role", backref=db.backref('user'), lazy='dynamic')
|
||||
|
||||
def set_password(self, password):
|
||||
"""设置密码,对密码进行加密存储"""
|
||||
self.password_hash = generate_password_hash(password)
|
||||
|
||||
def validate_password(self, password):
|
||||
"""校验密码方法"""
|
||||
return check_password_hash(self.password_hash, password)
|
||||
|
||||
@staticmethod
|
||||
def fields():
|
||||
return {
|
||||
'id': fields.Integer,
|
||||
'username': fields.String,
|
||||
'realname': fields.String,
|
||||
'mobile': fields.String,
|
||||
'avatar': fields.Url,
|
||||
'comment': fields.String,
|
||||
# 'password_hash': fields.String,
|
||||
'enable': fields.Boolean,
|
||||
# 'dept_id': fields.Integer,
|
||||
'dept_id': fields.Integer,
|
||||
'create_at': fields.DateTime,
|
||||
'update_at': fields.DateTime,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
from flask import Blueprint, request, render_template
|
||||
from sqlalchemy import desc
|
||||
from flask_restful import marshal
|
||||
|
||||
from applications.common.utils.http import table_api
|
||||
from applications.common.utils.rights import permission_required
|
||||
@@ -24,7 +23,19 @@ def login_log():
|
||||
url='/api/v1/passport/login').order_by(
|
||||
desc(LoggingModel.create_at)).paginate(
|
||||
page=page, per_page=limit, error_out=False)
|
||||
data = marshal(log_paginate.items, LoggingModel.fields())
|
||||
data = [
|
||||
{
|
||||
'id': item.id,
|
||||
'method': item.method,
|
||||
'uid': item.uid,
|
||||
'url': item.url,
|
||||
'desc': item.desc,
|
||||
'ip': item.ip,
|
||||
'success': item.success,
|
||||
'user_agent': item.user_agent,
|
||||
'create_at': item.create_at.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
} for item in log_paginate.items
|
||||
]
|
||||
|
||||
return table_api(result={'items': data,
|
||||
'total': log_paginate.total, },
|
||||
@@ -40,7 +51,19 @@ def operate_log():
|
||||
LoggingModel.url != '/api/v1/passport/login').order_by(
|
||||
desc(LoggingModel.create_at)).paginate(
|
||||
page=page, per_page=limit, error_out=False)
|
||||
data = marshal(log_paginate.items, LoggingModel.fields())
|
||||
data = [
|
||||
{
|
||||
'id': item.id,
|
||||
'method': item.method,
|
||||
'uid': item.uid,
|
||||
'url': item.url,
|
||||
'desc': item.desc,
|
||||
'ip': item.ip,
|
||||
'success': item.success,
|
||||
'user_agent': item.user_agent,
|
||||
'create_at': item.create_at.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
} for item in log_paginate.items
|
||||
]
|
||||
return table_api(result={'items': data,
|
||||
'total': log_paginate.total, },
|
||||
code=0)
|
||||
|
||||
Reference in New Issue
Block a user