说道 Stark 你是不是不会想到他——Tony Stark,超级英雄钢铁侠,这也是我的偶像。

不过我们今天要开发的 Stark 组件,倒是跟他的人工智能助手 JARVIS 有些类似,是帮助我们快速开发数据库增、删、改、查操作、应用各种功能的开发助手。

Stark 组件:快速开发神器 —— 页面显示

  • 一、数据
    • Role
    • Department
    • Team
    • RbacUserInfo
  • 二、查
    • ManyToManyField 处理
    • DateTimeField 处理
  • 三、增
    • IntegerField 处理
  • 四、改
  • 五、删

一、数据

在敲代码之前,通过 admin 来创建一些基本数据好供我们显示。

Role

Department

Team

RbacUserInfo

二、查

查看信息是第一位,因此我们先来做显示页面,这里要想一个问题,显示信息,并不能吧数据库表的所有字段都显示,那岂不是密码都放到页面上来了,因此,我们需要定制显示的字段,在不同的类中定义 displayList 属性表示显示字段。

在 StarkHandler 类中编写查看视图:

# Stark/main.pydef checkView(self, request, *args, **kwargs):"""查看功能视图函数"""# --------------------- 1.显示表格 ---------------------displayList = self.getDisplayList(request, *args, **kwargs)# 1.1、处理表格表头headerList = []if displayList:for item in displayList:verboseName = self.model._meta.get_field(item).verbose_nameheaderList.append(verboseName)else:headerList.append(self.model._meta.model_name)# 1.2、处理表格内容bodyList = []dataList = self.model.objects.all()for row in dataList:rowList = []if displayList:for item in displayList:rowList.append(getattr(row, item))else:rowList.append(row)bodyList.append(rowList)return render(request, "stark/checkView.html", {"headerList": headerList,"bodyList": bodyList,"dataList": dataList})

getDisplayList 方法就是用于获取要显示的字段:

# Stark/main.pydef getDisplayList(self, request, *args, **kwargs):"""获取页面显示的表格,预留自定义扩展定制显示内容"""value = []if self.displayList:value.extend(self.displayList)return value

在 RbacUserHandler 类中定义要显示的字段:

# RBAC/views/rbacUserinfo.py
from Stark.main import StarkHandlerclass RbacUserHandler(StarkHandler):def __init__(self, site, modelClass, prefix):super().__init__(site, modelClass, prefix)self.displayList = ["username", "email", "score", "grade", "roles", "team", "department", "dateJoined"]

继承 formwork.html 编写 check 页面:

{% extends 'formwork.html' %}
{% block content %}<div class="container" style="width: 100%; background-color: rgba(245, 245, 245, 0.7)"><form method="post">{% csrf_token %}<table class="table table-hover table-bordered table-striped"><thead><tr class="info">{% for header in headerList %}<th>{{ header }}</th>{% endfor %}</tr></thead><tbody>{% for row in bodyList %}<tr class="default">{% for element in row %}<td>{{ element }}</td>{% endfor %}</tr>{% endfor %}</tbody></table></form></div>
{% endblock %}

此时访问:http://127.0.0.1:7777/stark/RBAC/rbacuserinfo/check/

ManyToManyField 处理

基本的信息已经可以浏览了,但是职位这一栏有点问题,显示的貌似是一个对象,后边还跟了个 None,看一下数据库表结构可以发现,只有 roles 是 ManyToManyField,估计是多对多把 Stark 给整懵了,我们来给它定义一个处理多对多关系的方法,因为这是一个通用的方法,可以直接写到 Stark/main.py 中。

我们先给 roles 字段套上这么一个函数:

# RBAC/views/rbacUserinfo.py
from Stark.main import StarkHandler, getM2MTextclass RbacUserHandler(StarkHandler):def __init__(self, site, modelClass, prefix):super().__init__(site, modelClass, prefix)self.displayList = ["username", "email", "score", "grade", getM2MText("职务", "roles"), "team", "department","dateJoined"]

显示表格的时候是要通过 checkView 统一处理的,因此要对 checkView 做一些更改:

# Stark/main.pydef checkView(self, request, *args, **kwargs):"""查看功能视图函数"""# --------------------- 1.显示表格 ---------------------displayList = self.getDisplayList(request, *args, **kwargs)# 1.1、处理表格表头headerList = []if displayList:for item in displayList:verboseName = item(self, obj=None, isHeader=True) if isinstance(item, FunctionType) \else self.model._meta.get_field(item).verbose_nameheaderList.append(verboseName)else:headerList.append(self.model._meta.model_name)# 1.2、处理表格内容bodyList = []dataList = self.model.objects.all()for row in dataList:rowList = []if displayList:for item in displayList:rowList.append(item(self, obj=row, isHeader=False, *args, **kwargs) if isinstance(item, FunctionType)else getattr(row, item))else:rowList.append(row)bodyList.append(rowList)

最后编写一个 getM2MText 函数:

# Stark/main.py
def getM2MText(header, field):"""定义显示 ManyToManyField 字段的函数"""def inner(self, obj=None, isHeader=None, *args, **kwargs):return header if isHeader else ",".join([str(item) for item in getattr(obj, field).all()])return inner

这样职务就能正常显示了:

DateTimeField 处理

但是我现在有觉得那个加入时间显示的比较别扭,想让它按照我指定的格式显示。

处理方法跟 ManyToManyField 类似,也是定义一个函数:

# Stark/main.py
def getDatetime(header, field, timeFormat="%Y-%m-%d"):"""显示 DateTimeField 字段的函数"""def inner(self, obj=None, isHeader=None, *args, **kwargs):return header if isHeader else getattr(obj, field).strftime(timeFormat)return inner

然后将 displayList 中的"dateJoined" 改为 getDatetime("加入时间", "dateJoined")


不仅如此,相应的 Team 表和 Department 表也可以显示了:

from Stark.main import StarkHandlerclass TeamHandler(StarkHandler):def __init__(self, site, modelClass, prefix):super().__init__(site, modelClass, prefix)self.displayList = ["name", "introduce"]

from Stark.main import StarkHandlerclass DepartmentHandler(StarkHandler):def __init__(self, site, modelClass, prefix):super().__init__(site, modelClass, prefix)self.displayList = ["name", "duty"]


通过定义不同数据库表的处理类的 displayList 属性,就可以快速的在页面上显示列表信息。

三、增

增加功能对于项目来说不可或缺,所以我们需要在基类中定制一个通用的添加按钮。

首先要在 checkView 中获取添加按钮,如果能获取到说明此页面允许添加按钮,如果不能获取到说明不需要添加功能,默认都是可以获取到。

# Stark/main.pydef checkView(self, request, *args, **kwargs):"""查看功能视图函数"""# --------------------- 1.显示表格 ---------------------displayList = self.getDisplayList(request, *args, **kwargs)# 1.1、处理表格表头headerList = []if displayList:for item in displayList:verboseName = item(self, obj=None, isHeader=True) if isinstance(item, FunctionType) \else self.model._meta.get_field(item).verbose_nameheaderList.append(verboseName)else:headerList.append(self.model._meta.model_name)# 1.2、处理表格内容bodyList = []dataList = self.model.objects.all()for row in dataList:rowList = []if displayList:for item in displayList:rowList.append(item(self, obj=row, isHeader=False, *args, **kwargs) if isinstance(item, FunctionType)else getattr(row, item))else:rowList.append(row)bodyList.append(rowList)# --------------------- 2.添加按钮 ---------------------addButton = self.getAddButton(request, *args, **kwargs)return render(request, "stark/checkView.html", {"headerList": headerList,"bodyList": bodyList,"dataList": dataList,"addButton": addButton,})

getAddButton 就是用于获取添加按钮的方法,如果 self.hasAddButton 为 True 则返回添加按钮的 HTML 代码,默认就是设置为 True:

# Stark/main.pydef getAddButton(self, request, *args, **kwargs):"""如果 self.hasAddButton 为 True,在页面显示一个添加按钮"""return "<a class='btn btn-success' target='_blank' href='%s'>添加</a>" % self.reverseAddUrl(*args, **kwargs) \if self.hasAddButton else None

self.reverseAddUrl 是生成添加页面的 URL,如果在 checkView 页面带有一些初始搜索条件应该被保留:

# Stark/main.pydef reverseUrl(self, urlName, *args, **kwargs):"""生成带有原搜索条件的 URL"""name = "%s:%s" % (self.site.namespace, urlName)baseUrl = reverse(name, args=args, kwargs=kwargs)if self.request.GET:newQueryDict = QueryDict(mutable=True)  # mutable 可变类型newQueryDict["_filter"] = self.request.GET.urlencode()url = "%s?%s" % (baseUrl, newQueryDict.urlencode())else:url = baseUrlreturn urldef reverseAddUrl(self, *args, **kwargs):"""生成带有原搜索条件的添加 URL"""return self.reverseUrl(self.addUrlName, *args, **kwargs)

现在我们已经在页面生成了一个添加按钮,并且给了它一个跳转链接,接下来就得编写相应的视图函数来处理它:

# Stark/main.pydef addView(self, request, *args, **kwargs):"""添加功能视图函数"""form = NoneaddModelForm = self.getModelForm(isAdd=True, request=request, pk=None, *args, **kwargs)if request.method == "GET":form = addModelForm()elif request.method == "POST":form = addModelForm(data=request.POST)if form.is_valid():return self.save(request=request, form=form, isUpdate=False, *args, **kwargs) \or redirect(self.reverseListUrl(*args, **kwargs))return render(request, self.addTemplate or "stark/addOrChange.html", {"form": form})

getModelForm 方法是为了获取数据库表相应页面的 model form,方便地将数据库表的字段显示在页面上:

 def getModelForm(self, isAdd, request, pk, *args, **kwargs):"""添加和修改页面的 model form 定制"""class DynamicModelForm(StarkModelForm):class Meta:model = self.modelfields = "__all__"# 如果有自定义的 self.modelForm 则用自定义的,否则返回通用的return self.modelForm if self.modelForm else DynamicModelForm

在这里我们也预留一个扩展功能,如果添加页面要显示的字段需要定制,可以通过自定义 self.modelForm 来实现。

而 StarkModelForm 是为了给所有的显示字典添加一个默认样式,看起来更加美观:

class StarkModelForm(forms.ModelForm):"""统一给 ModelForm 生成字段添加样式"""def __init__(self, *args, **kwargs):super(StarkModelForm, self).__init__(*args, **kwargs)for name, field in self.fields.items():field.widget.attrs['class'] = 'form-control'

self.save 是为了保存添加的数据到数据库中的方法,如果有的页面想在保存到数据库中做一些操作,我们可以吧 save 方法预留出来:

 def save(self, request, form, isUpdate, *args, **kwargs):"""在使用 ModelForm 保存数据之前预留扩展方法"""form.save()

现在对于添加功能老说是万事俱备只欠东风了,也就是还没有编写添加页面,在写添加页面的代码之前,考虑一个问题,添加页面和修改页面的显示效果其实差不多,只不过是添加页面没有默认显示字段而修改页面要显示默认信息,因此我们可以编写一套通用的模板:

{% extends 'formwork.html' %}{% block content %}<div class="container"><form class="form-horizontal" method="post" novalidate>{% csrf_token %}{% for field in form %}<div class="form-group"><label class="col-sm-2 control-label">{{ field.label }}</label><div class="col-sm-7">{{ field }}<span style="color: red;">{{ field.errors.0 }}</span></div></div>{% endfor %}<div class="form-group"><div class="col-sm-offset-2 col-sm-8"><input type="submit" value="保 存" class="btn btn-primary"></div></div></form></div>
{% endblock %}

这样,我们的添加功能就完成了:

IntegerField 处理

这一跳回来又发现了一个问题,等级那里显示的有问题,按道理来说应该显示的 M1,而不是 1,这个是 IntegerField 字段的选项问题,对于选项我们也得编写一个通用的方法处理。

def getChoice(header, field):"""显示选项字段的中文信息"""def inner(self, obj=None, isHeader=None, *args, **kwargs):return header if isHeader else getattr(obj, "get_%s_display" % field)()return inner

这样选项字段就能够正常显示了:

四、改

如果要修改的话,必须拿到相应的 ID,才能针对具体的某一条记录做修改,因此我们可以在表格的最后一列加上一个修改按钮,一行一个对应其 ID。

首先是仿照 getAddButton 编写一个 getChangeButton:

 def getChangeButton(self, obj=None, isHeader=None, *args, **kwargs):"""编辑按钮"""return "操作" if isHeader else mark_safe("<a class='btn btn-warning' target='_blank' href='%s'>编辑</a>" % self.reverseChangeUrl(pk=obj.pk))

然后我们把 getDisplayList 扩展一下:

 def getDisplayList(self, request, *args, **kwargs):"""获取页面显示的表格,预留自定义扩展定制显示内容"""value = []if self.displayList:value.extend(self.displayList)value.append(type(self).getChangeButton)return value

这样就能在页面上显示一个编辑按钮了:

接下来要编写 changeView 视图函数:

 def changeView(self, request, pk, *args, **kwargs):"""修改功能视图函数"""form = NonecurrentChangeObject = self.model.objects.filter(pk=pk).first()if not currentChangeObject:return render(request, "404.html")modelForm = self.getModelForm(isAdd=False, request=request, pk=pk, *args, **kwargs)if request.method == "GET":form = modelForm(instance=currentChangeObject)elif request.method == "POST":form = modelForm(data=request.POST, instance=currentChangeObject)if form.is_valid():return self.save(request=request, form=form, isUpdate=True, *args, **kwargs) \or redirect(self.reverseListUrl(*args, **kwargs))return render(request, self.changeTemplate or "stark/addOrChange.html", {"form": form})

如果当前请求修改的记录不存在,返回一个404页面,因此我们还得搞一个404:

{% load static %}
<!DOCTYPE html>
<html class="fixed" lang="en">
<head><meta charset="UTF-8"><meta name="keywords" content="HTML5 Admin Template"/><meta name="description" content="Porto Admin - Responsive HTML5 Template"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/><link rel="stylesheet" href="/static/vendor/bootstrap-3.3.7-dist/css/bootstrap.min.css"><link rel="stylesheet" href="/static/vendor/font-awesome-4.7.0/css/font-awesome.min.css"><link rel="stylesheet" href="{% static 'RBAC/css/theme.css' %}"><link rel="stylesheet" href="{% static 'RBAC/css/default.css' %}"><title>404 Not Found</title>
</head>
<body>
<section class="body-error error-outside"><div class="center-error"><div class="error-header"><div class="row"><div class="col-md-12"><div class="row"><div class="col-md-8"><a href="/" class="logo"><img src="/static/images/MatrixLogo.png" style="height: 54px;" alt="Porto Admin"/></a></div><div class="col-md-4"><form class="form"><div class="input-group input-search"><label for="q"></label><input type="text" class="form-control" name="q" id="q" placeholder="Search..."><span class="input-group-btn"><button class="btn btn-default" type="submit"><i class="fa fa-search"></i></button></span></div></form></div></div></div></div></div><div class="row"><div class="col-md-8"><div class="main-error mb-xlg"><h2 class="error-code text-dark text-center text-semibold m-none">404 <i class="fa fa-file"></i></h2><p class="error-explanation text-center">We're sorry, but the page you were looking for doesn't exist.</p></div></div><div class="col-md-4"><h4 class="text">Here are some useful links</h4><ul class="nav nav-list primary"><li><a href="{% url 'index' %}"><i class="fa fa-caret-right text-dark"></i> Dashboard </a></li><li><a href="#"><i class="fa fa-caret-right text-dark"></i> User Profile </a></li><li><a href="#"><i class="fa fa-caret-right text-dark"></i> FAQ's </a></li></ul></div></div></div>
</section>
</body>
</html>

效果如下:

当然这个页面仅供参考,鼓励自己实现。

如果存在的话,通过 getModelForm 获取一个 modelForm,instance放当前请求的对象,这样就能将其原始信息显示出来了。

如果是 POST 请求的话,则是前端发送过来的修改完的数据,验证合法后存储,然后返回列表页面。


这样,我们的修改功能就完成了:

五、删

删除流程就跟修改差不多了,但这里我们就要考虑一个问题了,有些页面可能只需要修改按钮,而有些页面又只需要删除按钮,还有的页面两个按钮都需要,我们除了编写一个单独的删除按钮之外,还要编写一个同时具有修改和删除按钮的方法:

 def getDeleteButton(self, obj=None, isHeader=None, *args, **kwargs):"""删除按钮"""return "操作" if isHeader else mark_safe("<a class='btn btn-daner' target='_blank' href='%s'>删除</a>" % self.reverseDeleteUrl(pk=obj.pk))def getChangeAndDeleteButton(self, obj=None, isHeader=None, *args, **kwargs):"""编辑和删除按钮"""return "操作" if isHeader else mark_safe("<a class='btn btn-warning' target='_blank' href='%s'>编辑</a> ""<a class='btn btn-danger' target='_blank' href='%s'>删除</a>" % (self.reverseChangeUrl(pk=obj.pk), self.reverseDeleteUrl(pk=obj.pk)))

然后对 getDisplayList 再做一些修改:

 def getDisplayList(self, request, *args, **kwargs):"""获取页面显示的表格,预留自定义扩展定制显示内容"""value = []if self.displayList:value.extend(self.displayList)value.append(type(self).getChangeAndDeleteButton)return value

这样就可以在页面上显示出编辑和删除两个按钮了:

最后编写删除功能的视图函数:

 def deleteView(self, request, pk, *args, **kwargs):"""删除功能视图函数"""baseUrl = self.reverseListUrl(*args, **kwargs)if request.method == "GET":return render(request, self.deleteTemplate or "stark/delete.html", {"baseUrl": baseUrl})response = self.model.objects.filter(pk=pk).delete()return redirect(baseUrl) or HttpResponse(response)

如此这般,删除功能就实现了:

好了,截止目前,Stark 组件的基本增删改查和页面显示也就做完了,后续我们再给它添加更多的功能。

Stark 组件:快速开发神器 —— 页面显示相关推荐

  1. Stark 组件:快速开发神器 —— 锦上添花

    Stark 组件:快速开发神器 -- 锦上添花 一.分页 二.排序 三.搜索 1.关键字搜索 2.组合搜索 四.批量操作 经过前面几个篇章,我们的 Stark 组件已经能够批量生成 URL,快速实现增 ...

  2. Stark 组件:快速开发神器 —— 模板设计

    说道 Stark 你是不是不会想到他--Tony Stark,超级英雄钢铁侠,这也是我的偶像. 不过我们今天要开发的 Stark 组件,倒是跟他的人工智能助手 JARVIS 有些类似,是帮助我们快速开 ...

  3. Stark 组件:快速开发神器 —— 自动生成 URL

    说道 Stark 你是不是不会想到他--Tony Stark,超级英雄钢铁侠,这也是我的偶像. 不过我们今天要开发的 Stark 组件,倒是跟他的人工智能助手 JARVIS 有些类似,是帮助我们快速开 ...

  4. 介绍一个软件开发工具,堪称快速开发神器

    软件快速开发平台是一种软件开发工具,以通用技术架构(如MVC)为基础,集成常用建模工具.二次开发包.基础解决方案等而成.可以大幅缩减编码率,使开发者有更多时间关注客户需求,在项目的需求.设计.开发.测 ...

  5. 如何搭积木式的快速开发H5页面?

    2个月前开源的H5编辑器 H5-Dooring 目前已经成功迭代到1.0版本, 从最开始的基本的页面生成框架到现在的支持更丰富的组件资源,交互能力和数据追踪能力, 期间做了很多的设计和迭代,也收获了很 ...

  6. html页面引入vue.js + elementUI 离线包快速开发前端页面

    背景 需要开发一个页面,页面复杂,原生H5不熟悉.项目要求开发一键打开的html页面,不需要任何运行环境,例如node等.最后决定采用html + vue.js + elementUI离线开发. 离线 ...

  7. XML与web开发-01- 在页面显示和 XML DOM 解析

    前言: 关于 xml 特点和基础知识,可以菜鸟教程进行学习:http://www.runoob.com/xml/xml-tutorial.html 本系列笔记,主要介绍 xml 在 web 开发时需要 ...

  8. CRM项目之stark组件之列表展示页面详细功能实现3

    分页 分页器组件代码 """ 分页组件 """class Pagination(object):def __init__(self, cur ...

  9. SpringBoot 接口快速开发神器(接口可视化界面实现)

    点击关注公众号,实用技术文章及时了解 简介 magic-api 是一个基于Java的接口快速开发框架,编写接口将通过magic-api提供的UI界面完成,自动映射为HTTP接口,无需定义Control ...

最新文章

  1. WinForm编程数据视图之DataGridView浅析(续)
  2. stdthread(6)并发lockGuard
  3. hdu 5071 Chat(模拟|Splay)
  4. 2016 China Joy抢先看,文末有彩蛋!
  5. java 线程 获取消息_获取java线程中信息
  6. ResultMap和ResultType在使用中的区别
  7. 基于java ssm springboot选课推荐交流平台系统设计和实现
  8. Kafka中@KafkaListener如何动态指定多个topic
  9. POST 一张 图像的调试来认识 http post
  10. 数据库的Timeout
  11. html是描述型语言,JavaScript_JavaScript基础教程——入门必看篇,JavaScript他是一种描述性语言, - phpStudy...
  12. 狗屁不通的“视频专辑:零基础学习C语言(小甲鱼版)”(1)
  13. python powerbi知乎_数据分析-PowerBI
  14. 计算机毕业设计Java-ssm办公自动化管理系统源码+系统+数据库+lw文档
  15. 我教宝宝学AI (五)挖坑中成长
  16. Android Kotlin okhttp Retrofit 线程协程那些事
  17. SpringBoot 文件管理微服务 支持FastDFS/FTP/阿里云存储、华为云存储/天翼云存储/联通云存储移动云存储
  18. 苏宁易购 App 客户端架构演进
  19. 利用Python为女神制作一个专属网站
  20. 线下餐饮受挫,狂奔的自热速食

热门文章

  1. Wi-Fi模块的设置方法汇总
  2. 【密码学】CSP的概念
  3. linux C++怎么转java?从云计算切入容易么?
  4. nginx 日志获取不到远程访问ip问题解决
  5. 【★★★★★模板专区★★★★★】
  6. linux怎么知道ping命令,教程方法;通过ping命令查看服务器类型(linux还是windows系列)电脑技巧-琪琪词资源网...
  7. Java黑皮书课后题第3章:**3.29(几何:两个圆)编写程序,提示用户输入两个圆的中心坐标和各自的半径值,然后判断圆是在第一个圆内,还是和第一个圆重叠
  8. 哥伦比亚大学浙江大学计算机,大神offer | 恭喜C同学录取哥伦比亚大学-数据科学硕士!...
  9. OpenGL 期末考试作业
  10. Android Intent基本使用