目录
Django是目前比较火爆的框架,之前有在知乎刷到,很多毕业生进入大厂实习后因为不会git和Django框架3天就被踢掉了,因为他们很难把自己的工作融入到整个组的工作中。因此,我尝试自学Django并整理出如下笔记。
不同于之前的两篇,这篇主要的知识点是顺着项目实现串联的,主要是基于一个管理系统的小任务,包括部门管理和用户管理。再步骤上主要包括项目的准备工作、前端界面制作、Django模板嵌套、Django组件学习几部分。
我们先提前看看这次项目做出来的结果:
1.准备工作
1.1. 创建项目和app
我们在此创建一个Employee_Management(员工管理系统)项目,并为其创建app。相关操作在此不赘述,还不清楚的可以去逛从零开始的Django框架入门到实战教程(内含实战实例) - 01 创建项目与app、加入静态文件、模板语法介绍。
1.2. 项目设计
我们打算完成的员工管理项目包括这几个表格和变量。这中间涵盖了不少Django数据库定义的细节,主要是在models.py中的操作。
表名 | 列名 | 元素类型 |
---|---|---|
部门 | 部门ID(主键) | 自动增加大整形 |
**** | 部门名 | 32位字符 |
员工 | 员工ID(主键) | 自动增加大整形 |
**** | 姓名 | 16位字符 |
**** | 密码 | 64位字符 |
**** | 年龄 | 整型 |
**** | 账户余额 | 小数 |
**** | 入职时间 | 日期 |
**** | 性别 | 小整型 |
**** | 部门ID(外键) | 自动增加大整形 |
首先定义部门表,这中间用到了主键、自增型整型方面的知识。
# 部门
class Department(models.Model):
# ID实际上不需要我们自己创建,Django自动会帮我们生成一个ID,而且是主键。我们设置为自增型
# id = models.AutoField(verbose_name="ID", primary_key=True) # 小自增
id = models.BigAutoField(verbose_name="ID", primary_key=True) # 大自增
title = models.CharField(verbose_name="部门名称", max_length=32)
其次定义员工表,这中间用到了小数、日期、自定义约束条件、外键方面的知识。
# 员工
class User(models.Model):
# char类型之后的max_length是必须写的
name = models.CharField(verbose_name="姓名", max_length=16)
password = models.CharField(verbose_name="密码", max_length=64)
age = models.IntegerField(verbose_name="年龄")
# 设置数位是10,小数点后2位;如果新来一个员工,默认账户余额是0
account = models.DecimalField(verbose_name="账户余额", max_digits=10, decimal_places=2, default=0)
Onboarding_time = models.DateTimeField(verbose_name="入职时间")
# 我们约束性别只能写1, 2且设置好性别的对应关系
gender_choices = (
(1, "男"),
(2, "女"),
)
gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choices)
# 外键
# Department_ID = models.BigIntegerField(verbose_name="部门ID") # 无约束条件
# 有约束条件,外键连Department表的id列;
# ForeignKey在底层生成列名的时候会自动为Department加上_id,所以我们这里就不写_id了;
# 如果部门被删除了员工一起被删除,则用on_delete=models.CASCADE()实现级联删除;如果部门被删除了,员工的部门置空,则用blank=True, on_delete=models.SET_NULL
Department = models.ForeignKey(to="Department", to_field="id", on_delete=models.CASCADE
# Department = models.ForeignKey(to="Department", to_field="id", on_delete=models.SET_NULL, blank=True)
1.3. 数据库生成
先在MySQL中手动创建数据库,然后连接数据库,生成数据表具体操作在这不赘述,还不清楚的可以看从零开始的Django框架入门到实战教程(内含实战实例) - 02 请求与相应、数据库操作(ORM、MySQL),讲的很详细了。
1.4. 创建静态文件
在application01中创建静态文件和模板文件,创建后目录如下:
2. 部门基本功能实现
2.1. 部门管理(无Form和ModelForm组件)
2.1.1. 前端壳子
样式部分我们直接白嫖bootstrap官网(组件 · Bootstrap v3 中文文档 | Bootstrap 中文网 (bootcss.com))的,因为我们这个管理系统还是比较简单的,先把下拉列表和输入框去掉。先做这么个导航栏的壳子:
html代码如下:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>部门列表</title>
<link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<style>
.navbar{
border-radius: 0;
}
</style>
</head>
<body>
<nav class="navbar navbar-default">
<!-- <div class="container-fluid">-->
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Olsen用户管理系统</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="#">部门管理</a></li>
<li><a href="#">用户管理</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">登录</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">ClarkHu <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">个人资料</a></li>
<li><a href="#">我的信息</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">注销</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<script src = "{% static 'js/jquery.js' %}"></script>
<script src = "{% static 'plugins/bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</body>
</html>
有了导航栏显然还不够,我们还需要下面的列表、按钮等元素。按钮很简单,我们自己写就可以了;列表元素我们直接白嫖bootstrap官网上(组件 — 面板)的样式:
白嫖完之后,我们根据自己的需求加入图标、加入按钮、修改表格:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>部门列表</title>
<link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<style>
.navbar{
border-radius: 0;
}
</style>
</head>
<body>
<nav class="navbar navbar-default">
<!-- <div class="container-fluid">-->
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Olsen用户管理系统</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="#">部门管理</a></li>
<li><a href="#">用户管理</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">登录</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">ClarkHu <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">个人资料</a></li>
<li><a href="#">我的信息</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">注销</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<div>
<div class="container">
<div style="margin-bottom: 10px">
<a class="btn btn-success" href="#">
<span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>
新建部门
</a>
</div>
<div class="panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading">
<span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>
部门列表
</div>
<!-- Table -->
<table class="table table-bordered">
<thead>
<tr>
<th>部门ID</th>
<th>部门名称</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<th>1</th>
<td>销售部</td>
<td>
<a class="btn btn-primary btn-xs">编辑</a>
<a class="btn btn-danger btn-xs">删除</a>
</td>
</tr>
<tr>
<th>2</th>
<td>人事部</td>
<td>
<a class="btn btn-primary btn-xs">编辑</a>
<a class="btn btn-danger btn-xs">删除</a>
</td>
</tr>
<tr>
<th>3</th>
<td>开发部</td>
<td>
<a class="btn btn-primary btn-xs">编辑</a>
<a class="btn btn-danger btn-xs">删除</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<script src = "{% static 'js/jquery.js' %}"></script>
<script src = "{% static 'plugins/bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</body>
</html>
至此,我们网页的壳子就做好了。
2.1.2. 连接数据库
因为现在的前端就是个壳子,我们先在数据库中输入几个数据,然后再把数据展示在前端,完成数据库前后端的连接。所以先在数据库中手动添加几个部门信息:
insert into application01_department(title) values("IT部门"),("销售部"),("人事部");
连接数据库,将读入的值传入html:
# 部门列表
def depart_list(request):
depart_list = models.Department.objects.all()
return render(request, "depart_list.html", {"depart_list": depart_list})
修改表格的tbody,把这个表格变成动态显示的:
<tbody>
{% for obj in depart_list %}
<tr>
<th>{{ obj.id }}</th>
<td>{{ obj.title }}</td>
<td>
<a class="btn btn-primary btn-xs">编辑</a>
<a class="btn btn-danger btn-xs">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
结果展示:
2.2. 新建部门
按照我们的设计,我们需要在点击新建部门时跳转到新建部门的页面,在新的页面中同样应该具有导航栏,也应该有添加用户的面板。
2.2.1. 前端壳子
老样子我们先在这里做个壳子。这里的前端代码因为不是重点,我们继续白嫖全局 CSS 样式 · Bootstrap v3 中文文档 | Bootstrap 中文网 (bootcss.com)中的表单。效果如下:
depart_add.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>添加用户</title>
<link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<style>
.navbar{
border-radius: 0;
}
</style>
</head>
<body>
<nav class="navbar navbar-default">
<!-- <div class="container-fluid">-->
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Olsen用户管理系统</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="#">部门管理</a></li>
<li><a href="#">用户管理</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">登录</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">ClarkHu <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">个人资料</a></li>
<li><a href="#">我的信息</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">注销</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<div>
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">新建部门</h3>
</div>
<div class="panel-body">
<!-- 这个表单样式感觉不怎么合适,就先放着,我们换一个-->
<!-- <form class="form-horizontal">-->
<!-- <div class="form-group">-->
<!-- <label class="col-sm-2 control-label">标题</label>-->
<!-- <div class="col-sm-10">-->
<!-- <input type="email" class="form-control" placeholder="标题" name="title">-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="form-group">-->
<!-- <div class="col-sm-offset-2 col-sm-10">-->
<!-- <button type="submit" class="btn btn-primary">保存</button>-->
<!-- </div>-->
<!-- </div>-->
<!-- </form>-->
<form>
<div class="form-group">
<label>部门名称</label>
<input type="email" class="form-control" placeholder="部门名称" name="title">
</div>
<button type="submit" class="btn btn-primary">提 交</button>
</form>
</div>
</div>
</div>
</div>
<script src="{% static 'js/jquery.js' %}"></script>
<script src="{% static 'plugins/bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</body>
</html>
2.2.2. 添加部门
同样的,做完壳子,我们还是需要与后端数据库连接,将添加进来的数据传输到后端数据库中去。效果如下:
views.py
# 添加部门
def depart_add(request):
if request.method == "GET":
return render(request, "depart_add.html")
# 如果有信息被post过来,我们获取信息并添加记录
title = request.POST.get("title")
models.Department.objects.create(title=title)
# 回到list页面
return redirect("/depart/list")
depart_add.html
的表单部分也需要修改一下。
<form method="post">
{% csrf_token %}
<div class="form-group">
<label>部门名称</label>
<input type="email" class="form-control" placeholder="部门名称" name="title">
</div>
<button type="submit" class="btn btn-primary">提 交</button>
</form>
2.3. 删除部门
部门删除这部分我们继续模仿以前的做法,删除时跳转到一个新页面,完成数据库操作之后跳转回来,这个过程因为时间极短,所以用户是看不到的,呈现在用户面前的效果就是某条数据在点击删除之后就没了。效果如下:
首先我们修改depart_list.html
中的删除按钮,我们需要在删除按钮被点击时传入对应的nid:
<td>
<a class="btn btn-primary btn-xs">编辑</a>
<a class="btn btn-danger btn-xs" href="/depart/del?nid={{ obj.id }}">删除</a>
</td>
然后我们建立一个depart_del.html
,实际上没什么用,只是作为一个中转站传参:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>
最后我们在views.py
函数中写删除的操作:
# 删除部门
def depart_del(request):
# 获取被删除的部门的id
nid = request.GET.get("nid")
# 在数据库中根据id删除对应部门
models.Department.objects.filter(id=nid).delete()
# 跳转回用户列表界面
return redirect("/depart/list")
2.4. 编辑部门
这部分的前端是参考新建部门和删除部门两部分的,效果是在点击编辑部门之后跳转到部门编辑的界面,并且在这个界面中的对话框内默认显示当前部门的部门名称。这部分我们暂时不使用?nid=
的方法传参,采取<int: nid>
的写法传参。其他部分在没有用组件的适合一切正常,组件会在后面的部分用到。效果如下:
因为要用到<int: nid>
,我们的urls.py
要和之前的urls有所区别:
urlpatterns = [
# path('admin/', admin.site.urls),
path('depart/list', views.depart_list),
path('depart/add', views.depart_add),
path('depart/del', views.depart_del),
path('depart/<int:nid>/edit', views.depart_edit),
]
depart_edit.html
模仿之前的depart_add.html
就好了:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>添加用户</title>
<link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<style>
.navbar{
border-radius: 0;
}
</style>
</head>
<body>
<nav class="navbar navbar-default">
<!-- <div class="container-fluid">-->
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Olsen用户管理系统</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="#">部门管理</a></li>
<li><a href="#">用户管理</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">登录</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">ClarkHu <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">个人资料</a></li>
<li><a href="#">我的信息</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">注销</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<div>
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">编辑部门</h3>
</div>
<div class="panel-body">
<form method="post">
{% csrf_token %}
<div class="form-group">
<label>部门名称</label>
<input type="text" class="form-control" placeholder="部门名称" name="title" value="{{ obj.title }}">
</div>
<button type="submit" class="btn btn-primary">提 交</button>
</form>
</div>
</div>
</div>
</div>
<script src="{% static 'js/jquery.js' %}"></script>
<script src="{% static 'plugins/bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</body>
</html>
因为要换种传参的方式,所以depart_list.html
中的编辑按钮也应该修改一下:
<td>
<a class="btn btn-primary btn-xs" href="/depart/{{ obj.id }}/edit">编辑</a>
<a class="btn btn-danger btn-xs" href="/depart/del?nid={{ obj.id }}">删除</a>
</td>
views.py:
# 修改部门
def depart_edit(request, nid):
# 根据nid获取当前需要编辑的记录的数据
obj = models.Department.objects.filter(id=nid).first()
# 没有修改:显示默认值(当前部门名称)
if request.method == "GET":
return render(request, "depart_edit.html", {"obj": obj})
# 修改
title = request.POST.get("title")
models.Department.objects.filter(id=nid).update(title=title)
return redirect("/depart/list")
3. 模板的继承
Django提供了模板的操作,这个东西还是比较使用的。我们刚刚写的4个页面基本上都用用到导航栏,写的时候没啥感觉,一个复制粘贴就好了,但是如果要修改,这部分就会很麻烦,要一个个改过去。这几个页面倒还好说,但是项目一旦做大,那每次修改涉及到的页面就不是那么几个了,这个时候模板的好处就显现出来了。所以,我们在这里尝试制作网页的模板。个人感觉模板跟vue的插槽还是蛮像的。
首先我们得先写个模板layout.html
:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>部门列表</title>
<link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<style>
.navbar{
border-radius: 0;
}
</style>
</head>
<body>
<nav class="navbar navbar-default">
<!-- <div class="container-fluid">-->
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Olsen用户管理系统</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="#">部门管理</a></li>
<li><a href="#">用户管理</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">登录</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">ClarkHu <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">个人资料</a></li>
<li><a href="#">我的信息</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">注销</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<div>
{% block content %}
{% endblock %}
</div>
<script src="{% static 'js/jquery.js' %}"></script>
<script src="{% static 'plugins/bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</body>
</html>
其中,{% block content %}{% endblock %}
就是每个网页个性化的部分,其他部分就是模板。每个网页引用模板时只需在顶部写继承模板并加入{% block content %}{% endblock %}`即可:
{% extends "layout.html" %}
{% block content %}
网页个性化的内容
{% endblock %}
按照这个写法,我们修改之前写的3个html文件。
depart_list.html
{% extends "layout.html" %}
{% block content %}
<div class="container">
<div style="margin-bottom: 10px">
<a class="btn btn-success" href="/depart/add">
<span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>
新建部门
</a>
</div>
<div class="panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading">
<span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>
部门列表
</div>
<!-- Table -->
<table class="table table-bordered">
<thead>
<tr>
<th>部门ID</th>
<th>部门名称</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for obj in depart_list %}
<tr>
<th>{{ obj.id }}</th>
<td>{{ obj.title }}</td>
<td>
<a class="btn btn-primary btn-xs" href="/depart/{{ obj.id }}/edit">编辑</a>
<a class="btn btn-danger btn-xs" href="/depart/del?nid={{ obj.id }}">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
depat_add.html
{% extends "layout.html" %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">新建部门</h3>
</div>
<div class="panel-body">
<form method="post">
{% csrf_token %}
<div class="form-group">
<label>部门名称</label>
<input type="text" class="form-control" placeholder="部门名称" name="title">
</div>
<button type="submit" class="btn btn-primary">提 交</button>
</form>
</div>
</div>
</div>
{% endblock %}
depart_edit.html
{% extends "layout.html" %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">编辑部门</h3>
</div>
<div class="panel-body">
<form method="post">
{% csrf_token %}
<div class="form-group">
<label>部门名称</label>
<input type="text" class="form-control" placeholder="部门名称" name="title" value="{{ obj.title }}">
</div>
<button type="submit" class="btn btn-primary">提 交</button>
</form>
</div>
</div>
</div>
{% endblock %}
4. 用户基本功能实现
在这部分我们要完成用户管理的部分,这部分跟上部分有很大的相似程度,所以代码有部分直接借鉴上部分就可以了,旧的知识点讲的就没那么详细了。
为了方便展示,我们先在数据库中放点数据
insert into application01_user(name,password,age,account,Onboarding_time,gender,Department_id)values("周珑瓶","lovejtx",20,98765432.10,"2021-5-20",1,1);
insert into application01_user(name,password,age,account,Onboarding_time,gender,Department_id)values("黄踊拳","water",21,1234567.89,"2022-1-24",1,4);
insert into application01_user(name,password,age,account,Onboarding_time,gender,Department_id)values("黄式欲","3point",24,200203.15,"2018-4-29",1,2);
4.1. 用户列表
主要注意对性别的操作和对日期的操作,此外,在部门操作中还用到了数据表的关联。注意模板语法和python语法的区别。
views.py
# 用户列表
def user_list(request):
user_list = models.User.objects.all()
# for obj in user_list:
# print(obj.id, obj.name, obj.password, obj.age, obj.account, obj.Onboarding_time.strftime("%Y-%m-%d"), obj.get_gender_display(), obj.Department_id, obj.Department.title)
return render(request, "user_list.html", {"user_list": user_list})
user_list.html
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div style="margin-bottom: 10px">
<a class="btn btn-success" href="#">
<span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>
添加用户
</a>
</div>
<div class="panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading">
<span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>
用户列表
</div>
<!-- Table -->
<table class="table table-bordered">
<thead>
<tr>
<th>用户ID</th>
<th>姓名</th>
<th>密码</th>
<th>年龄</th>
<th>账户余额</th>
<th>入职时间</th>
<th>性别</th>
<th>所属部门</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for obj in user_list %}
<tr>
<th>{{ obj.id }}</th>
<td>{{ obj.name }}</td>
<td>{{ obj.password }}</td>
<td>{{ obj.age }}</td>
<td>{{ obj.account }}</td>
<td>{{ obj.Onboarding_time|date:"Y-m-d" }}</td>
<td>{{ obj.get_gender_display }}</td>
<td>{{ obj.Department.title }}</td>
<td>
<a class="btn btn-primary btn-xs" href="/depart/{{ obj.id }}/edit">编辑</a>
<a class="btn btn-danger btn-xs" href="/depart/del?nid={{ obj.id }}">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
效果如下:
4.2. 新建用户
4.2.1. 原始方法(其他框架也可以做)
所谓最原始的方法,其实就是我们之前尝试的方法,一条一条写,比较机械重复,但是还是可以实现功能的。值得注意的就是性别的获取方式和部门的获取方式。
效果如下:
代码如下:
view.py
# 添加用户
def user_add(request):
if request.method == "GET":
context = {
"gender_choices": models.User.gender_choices,
"depart_list": models.Department.objects.all(),
}
return render(request, "user_add.html", context)
user = request.POST.get("name")
pwd = request.POST.get("pwd")
age = request.POST.get("age")
account = request.POST.get("account")
c_time = request.POST.get("c_time")
gender = request.POST.get("gender")
depart_id = request.POST.get("dp")
print(depart_id)
models.User.objects.create(name=user, password=pwd, age=age, account=account, Onboarding_time=c_time, gender=gender, Department_id=depart_id)
return redirect("/user/list")
user_add.html
{% extends "layout.html" %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">添加用户</h3>
</div>
<div class="panel-body">
<form method="post">
{% csrf_token %}
<div class="form-group">
<label>姓名</label>
<input type="text" class="form-control" placeholder="姓名" name="name">
</div>
<div class="form-group">
<label>密码</label>
<input type="text" class="form-control" placeholder="密码" name="pwd">
</div>
<div class="form-group">
<label>年龄</label>
<input type="text" class="form-control" placeholder="年龄" name="age">
</div>
<div class="form-group">
<label>账户余额</label>
<input type="text" class="form-control" placeholder="账户余额" name="account">
</div>
<div class="form-group">
<label>入职时间</label>
<input type="text" class="form-control" placeholder="入职时间" name="c_time">
</div>
<div class="form-group">
<label>性别</label>
<!-- <input type="text" class="form-control" placeholder="性别" name="title">-->
<select class="form-control" name="gender">
{% for item in gender_choices %}
<option value="{{item.0}}">{{item.1}}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label>部门</label>
<!-- <input type="text" class="form-control" placeholder="部门" name="title">-->
<select class="form-control" name="dp">
{% for item in depart_list %}
<option value="{{item.id}}">{{item.title}}</option>
{% endfor %}
</select>
</div>
<button type="submit" class="btn btn-primary">提 交</button>
</form>
</div>
</div>
</div>
{% endblock %}
4.3. Django组件优化新建用户(Form组件、ModelForm组件)
上面原始代码除了机械重复,还有以下问题:
- 数据校验,起码要检验数据是否为空(
form.is_valid
) - 错误提示(重写定义,
LANGUAGE_CODE
) - 页面上每一个字段都要我们重新写一遍(重定义初始化方法)
- 关联数据处理太复杂了(手动获取->后端->前端->循环展示)(field里面自己就解决了)
话不多说,先看看问题解决后的效果:
我们选择用ModelForm组件完成,这里就不提Form了,Form主要是ModelForm理解的一个过渡,理解ModelForm才是这个的精髓。实现的细节和每一步的步骤都写在代码注释里了:
view.py
这部分一定要看明白,主要的前后端连接都已经放到这里了对比原始方法应该是有非常大的优化了,很多机械重复的操作在这里只有一句话。
# 导入form包
from django import forms
# 这里规定models的类,在用modelform的时候必须要有这么一个类
class UserModelForm(forms.ModelForm):
# 校验用的,我们在这里重写下它
name = forms.CharField(min_length=2, label="用户名")
class Meta:
# 这里把它实例化是为了之后可以挑选自己想要的字段而不是所有字段
model = models.User
# 相当于在User里面取元素,注意这里的变量名一定是fields。
# User类里面的东西在ModelForm里面几乎都能取出来。
fields = ["name", "password", "age", "account", "Onboarding_time", "gender", "Department"]
# 当然可以这么做,但是会比较麻烦,要一条一条写过去,所以我们采取后面的重定义类的方法
# widgets = {
# # 默认插件是input标签,我们通过控制插件做到对几个输入框的控制
# "name": forms.TextInput(attrs={"class": "form-control"}),
# "password": forms.PasswordInput(attrs={"class": "form-control"}),
# "age": forms.TextInput(attrs={"class": "form-control"})
# }
# 这部分一定要注意def的缩进
def __init__(self, *args, **kwargs):
# 重定义初始化方法,并执行继承父类的方法
super().__init__(*args, **kwargs)
# 相当于在这里把field里所有的要定义的字段的名字都拿到,这部分是根据源码改编的,不能改name,
# 循环找到所有的插件并给它添加class=form-control的样式
for name, field in self.fields.items():
if name == "password":
field.widget.attrs = {"class": "form-control", "placeholder": field.label, "type": "password"}
field.widget.attrs = {"class": "form-control", "placeholder": field.label}
# 添加用户modelform
def user_madd(request):
if request.method == "GET":
form = UserModelForm()
return render(request, "user_madd.html", {"form": form})
# 与之前相比,这部分多了个数据的校验
form = UserModelForm(data=request.POST)
if form.is_valid():
# form.cleaned_data相当于是获取到了数据
# print(form.cleaned_data)
# 在modelform中这句话可以替换掉之前的models.User.objects.create,目标的表就是Meta中的model的表
form.save()
return redirect("/user/list")
return render(request, "user_madd.html", {"form": form})
前端展示部分直接简单了 ,因为样式之类的在后端Django可以直接渲染出前端的代码,我们前端真正要写的就仅仅是一个框样式和循环。user_madd.html
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">添加用户</h3>
</div>
<div class="panel-body">
<!-- 为了体现我们自己的校验,我们关掉浏览器自带的校验-->
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label>{{ field.label }}</label>
{{ field }}
<span style="color:red;">{{ field.errors.0 }}</span>
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">提 交</button>
</form>
</div>
</div>
</div>
{% endblock %}
我们添加了数据校验的功能,但是蹦出来的是英文,作为码农我们自己看的很习惯,但是这对用户其实是非常不友好的,所以我们想办法解决。其实只需要在settings.py
中修改下配置就好了。注释掉原来的英文语言,并添加中文语言。
# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'zh-hans'
4.4. 编辑用户
老样子,编辑页面的效果跟添加页面应该差不多,核心问题是:
- 将数据传入到编辑页面,并展示出来;
- 数据校验;
- 更新数据库;
我们先放上已经做好了的编辑页面:
主要的变化都在views.py中:
# 编辑用户
def user_edit(request, nid):
# 先获取需要编辑的那一行的数据
row = models.User.objects.filter(id=nid).first()
if request.method == "GET":
# 我们继续用之前的form
# 只要写上instance的对象,默认会把每一个值在value中显示出来 => 简单到有点害怕
form = UserModelForm(instance=row)
return render(request, "user_edit.html", {"form": form})
# 校验
# 这里一定要加instance,不然的话会再添加一个修改后的数据。instance相当于告诉Django你要再哪里更新
form = UserModelForm(data=request.POST, instance=row)
if form.is_valid():
form.save()
# 上面的是批量保存,我们如果有其他的想保存的但是不在fields里面的(用户输入的值以外的),可以用以下方法。
# form.instance.字段名 = 值
return redirect("/user/list")
return render(request, "user_edit.html", {"form": form})
user_edit.html
跟user_madd.html
差不多:
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">编辑用户</h3>
</div>
<div class="panel-body">
<!-- 为了体现我们自己的校验,我们关掉浏览器自带的校验-->
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label>{{ field.label }}</label>
{{ field }}
<span style="color:red;">{{ field.errors.0 }}</span>
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">提 交</button>
</form>
</div>
</div>
</div>
{% endblock %}
4.5. 删除用户
目前不打算给删除加交互,就按照之前的做就好了。
views.py
# 删除用户
def user_del(request, nid):
# 先获取需要编辑的那一行的数据
models.User.objects.filter(id=nid).delete()
return redirect('/user/list')
效果如下:
至此,我们完成了一整个系统的设计与实现,当然后期会有更多更完整的系统实现。