开发环境:
Microsoft Windows 10 Enterprise LTSC [Version 10.0.19044.1586], locale zh-CN
Python 3.8.10
PyCharm 2021.2 (Professional Edition)
Visual Studio Code, 64-bit edition (version 1.67.2)
自己实现表单添加组织构架 在项目 templates/department/
文件夹添加用于以下几个新的HTML文件待用: edit.html
, detail.html
, delete.html
,
打开 department/views.py
文件, 在文件中添加用于 新增
、修改
、删除
、查看详细内容
的函数, 代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from django.shortcuts import render, redirect, get_object_or_404from django.core.paginator import Paginator, EmptyPage, PageNotAnIntegerfrom .models import Department def index (request ): departments = Department.objects.all () return render(request, 'department/index.html' , {'departments' : departments}) def create (request ): return render(request, 'department/edit.html' ) def update (request, dep_id ): return render(request, 'department/edit.html' ) def delete (request, dep_id ): return render(request, 'department/delete.html' ) def detail (request, dep_id ): return render(request, 'department/detail.html' )
添加路由时, 找不到目标函数会报错, 所以这里先添加几个没有具体功能的函数
添加新功能的路由 打开 department/urls.py
文件, 编辑内容如下:
1 2 3 4 5 6 7 8 9 10 11 from django.urls import pathfrom . import viewsurlpatterns = [ path('' , views.index, name='index' ), path('create/' , views.create, name='create' ), path('<int:dep_id>/' , views.detail, name='detail' ), path('update/<int:dep_id>/' , views.update, name='update' ), path('delete/<int:dep_id>/' , views.delete, name='delete' ) ]
<int:dep_id>
: 表示该路由接收一个名为 dep_id
的 int
类型的参数
跳转到新页面 打开 templates/department/index.html
文件, 编辑部分代码实现到 新增
、修改
等功能的跳转
, 代码如以下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 {% extends "layout.html" %} {% block title %}部门管理{% endblock %} {% block content %} {% include "nav.html" %} <div class ="container flex-grow-1" > <div class ="my-2 d-flex" > <h3 class ="flex-grow-1" > 组织构架</h3 > <a class ="btn btn-outline-success" href ="{% url 'create' %}" > 新增</a > </div > <table class ="table table-striped table-hover" > <thead > <tr > <th > ID</th > <th > 名称</th > <th > 类别</th > <th > 所属</th > <th > 操作</th > </tr > </thead > <tbody > {% for item in departments %} <tr > <td > {{ item.id }}</td > <td > <a class ="text-decoration-none" href ="{% url 'detail' item.id %}" > {{ item.title }}</a > </td > <td > {% if item.type == 'department' %} 部门 {% else %} 公司 {% endif %} </td > <td > {{ item.parent.title }}</td > <td width ="120px" > <a class ="btn btn-sm btn-outline-primary" href ="{% url 'update' item.id %}" > 编辑</a > <a class ="btn btn-sm btn-outline-danger" href ="{% url 'delete' item.id %}" > 删除</a > </td > </tr > {% endfor %} </tbody > </table > </div > {% include "footer.html" %} {% endblock %}
修改说明: 在表格右上解添加了一个按钮外观的 新增
超链接; 表格行右边的两个按钮也修改为 超链接
{% url 'create' %}
: 此处的 create
表示在 路由表中 urlpatterns
的 name
的值
{% url 'update' item.id %}
: item.id
为传递到路由的参数
实现新增函数 打开 department/views.py
文件, 编辑 create
函数的代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 def create (request ): if request.method == 'POST' : _title = request.POST['title' ] _type = request.POST['type' ] _description = request.POST['description' ] _parentId = request.POST['parent' ] dep = Department.objects.filter (title=_title) if dep: pass else : if _parentId == '------' : _parent = None else : _parent = Department.objects.filter (pk=_parentId).first() Department.objects.create( title=_title, type =_type , description=_description, parent=_parent) return redirect(index) else : departments = Department.objects.all () return render(request, 'department/edit.html' , {'departments' : departments})
实现新增界面 打开 templates/department/edit.html
文件, 编辑代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 {% extends "layout.html" %} {% block title %}组织构架{% endblock %} {% block content %} {% include "nav.html" %} <div class ="container flex-grow-1" > <h3 > 新增组织构架</h3 > <form class ="row justify-content-md-center" method ="POST" > {% csrf_token %} <div class ="mb-3 col-md-8" > <label for ="parent" class ="form-label" > 上级部门</label > <select class ="form-select" name ="parent" > <option > ------</option > {% for item in departments %} <option value ="{{ item.id }}" {% if item.title == department.parent.title %} selected {% endif %} > {{ item.title }}</option > {% endfor %} </select > </div > <div class ="mb-3 col-md-8" > <label for ="title" class ="form-label" > 名称</label > <input type ="text" class ="form-control" name ="title" placeholder ="输入名称" value ="{{ department.title }}" > </div > <div class ="mb-3 col-md-8" > <label for ="type" class ="form-label" > 类型</label > <select class ="form-select" name ="type" > <option value ="department" {% if department.type == 'deprtment' %}selected {% endif %} > 部门 </option > <option value ="firm" {% if department.type == 'firm' %}selected {% endif %} > 公司 </option > </select > </div > <div class ="mb-3 col-md-8" > <label for ="description" class ="form-label" > 描述</label > <textarea rows ="3" class ="form-control" name ="description" placeholder ="说明" > {{ department.description }}</textarea > </div > <div class ="mb-3 col-md-8 d-flex flex-row-reverse" > <button type ="submit" class ="btn btn-sm btn-outline-success" > 保存</button > </div > </form > </div > {% include "footer.html" %} {% endblock %}
表单中 name
属性的值要与接收数据的函数 request.POST['title']
中的名字(例如这里的 title
)相同
运行、预览 启动项目, 点击跳转到 新增
页面, 输入新部门数据
点 保存
按钮后, 添加数据成功, 重定向到部门列表
使用表单类 自定义的表单没有数据验证功能, 在实现开发时可以使用 Django Forms API
来增强表单的功能
Django
使用两种类型的 form
:forms.Form
和 forms.ModelForm
。Form
类是通用的表单实现。我们可以使用它来处理与应用程序 model
没有直接关联的数据。ModelForm
是 Form
的子类,它与 model
类相关联。
添加表单类 在 users
文件夹新建名为 forms.py
的文件, 编辑其内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 from django import formsfrom .models import Departmentclass DepartmentUpdateForm (forms.ModelForm): parent = forms.ModelChoiceField( label='上级部门' , required=False , queryset=Department.objects.all (), widget=forms.Select(attrs={'class' : 'form-select' }) ) title = forms.CharField( label='名称' , required=True , max_length=60 , widget=forms.TextInput(attrs={'class' : 'form-control' }) ) type = forms.ChoiceField( label='类型' , choices=(('department' , '部门' ), ('firm' , '公司' )), widget=forms.Select(attrs={'class' : 'form-select' }), ) description = forms.CharField( label='描述' , required=False , max_length=200 , widget=forms.Textarea(attrs={'class' : 'form-control' , 'rows' : 3 }), ) class Meta : model = Department fields = ['parent' , 'title' , 'type' , 'description' ]
forms.ModelChoiceField
: 用于对 models
里 Forekey
(外键字段) 进行渲染的下拉框
forms.CharField
: 渲染为输入框, 属性 widget=forms.TextInput
表示单行文本框; widget=forms.Textarea()
表示多行文本框
forms.ChoiceField
: 可以渲染为 单选框
(widget=forms.RadioSelect()), 或 下拉框
(widget=forms.Select())
required=False
: 是否为必填项
attrs={'class': 'form-control', 'rows': 3}
: 用于给渲染出来的表单控件添加属性, 'class': 'form-control'
为添加CSS, 'rows': 3
为指定文本框的行数
修改新增部门的视图函数 打开 department/views.py
文件, 修改 create
函数如以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 def create (request ): if request.method == 'POST' : form = DepartmentUpdateForm(request.POST) if form.is_valid(): cd = form.cleaned_data Department.objects.create( title=cd['title' ], type =cd['type' ], description=cd['description' ], parent=cd['parent' ]) return redirect(index) else : error_msg = '新增部门出现异常' return render(request, 'department/edit.html' , locals ()) else : form = DepartmentUpdateForm() return render(request, 'department/edit.html' , locals ())
修改新增界面 打开 templates/department/edit.html
文件, 修改内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 {% extends "layout.html" %} {% block title %}组织构架{% endblock %} {% block content %} {% include "nav.html" %} <div class ="container flex-grow-1" > <h3 > {% if form.title.value %} 修改组织构架 {% else %} 新增组织构架 {% endif %} </h3 > <form class ="row justify-content-md-center" method ="POST" > {% csrf_token %} <div class ="text-danger text-center" > {{ error_msg }}</div > <div class ="mb-3 col-md-8" > <label for ="parent" class ="form-label" > 上级部门</label > {{ form.parent }} </div > <div class ="mb-3 col-md-8" > <label for ="title" class ="form-label" > 名称</label > {{ form.title }} </div > <div class ="mb-3 col-md-8" > <label for ="type" class ="form-label" > 类型</label > {{ form.type }} </div > <div class ="mb-3 col-md-8" > <label for ="description" class ="form-label" > 描述</label > {{ form.description }} </div > <div class ="mb-3 col-md-8 d-flex flex-row-reverse" > <button type ="submit" class ="btn btn-sm btn-outline-success" > 保存</button > </div > </form > </div > {% include "footer.html" %} {% endblock %}
运行、预览 再次点击跳转到 新增
页面, 输入新部门数据
点 保存
按钮后, 添加数据成功, 重定向到部门列表
实现修改功能 打开 department/views.py
文件, 修改 update
函数如以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 def update (request, dep_id ): if request.method == 'POST' : department = get_object_or_404(Department, pk=dep_id) form = DepartmentUpdateForm(request.POST) if form.is_valid(): cd = form.cleaned_data department.title = cd['title' ] department.type = cd['type' ] department.description = cd['description' ] department.parent = cd['parent' ] department.save() return redirect(index) else : data = get_object_or_404(Department, pk=dep_id) form = DepartmentUpdateForm(initial={ 'title' : data.title, 'type' : data.type , 'description' : data.description, 'parent' : data.parent }) return render(request, 'department/edit.html' , locals ())
新增
和 修改
使用同一个模板文件, 模板中的 <form></form>
标签不设置 action
属性, 当点击提交按钮时, 数据会回传到 启动该模板
的函数.
删除组织构架 打开 department/views.py
文件, 修改 delete
函数如以下代码:
1 2 3 4 5 6 7 8 def delete (request, dep_id ): if request.method == 'GET' : data = get_object_or_404(Department, pk=dep_id) return render(request, 'department/delete.html' , {'department' : data}) else : data = get_object_or_404(Department, pk=dep_id) data.delete() return redirect(index)
打开 templates/department/delete.html
文件, 编辑内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 {% extends "layout.html" %} {% block title %}组织构架{% endblock %} {% block content %} {% include "nav.html" %} <div class ="container flex-grow-1" > <h3 > 删除记录</h3 > <form class ="row justify-content-md-center" method ="POST" > {% csrf_token %} <input type ="hidden" name ="id" value ="{{ department.id }}" > <div class ="mb-3 col-md-8" > <label for ="parent" class ="form-label" > 上级部门:</label > {{ department.parent }} </div > <div class ="mb-3 col-md-8" > <label for ="title" class ="form-label" > 名称:</label > {{ department.title }} </div > <div class ="mb-3 col-md-8" > <label for ="type" class ="form-label" > 类型:</label > {% if item.type == 'department' %} 部门 {% else %} 公司 {% endif %} </div > <div class ="mb-3 col-md-8" > <label for ="description" class ="form-label" > 描述:</label > {{ department.description }} </div > <div class ="mb-3 col-md-8 d-flex flex-row-reverse" > <button type ="submit" class ="btn btn-sm btn-outline-danger" > 删除</button > <a class ="btn btn-sm btn-outline-success mx-2" href ="{% url 'index' %}" > 放弃</a > </div > </form > </div > {% include "footer.html" %} {% endblock %}
进入删除页面
查看详细内容 打开 department/views.py
文件, 修改 detail
函数如以下代码:
1 2 3 def detail (request, dep_id ): data = get_object_or_404(Department, pk=dep_id) return render(request, 'department/detail.html' , {'department' : data})
打开 templates/department/detail.html
文件, 编辑内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 {% extends "layout.html" %} {% block title %}组织构架{% endblock %} {% block content %} {% include "nav.html" %} <div class ="container flex-grow-1" > <h3 > 详细信息</h3 > <div class ="row justify-content-md-center" > <div class ="mb-3 col-md-8" > <label for ="parent" class ="form-label" > 上级部门:</label > {{ department.parent }} </div > <div class ="mb-3 col-md-8" > <label for ="title" class ="form-label" > 名称:</label > {{ department.title }} </div > <div class ="mb-3 col-md-8" > <label for ="type" class ="form-label" > 类型:</label > {% if item.type == 'department' %} 部门 {% else %} 公司 {% endif %} </div > <div class ="mb-3 col-md-8" > <label for ="description" class ="form-label" > 描述:</label > {{ department.description }} </div > <div class ="mb-3 col-md-8 d-flex flex-row-reverse" > <a class ="btn btn-sm btn-outline-primary mx-2" href ="{% url 'update' department.id %}" > 修改</a > <a class ="btn btn-sm btn-outline-success mx-2" href ="{% url 'index' %}" > 返回</a > </div > </div > </div > {% include "footer.html" %} {% endblock %}
在组织架构列表点击 名称
进入详细内容页
分页功能 打开 department/views.py
文件, 编辑 index
函数的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def index (request ): """ 分页查询时URL格式:/department/?page=1&limit=10 """ page = request.GET.get('page' ) limit = request.GET.get('limit' ) if not limit: limit = 2 department_list = Department.objects.all () paginator = Paginator(department_list, limit) try : current_page = paginator.page(page) departments = current_page.object_list except PageNotAnInteger: current_page = paginator.page(1 ) departments = current_page.object_list except EmptyPage: current_page = paginator.page(paginator.num_pages) departments = current_page.object_list return render(request, 'department/index.html' , locals ())
locals()
: 返回一个包含 当前作用域
里面的所有变量和它们的值的字典
打开 templates/department/index.html
文件, 在 </table>
标签后加入 Bootstrap5 分页组件
, 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <ul class ="pagination" > {# current_page 为视图中使用的变量名 #} {% if current_page.has_previous %} <li class ="page-item" > <a class ="page-link" href ="/department/?page={{ current_page.previous_page_number }}" > 前一页</a > </li > {% else %} <li class ="page-item disabled" > <a class ="page-link" href ="#" > 前一页</a > </li > {% endif %} {% for item in paginator.page_range %} <li class ="page-item" > <a class ="page-link" href ="/department/?page={{ item }}" > {{ item }}</a > </li > {% endfor %} {% if current_page.has_next %} <li class ="page-item" > <a class ="page-link" href ="/department/?page={{ current_page.next_page_number }}" > 下一页</a > </li > {% else %} <li class ="page-item disabled" > <a class ="page-link" href ="#" > 下一页</a > </li > {% endif %} </ul >
分页效果预览
完整代码参考 视图文件代码 department/views.py
文件的完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 from django.shortcuts import render, redirect, get_object_or_404from django.core.paginator import Paginator, EmptyPage, PageNotAnIntegerfrom django.http import HttpResponse, HttpResponseRedirectfrom .models import Department from .forms import DepartmentUpdateFormfrom django.contrib.auth.decorators import login_requireddef home (request ): return HttpResponseRedirect('/department/' ) def index (request ): """ 分页查询时URL格式:/department/?page=1&limit=10 """ page = request.GET.get('page' ) limit = request.GET.get('limit' ) if not limit: limit = 10 department_list = Department.objects.all () paginator = Paginator(department_list, limit) try : current_page = paginator.page(page) departments = current_page.object_list except PageNotAnInteger: current_page = paginator.page(1 ) departments = current_page.object_list except EmptyPage: current_page = paginator.page(paginator.num_pages) departments = current_page.object_list return render(request, 'department/index.html' , locals ()) @login_required(login_url='/login/' ) def create (request ): if request.method == 'POST' : form = DepartmentUpdateForm(request.POST) if form.is_valid(): cd = form.cleaned_data Department.objects.create( title=cd['title' ], type =cd['type' ], description=cd['description' ], parent=cd['parent' ]) return redirect(index) else : error_msg = '新增部门出现异常' return render(request, 'department/edit.html' , locals ()) else : form = DepartmentUpdateForm() return render(request, 'department/edit.html' , locals ()) @login_required(login_url='/login/' ) def update (request, dep_id ): if request.method == 'POST' : department = get_object_or_404(Department, pk=dep_id) form = DepartmentUpdateForm(request.POST) if form.is_valid(): cd = form.cleaned_data department.title = cd['title' ] department.type = cd['type' ] department.description = cd['description' ] department.parent = cd['parent' ] department.save() return redirect(index) else : data = get_object_or_404(Department, pk=dep_id) form = DepartmentUpdateForm(initial={ 'title' : data.title, 'type' : data.type , 'description' : data.description, 'parent' : data.parent }) return render(request, 'department/edit.html' , locals ()) @login_required(login_url='/login/' ) def delete (request, dep_id ): if request.method == 'GET' : data = get_object_or_404(Department, pk=dep_id) return render(request, 'department/delete.html' , {'department' : data}) else : data = get_object_or_404(Department, pk=dep_id) data.delete() return redirect(index) def detail (request, dep_id ): data = get_object_or_404(Department, pk=dep_id) return render(request, 'department/detail.html' , {'department' : data})
模板文件代码 templates/department/index.html
文件的完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 {% extends "layout.html" %} {% block title %}组织构架{% endblock %} {% block content %} {% include "nav.html" %} <div class ="container flex-grow-1" > <div class ="my-2 d-flex" > <h3 class ="flex-grow-1" > 组织构架</h3 > <a class ="btn btn-outline-success" href ="{% url 'create' %}" > 新增</a > </div > <table class ="table table-striped table-hover" > <thead > <tr > <th > ID</th > <th > 名称</th > <th > 类别</th > <th > 所属</th > <th > 操作</th > </tr > </thead > <tbody > {% for item in departments %} <tr > <td class ="align-middle" > {{ item.id }}</td > <td class ="align-middle" > <a class ="text-decoration-none" href ="{% url 'detail' item.id %}" > {{ item.title }}</a > </td > <td class ="align-middle" > {# get_type_display: 显示 type 字段的 choices 的名字 #} {{ item.get_type_display }} {# {% if item.type == 'department' %}#} {# 部门#} {# {% else %}#} {# 公司#} {# {% endif %}#} </td > <td class ="align-middle" > {{ item.parent.title }}</td > <td class ="align-middle" width ="120px" > <a class ="btn btn-sm btn-outline-primary" href ="{% url 'update' item.id %}" > 编辑</a > <a class ="btn btn-sm btn-outline-danger" href ="{% url 'delete' item.id %}" > 删除</a > </td > </tr > {% endfor %} </tbody > </table > <ul class ="pagination" > {# current_page 为视图中使用的变量名 #} {% if current_page.has_previous %} <li class ="page-item" > <a class ="page-link" href ="/department/?page={{ current_page.previous_page_number }}" > 前一页</a > </li > {% else %} <li class ="page-item disabled" > <a class ="page-link" href ="#" > 前一页</a > </li > {% endif %} {% for item in paginator.page_range %} <li class ="page-item" > <a class ="page-link" href ="/department/?page={{ item }}" > {{ item }}</a > </li > {% endfor %} {% if current_page.has_next %} <li class ="page-item" > <a class ="page-link" href ="/department/?page={{ current_page.next_page_number }}" > 下一页</a > </li > {% else %} <li class ="page-item disabled" > <a class ="page-link" href ="#" > 下一页</a > </li > {% endif %} </ul > </div > {% include "footer.html" %} {% endblock %}
参考资料:
Django v4.0 中文文档
Django入门与实践教程
Bootstrap5 中文手册
===END===