flask-admin+Flask-WTF 实现实现增删改查

背景:

flask-admin+flask-wtf在网上可以搜索到很多资料,但有价值的很少,或许是太简单,或者是很少人这么用,或者。。。,本文将作者近礼拜摸索到的一点经验分享出来,给自己做个记录。

材料:

1、Flask==3.1.0

2、flask-babel==4.0.0

3、Flask-SQLAlchemy==3.1.1

4、Flask-WTF==1.2.2

5、WTForms==3.2.1

制作:

1、安装响应库:依托于pip 安装很简单,省略)

2、应用集成:Flask-WTF 无需集成,安装库文件后直接可以使用

3、flask-admin的集成与配置:

a、项目目录如下

b、在admin 目录下创建base_admin.py文件,便于工程化配置

class MyAdminIndexView(AdminIndexView):
    # def is_accessible(self):
    #     return current_user.is_authenticated

    # def inaccessible_callback(self, name, **kwargs):
    #     return redirect('/admin/login')

    @expose("/")
    def homepage(self):
        isAuth = current_user.is_authenticated
        return self.render("admin/index.html", isAuth=isAuth)


class BaseAdmin:
    # admin视图初始化.
    def init_app(app):
        admin = Admin(
            app,  
            name=u"AI阅读管理系统",
            index_view=MyAdminIndexView(name="首页"),
            template_mode='bootstrap3'
        )
        admin.add_view(MyOrganView(name='机构管理', endpoint="/org"))
        admin.add_view(MyUserView(name="用户管理", endpoint="/user", category='系统管理'))
        admin.add_view(MyCategoryView(name="权限管理", endpoint="/cate", category='系统管理'))
        admin.add_view(MyRoleView(name="角色管理", endpoint="role", category='系统管理'))
        admin.add_view(MyAiConfigView(db.session, name='Dify配置', category='Dify管理'))
        admin.add_view(MyAiFlowView(db.session, name='Dify工作流', category='Dify管理'))
        admin.add_view(DifyFlowTypeView(db.session, name='工作流类型', category='Dify管理'))
        admin.add_view(MyUrlsView(name="网站管理", endpoint="/urls", category='AI阅读'))
        admin.add_view(MyNewsView(name="今日要闻", endpoint="/news", category='AI阅读'))
        return admin

c、 基于flask-admin的视图基础的有2种,自定义视图(BaseView)和模型视图(ModelView)下面分别介绍本文作者理解的2种视图及用法。

自定义视图(BaseView)

1、自定义视图需要集成baseview基类,特点是集成这个基类后增删改查都需要自己去实现

2、务必要实现 @expose('/')的函数重构(默认进入这个视图模块的路径)

3、样例代码:

from flask_admin import BaseView, expose
from flask_login import current_user
from flask import redirect, url_for


class MyUrlsView(BaseView):

    def is_accessible(self):
        return current_user.is_authenticated and current_user.is_pass('SYS_URLS_ITEM')
    
    def inaccessible_callback(self, name, **kwargs):
        return redirect('/admin/login')

    @expose('/')
    def index(self):
        isAuth = current_user.is_authenticated
        return self.render('/urlsSetList.html', isAuth=isAuth)

    @expose('/detail/<string:news_id>')
    def detail(self, news_id):
        return self.render('newsDetail.html', news_id=news_id)

4、样例代码中的@expose('/detail/<string:news_id>') 只是演示传参方式 ,将业务导流到了其他的API实现

5、样例代码中的is_accessible与inaccessible_callback 函数是权限控制用的,本文不做介绍。

6、如果想把自定义的页面集成到flask-admin 中,切记自己的视图文件需要集成自{% extends 'admin/master.html' %}

{% extends 'admin/master.html' %}
{% block head %}
<script type="text/javascript" src="/static/bootstrap-3.4.1/js/jquery.min.js"></script>
<style>
    .myactive{
        z-index: 2;
        color: #ffffff;
        background-color: #2fa4e7;
        border-color: #2fa4e7;
        background-image: glyphicon glyphicon-ok;
    }
    
</style>
{% endblock %}
{% block body %}

  自己的html
{% endblock %}
{% block tail_js %}
<script src="/static/bootstrap-3.4.1/js/bootstrap.min.js"></script>
<script type="text/javascript">

  自己的js及控制
</script>
{% endblock %}

8、效果:

9、如果实现其他的视图需要集成flask-admin响应的模板的,可以直接从flask-admin/flask_admin/templates/bootstrap3/admin/base.html at v1.6.1 · pallets-eco/flask-admin · GitHubSimple and extensible administrative interface framework for Flask - flask-admin/flask_admin/templates/bootstrap3/admin/base.html at v1.6.1 · pallets-eco/flask-adminicon-default.png?t=O83Ahttps://github.com/pallets-eco/flask-admin/blob/v1.6.1/flask_admin/templates/bootstrap3/admin/base.html

 10、位置

11、学习它的最好方式是抄袭它的源码 

模型视图(ModelView)

1、直接上代码

a、视图代码

from flask_admin.babel import gettext
from flask_admin.contrib.sqla import ModelView
from apps.src.db.entity.dify_flow_type import DifyFlowType
from apps.src.services.vo.news_dify_flow_type_vo import NewsDifyFlowTypeVo
from flask_login import current_user
from flask_admin import expose
from flask import redirect, url_for, request, flash
from apps.src.db.entity.exts import db
from wtforms import form, fields, validators
from wtforms.widgets import TextArea


class DivyFlowTypeForm(form.Form):
    status = fields.HiddenField('状态', default=0)
    type_name = fields.StringField('类型名称', validators=[validators.DataRequired()])
    type_code = fields.StringField('类型CODE', validators=[validators.DataRequired()])
    description = fields.TextAreaField('描述', widget=TextArea())


class DifyFlowTypeView(ModelView):
    def is_accessible(self):
        return current_user.is_authenticated and current_user.is_admin == 1
    
    def inaccessible_callback(self, name, **kwargs):
        return redirect(url_for('admin/login'))

        # 列表中文翻译
    column_labels = {
        'created_at': '创建时间',
        'updated_at': '修改时间',
        'type_name': '类型名称',
        'type_code': '类型CODE',
        'description': '工作流描述'
    }
  
    # 重写新增
    @expose('/new/', methods=('GET', 'POST'))
    def create_view(self):
        bean = DifyFlowType()
        form = DivyFlowTypeForm(request.form, obj=bean)
        if self.validate_form(form):
            model = self.create_model(form)
            if model:
                flash(gettext('Record was successfully created.'), 'success')
                return redirect(self.get_save_return_url(model, is_created=True))

        if self.create_modal and request.args.get('modal'):
            template = self.create_modal_template
        else:
            template = self.create_template
        return self.render(template, form=form)

    @expose('/edit/', methods=('GET', 'POST'))
    def edit_view(self):
        id = request.args['id']
        return_url = request.values.get('url') or self.get_url('.index_view')
        model = self.get_one(id)
        if model is None:
            flash(gettext('Record does not exist.'), 'error')
            return redirect(return_url)
        form = DivyFlowTypeForm(request.form, obj=model)

        if self.validate_form(form):
            if self.update_model(form, model):
                flash(gettext('Record was successfully saved.'), 'success')
                # save button
                return redirect(self.get_save_return_url(model, is_created=False))   

        if request.method == 'GET' or form.errors:
            self.on_form_prefill(form, id)

        if self.edit_modal and request.args.get('modal'):
            template = self.edit_modal_template
        else:
            template = self.edit_template
        return self.render(template, form=form)
    
    # 隐藏主键
    column_display_pk = False
    # 列表排序,默认倒序
    column_default_sort = [('updated_at', True), ('created_at', True)]
    # 有此项会显示分页
    page_size = 10
    # 列表查询
    column_searchable_list = ['type_name']
    # 列表显示的字段
    column_list = ('updated_at', 'type_name', 'description')
    # 编辑页面有用
    form_columns = ('type_name', 'type_code', 'description')

    def __init__(self, session, **kwargs):
        # You can pass name and other parameters if you want to
        super(DifyFlowTypeView, self).__init__(DifyFlowType, session, **kwargs)

b、模型代码

from .exts import db
from flask_login import UserMixin


class DifyFlowType(db.Model, UserMixin):
    __tablename__ = 'dify_flow_type'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    created_at = db.Column(db.TIMESTAMP)
    updated_at = db.Column(db.TIMESTAMP)
    status = db.Column(db.Integer)
    type_name = db.Column(db.String(128))
    type_code = db.Column(db.Integer)
    description = db.Column(db.String(200))

c、关联类代码

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

2、直接上效果

a、列表

 

b、新增

 

c、修改

 

3、难点解说:

a、flask-admin 默认的表单中针对下拉框选择的支持感觉很弱,或许是我没接触好,找了很久只找到了主子表的这种内联表单和下拉框选择,效果很是不行,直接显示类名,受不了

    # form_ajax_refs = {
    #                 'ai_config': {
    #                     'fields': ['ai_name', 'ai_code'],
    #                     'placeholder': '请输入',
    #                     'page_size': 10,
    #                     'minimum_input_length': 0,
    #                 },'organ':{
    #                     'fields': ['org_name'],
    #                     'placeholder': '请输入',
    #                     'page_size': 10,
    #                     'minimum_input_length': 0,
    #                 }
    #             }

b、使用flask-admin 内嵌wtforms 表单的方式务必处理好 实体外键和relationship 的使用

 org_id = db.Column(db.Integer(), db.ForeignKey('organs.id'))

 organ = db.relationship('Organ',  backref=db.backref('ai_config', lazy='dynamic'))

c、自定义类中只能使用模型中的relationship 对应的实体映射名,否则很难处理编辑时的绑定

class AiConfigForm(form.Form):

    def get_countries():
        return Organ.query.order_by('updated_at').all()

    organ = QuerySelectField('机构名称', validators=[validators.DataRequired()], query_factory=get_countries, get_label='org_name') 

d、 当然可以使用自定义的方式实现,但不推荐,不太好处理。

    @expose('/new/', methods=('GET', 'POST'))
    def create_view(self):
        # if request.method == 'POST':
        #     form = AiFlowForm(request.form)
        #     if form.validate:
        #         form.populate_obj(AiFlow)
        #         aiflow = NewsAiFlowVo.to_entity(AiFlow)
        #         db.session.add(aiflow)
        #         db.session.commit()
        #         return redirect('/admin/aiflow/')
        # else:
        #     form = AiFlowForm(request.form)

 下篇介绍

国际化

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/940786.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++简明教程(文章要求学过一点C语言)(3)

一、编程工具大揭秘——IDE 当我们准备踏入 C 编程的奇妙世界时&#xff0c;首先要认识一个重要的“魔法盒子”——集成开发环境&#xff08;IDE&#xff09;。IDE 就像是一个全能的编程工作室&#xff0c;它把我们写代码所需要的各种工具都整合到了一起&#xff0c;让编程这件…

STM32-笔记5-按键点灯(中断方法)

1、复制03-流水灯项目&#xff0c;重命名06-按键点灯&#xff08;中断法&#xff09; 在\Drivers\BSP目录下创建一个文件夹exti&#xff0c;在该文件夹下&#xff0c;创建两个文件exti.c和exti.h文件&#xff0c;并且把这两个文件加载到项目中&#xff0c;打开项目工程文件 加载…

实现 WebSocket 接入文心一言

目录 什么是 WebSocket&#xff1f; 为什么需要 WebSocket&#xff1f; HTTP 的局限性 WebSocket 的优势 总结&#xff1a;HTTP 和 WebSocket 的区别 WebSocket 的劣势 WebSocket 常见应用场景 WebSocket 握手过程 WebSocket 事件处理和生命周期 WebSocket 心跳机制 …

leetcode-80.删除有序数组的重复项II-day12

总结&#xff1a;不必过于死磕一道题目&#xff0c;二十分钟没做出来就可参考题解

RTOS之邮箱

邮箱 邮箱 (Mailbox) 服务是实时操作系统中一种常用的线程间通信机制。它提供了一种高效、低开销的消息传递方式&#xff0c;允许线程之间交换固定大小的数据。 1. 邮箱的应用场景 考虑一个简单的示例&#xff1a;线程 1 负责检测按键状态并将状态信息发送出去&#xff0c;线程…

凯酷全科技抖音电商服务的卓越践行者

在数字经济蓬勃发展的今天&#xff0c;电子商务已成为企业增长的新引擎。随着短视频平台的崛起&#xff0c;抖音作为全球领先的短视频社交平台&#xff0c;不仅改变了人们的娱乐方式&#xff0c;也为品牌和商家提供了全新的营销渠道。厦门凯酷全科技有限公司&#xff08;以下简…

AI的进阶之路:从机器学习到深度学习的演变(三)

&#xff08;承接上集&#xff1a;AI的进阶之路&#xff1a;从机器学习到深度学习的演变&#xff08;二&#xff09;&#xff09; 四、深度学习&#xff08;DL&#xff09;&#xff1a;机器学习的革命性突破 深度学习&#xff08;DL&#xff09;作为机器学习的一个重要分支&am…

数据集-目标检测系列 车牌检测识别 数据集 CCPD2019

车牌检测&识别 数据集 CCPD2019 DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 需要更多数据资源和技术解决方案&#xff0c;知识星球&#xff1a; “DataBall - X 数据球(free)” 贵在坚持&#xff01; 数据样…

安全算法基础(一)

安全算法是算法的分支之一&#xff0c;还的依靠大量的数学基础进行计算&#xff0c;本文参照兜哥的AI安全样本对抗&#xff0c;做一个简单的算法安全概括&#xff0c;从零学习。 最新的安全算法对于我们常规的攻击样本检测&#xff0c;效果是不理想的&#xff0c;为了探究其原…

[SZ901]JTAG高速下载设置(53Mhz)

SZ901最高支持JTAG 53MHz的时钟频率&#xff0c;下载bit文件和固化程序的速度提升非常明显。 首先设置参数 1&#xff0c;将JTAG0 分频系数修改为3 2&#xff0c;设置参数&#xff0c;更新参数。&#xff08;完成&#xff09; 打开VIVADO VIVADO 正常识别FPGA&#xff0c;速…

图漾相机-ROS1_SDK_ubuntu版本编译(新版本)

文章目录 官网编译文档链接官网SDK下载链接1、下载 Camport ROS1 SDK1.下载git2、下载链接 2、准备编译工作1、安装 catkin2、配置环境变量3. 将Camport3中的linux库文件拷贝到 user/lib目录下4、修改lunch文件制定相机&#xff08;可以放在最后可以参考在线文档&#xff09;**…

openbmc hwmon与sensor监控

1.说明 参考文档: https://github.com/openbmc/entity-manager/blob/master/docs/entity_manager_dbus_api.mdhttps://github.com/openbmc/entity-manager/blob/master/docs/my_first_sensors.md 1.1 简单介绍 注意: 本节是快速浏览整个sensor框架&#xff0c;了解大致open…

Java --- 多线程

目录 前言&#xff1a; 一.线程的创建&#xff1a; 1.通过继承 Thread 类来创建线程&#xff1a; 2.通过Runnable接口创建线程&#xff1a; 3.通过Java8引入的lambda语法&#xff1a; 线程的优先级&#xff1a; 二.线程的生命周期&#xff1a; 三. 中断线程&#xff1a…

使用 acme.sh 申请域名 SSL/TLS 证书完整指南

使用 acme.sh 申请域名 SSL/TLS 证书完整指南 简介为什么选择 acme.sh 和 ZeroSSL&#xff1f;前置要求安装过程 步骤一&#xff1a;安装 acme.sh步骤二&#xff1a;配置 ZeroSSL 证书申请 方法一&#xff1a;手动 DNS 验证&#xff08;推荐新手使用&#xff09;方法二&#xf…

Flutter组件————Scaffold

Scaffold Scaffold 是一个基础的可视化界面结构组件&#xff0c;它实现了基本的Material Design布局结构。使用 Scaffold 可以快速地搭建起包含应用栏&#xff08;AppBar&#xff09;、内容区域&#xff08;body&#xff09;、抽屉菜单&#xff08;Drawer&#xff09;、底部导…

YOLOv8目标检测(七)_AB压力测试

YOLOv8目标检测(一)_检测流程梳理&#xff1a;YOLOv8目标检测(一)_检测流程梳理_yolo检测流程-CSDN博客 YOLOv8目标检测(二)_准备数据集&#xff1a;YOLOv8目标检测(二)_准备数据集_yolov8 数据集准备-CSDN博客 YOLOv8目标检测(三)_训练模型&#xff1a;YOLOv8目标检测(三)_训…

SpringBoot的创建方式

SpringBoot创建的五种方式 1.通过Springboot官网链接下载 注意SpringBoot项目的封装方式默认为Jar 需要查看一下&#xff0c;自己的Maven版本是否正确 创建成功 2.通过 aliyun官网链接下载 修改服务路径为阿里云链接 创建成功 3.通过Springboot官网下载 点击&#xff0c;拉到最…

批处理理解

初识批处理 如何批处理&#xff1a; 命名&#xff1a;.bat 方法&#xff1a;创建一个记事本文件&#xff0c;然后将其扩展改为.bat 批处理作用&#xff1a;自上而下成批处理每一条DOS命令&#xff0c;直到执行到最后一条。运行环境&#xff1a;当然是我们cmd了 回归我学过的…

概率论得学习和整理29: 用EXCEL 描述二项分布

目录 1 关于二项分布的基本内容 2 二项分布的概率 2.1 核心要素 2.2 成功K次的概率&#xff0c;二项分布公式 2.3 期望和方差 2.4 具体试验 2.5 概率质量函数pmf 和cdf 3 二项分布的pmf图的改进 3.1 改进折线图 3.2 如何生成这种竖线图呢 4 不同的二项分布 4.1 p0.…

全志H618 Android12修改doucmentsui功能菜单项

背景: 由于当前的文件管理器在我们的产品定义当中,某些界面有改动的需求,所以需要在Android12 rom中进行定制以符合当前产品定义。 需求: 在进入File文件管理器后,查看...功能菜单时,有不需要的功能菜单,需要隐藏,如:新建窗口、不显示的文件夹、故代码分析以及客制…