主题:员工管理系统
参考:最新Python的web开发全家桶(django+前端+数据库)_哔哩哔哩_bilibili
1. 创建项目并生成数据库与数据表
新建项目,创建注册app,具体参见前两节,这里只展示在【models.py】中定义好的两个类,即对应着数据库中的两个数据表
#【models.py】
from django.db import models
# Create your models here.
class Department(models.Model):
# 部门表
title = models.CharField(verbose_name='部门标题',max_length=32) #verbose_name:对当前列的注解,可写可不写,写了便于理解
def __str__(self): # 应对使用ModelForm时,输出部门对象无法输出title的情况 [1]
return self.title
class UserInfo(models.Model):
# 员工表
name = models.CharField(verbose_name='姓名',max_length=16) # [2]
password = models.CharField(verbose_name='密码', max_length=64)
age = models.IntegerField(verbose_name='年龄')
account = models.DecimalField(verbose_name="账户余额",max_digits=10,decimal_places=2,default=0) # 分别定义m,d
create_time = models.DateTimeField(verbose_name="入职时间")
# django中的约束:在填写性别时只能填写0或1 [3]
gender_choices=(
(0, "女"),
(1, '男')
)
gender = models.SmallIntegerField(verbose_name="性别",choices=gender_choices)
# 外键设置 [4]
depart = models.ForeignKey(to="Department", to_field="id", on_delete=models.CASCADE)
# to:关联的表,to_field关联表的列
# django自动的外键命名方式:设置列名 + ‘_id’ , 如:depart_id
# 设置关联表相关数据删除时的处理方式:
# 1. 级联删除:depart = models.ForeignKey(to="Department",to_field="id",on_delete=models.CASCADE)
# 2. 置空:depart = models.ForeignKey(to="Department",to_field="id",null=True,blank=True,on_delete=models.SET_NULL)
【注意】
-
在部门表中,需要重写类的
__str__()
函数,以保证在直接print对象时,可以输出指定的内容(这里是部门名称),以解决后面在form表单输出时直接输出部门对象导致无法输出部门名称的问题 [1] -
在员工表中的成员定义中的
verbose_name
字段,是对当前成员的解释信息,后面使用ModelForm的时候会用到 -
在员工表中,在性别列的定义中,我们可以使用
choice
字段,来完成自定义的映射和数据约束,即使用一个以双元素元组为元素的元组定义映射,然后在声明成员时将这个元组当作choice
字段的值 [2] -
在员工表中的“所属部门”需要和部门表进行关联映射,所以需要设置为外键以满足数据约束(员工表中数据必须在部门表中存在),设置方式即
外键名 = models.ForeignKey(to="关联表名", to_field="关联表的列(一般是主键)", on_delete=关联表删除数据时的处理方式)
2. 模板的继承
随便找的一个巨丑的模板:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户管理系统</title>
<style>
/* 添加一些基本样式,可以根据需要进行自定义 */
body {
font-family: Arial, sans-serif;
}
.navbar {
background-color: #333;
overflow: hidden;
}
.navbar a {
float: left;
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
.navbar a:hover {
background-color: #ddd;
color: black;
}
.title {
text-align: center;
padding: 20px;
background-color: #f4f4f4;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 8px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background-color: #f2f2f2;
}
.btn {
padding: 4px 8px;
border: none;
cursor: pointer;
}
.btn-edit {
background-color: #4CAF50;
color: white;
}
.btn-delete {
background-color: #f44336;
color: white;
}
.input-container {
text-align: center;
padding: 20px;
}
.input-field {
padding: 8px;
}
</style>
</head>
<body>
<div class="title">
<h1>用户管理系统</h1>
</div>
<div class="navbar">
<a href="user_list.html">用户列表</a>
<a href="department_list.html">部门列表</a>
</div>
<div class="title">
<h2>用户列表</h2>
</div>
<table>
<tr>
<th>ID</th>
<th>部门</th>
<th>操作</th>
</tr>
<tr>
<td>1</td>
<td>销售部</td>
<td>
<button class="btn btn-edit">编辑</button>
<button class="btn btn-delete">删除</button>
</td>
</tr>
<tr>
<td>2</td>
<td>人力资源部</td>
<td>
<button class="btn btn-edit">编辑</button>
<button class="btn btn-delete">删除</button>
</td>
</tr>
<!-- 添加更多行... -->
</table>
<div class="input-container">
<h2>添加新部门</h2>
<input type="text" class="input-field" placeholder="部门名称">
<button class="btn btn-submit">提交</button>
</div>
</body>
</html>
大概是这样的:
但是有导航栏,功能齐全,我们想让后面写得所有页面都有导航栏,又不想每个页面都把这份代码写一遍(而且那样也不好修改),于是我们可以使用django的模板继承功能。
2.1 编写模板页面
在app【templates】目录下,编写【layout.html】文件(当然也可以不叫这个名)
<!-- 【layout.html】 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% block title %}
{% endblock %}
<style>
/* 添加一些基本样式,可以根据需要进行自定义 */
body {
font-family: Arial, sans-serif;
}
.navbar {
background-color: #333;
overflow: hidden;
}
.navbar a {
float: left;
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
.navbar a:hover {
background-color: #ddd;
color: black;
}
.title {
text-align: center;
padding: 20px;
background-color: #f4f4f4;
}
a:link {
color: white;
background-color: transparent;
text-decoration: none;
}
a:visited {
color: white;
background-color: transparent;
text-decoration: none;
}
a:hover {
color: white;
background-color: transparent;
text-decoration: underline;
}
a:active {
color: white;
background-color: transparent;
text-decoration: underline;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 8px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background-color: #f2f2f2;
}
.btn {
padding: 4px 8px;
border: none;
cursor: pointer;
}
.btn-edit {
background-color: #4CAF50;
color: white;
}
.btn-delete {
background-color: #f44336;
color: white;
}
.input-container {
text-align: center;
padding: 20px;
}
.input-field {
padding: 8px;
}
</style>
</head>
<body>
<div class="title">
<h1>员工管理系统</h1>
</div>
<div class="navbar">
<a href="/depart/list/">部门列表</a>
<a href="/user/list/">用户列表</a>
</div>
{% block content %}
{% endblock %}
<div class="title">
<h2>欢迎访问员工管理系统</h2>
</div>
</body>
</html>
注意,在每个页面都要保留的部分和样式表,我们都原封不动的保留,但是在每个页面需要特异性的地方,我们使用格式:
{% block 名称 %}
{% endblock %}
进行占位,这样,【layout.html】就成了挖好了空的“代码模板”,供我们后续使用
2.2 继承并使用模板
在编写特定页面时,我们只需要继承模板页面,然后只需要填写在挖空部分的代码即可
<!-- 【depart_list.html】 -->
{% extends 'layout.html' %} <!-- 使用此代码继承 layout.html -->
{% block title %} <!-- 在占位符之间加入需要的代码 -->
<title>部门列表</title>
{% endblock %}
{% block content %}
<h2 class="title">部门列表</h2>
<button><a href="/depart/add/">添加部门</a></button>
<table>
<tr>
<th>ID</th>
<th>部门</th>
<th>操作</th>
</tr>
<tbody>
{% for obj in data_list %}
<tr>
<td>{{ obj.id }}</td>
<td>{{ obj.title }}</td>
<td>
<button class="btn btn-edit" ><a href="/depart/{{ obj.id }}/update/">编辑</a></button>
<button class="btn btn-delete"><a href="/depart/delete/?nid={{ obj.id }}">删除</a></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
这样,我们就可以在多种不同页面中使用同一套布局和样式
<!-- 【depart_add.html】 -->
{% extends 'layout.html' %}
{% block title %}
<title>添加部门</title>
{% endblock %}
{% block content %}
<h2 class="title">添加部门</h2>
<div class="input-container">
<form method="post" ><!--post当前页面时,action可以不写-->
{% csrf_token %} <!-- 别忘了!! -->
<input type="text" name="depart_name" class="input-field" placeholder="部门名称">
<button class="btn btn-submit">提交</button>
</form>
</div>
{% endblock %}
3. 完善部门管理功能
关于页面数据表的增加与删除,前面的学习笔记中都有记录,这里不再赘述,具体代码如下:
# 【urls.py】
from django.urls import path
from app01 import views
urlpatterns = [
#path('admin/', admin.site.urls),
path('depart/list/', views.depart_list),
path('depart/add/', views.depart_add),
path('depart/delete/', views.depart_del),
#格式化的地址输入,/depart/和/update/之间必须有一个int型的数值
path('depart/<int:nid>/update/', views.depart_upd),
]
# 【views.py】
from django.shortcuts import render, redirect
from app01.models import Department,UserInfo
# Create your views here.
def depart_list(request):
## 部门列表
data_list = Department.objects.all()
return render(request,'depart_list.html',{'data_list':data_list})
def depart_add(request):
if request.method == 'GET':
return render(request,'depart_add.html')
depart_name = request.POST.get("depart_name")
Department.objects.create(title=depart_name)
return redirect('/depart/list/')
def depart_del(request):
nid = request.GET.get('nid')
Department.objects.filter(id=nid).delete()
return redirect("/depart/list/")
def depart_upd(request,nid): # 多传一个nid的参数
if request.method == 'GET':
row_obj = Department.objects.filter(id=nid).first()
return render(request,"depart_update.html",{"row_obj": row_obj})
title = request.POST.get("depart_name")
Department.objects.filter(id=nid).update(title=title)
return redirect("/depart/list/")
这里主要新增了一个更改部门名称的功能,首先,修改数据和新增数据需要的操作和输入基本相同,其页面在新增部门的页面基础上稍作修改即可。主要是获取到修改部门的id,然后根据id在输入框中显示出部门名称。
<!-- 【depart_update.html】 -->
{% extends 'layout.html' %}
{% block title %}
<title>编辑部门</title>
{% endblock %}
{% block content %}
<h2 class="title">编辑部门</h2>
<div class="input-container">
<form method="post" ><!--post当前页面时,action可以不写-->
{% csrf_token %} <!-- 别忘了!! -->
<input type="text" name="depart_name" class="input-field" placeholder="部门名称" value="{{ row_obj.title }}">
<button class="btn btn-submit">提交</button>
</form>
</div>
{% endblock %}
在修改部门的数据传递上,除去之前学到的使用url后面加上 ’ ?nid=xx ’ 的方式,我们也可以使用另外一种格式化在地址中加入数值的方式,即在【urls.py】的path中将地址写为:
path('depart/<int:nid>/update/', views.depart_upd),
/<int:nid>/
代表url在当前位置必须加入一个 int 型数值的参数,参数名为 nid。
而相应的,在【views.py】定义的函数中,我们不需要使用 GET 方法获取nid,而是直接在函数参数中写上nid,即可直接获得nid的值。
def depart_upd(request,nid):
....
在部门列表页面【depart_list.html】中通过url传递数据时,按照【urls.py】中定义的格式在相应位置写入nid的值即可
<button class="btn btn-edit" ><a href="/depart/{{ obj.id }}/update/">编辑</a></button>
4. 通过ModelForm功能实现员工信息管理
上面部门管理时进行数据管理的缺点:
- 用户提交数据没有校验,发生错误应该有错误提示
- 页面上每个字段都要独立写一遍
- 数据关联全凭手动
以新建用户为例,使用ModelForm组件:
#【views.py】
from django import forms
from app01 import models
class UserModelForm(forms.ModelForm):
# 定义数据约束(默认非空,无需设置)
age = forms.IntegerField(min_value=1) #重写age,限制其输入必须为正数
class Meta:
model = models.UserInfo
# 选择需要输入数据的列名
fields = ['name','password','age','account','create_time','gender','depart']
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
for name,field in self.fields.items():#修改构造函数,给所有输入框加上样式表和其他属性(如果有想用不同样式的,特判name即可)
field.widget.attrs = {'class':'input-field','placeholder':field.label}
def user_add(request):
if request.method == 'GET':
form = UserModelForm()
return render(request,'user_add.html',{"form":form})
# 提交数据校验
form = UserModelForm(data=request.POST)
if form.is_valid():
#数据合法,一键保存到数据库
form.save() #直接保存到form对应那个的表里面
return redirect('/user/list/')
else:
# 校验失败,在页面显示错误信息
return render(request,'user_add.html',{"form":form})
由上面可以看到,在【views.py】页面,除去编写url对应函数外,需要声明一个继承自ModelForm的类,其中可以设置数据约束、选择待编辑列名和输入表单样式属性等,在函数部分,直接将form对象传入html文件即可。
而在获取到用户输入的表单信息后,可以进行数据合法性判断,若数据合法,可以使用form.save()
一键保存到数据库,若不合法,返回错误信息到页面即可。
<!-- 【user_add.html】 -->
{% extends 'layout.html' %}
{% block title %}
<title>添加用户</title>
{% endblock %}
{% block content %}
<h2 class="title">添加用户</h2>
<div class="input-container">
<form method="post" ><!--post当前页面时,action可以不写-->
{% csrf_token %} <!-- 别忘了!! -->
{% for field in form %}
<div>
{{ field.label }} : {{ field }}
<span>{{ field.errors.0 }}</span><!-- 显示错误信息!! -->
<br><br><br>
</div>
{% endfor %}
<button class="btn btn-edit">提交</button>
</form>
</div>
{% endblock %}
在html文件中,直接将form对象进行渲染,其中field.lable
是之前在【models.py】定义列名时写下的verbose_name
字段,field
会直接打印相应对象(所以外键需要设置字符输出)
此外,若编辑用户时输入错误或不符合数据要求,还可以通过field.errors
返回相应的错误信息 对输入者进行提示。
5. 用户编辑功能处理
# 【views.py】
def user_upd(request,nid):
row_obj = UserInfo.objects.filter(id=nid).first()
if request.method == 'GET':
# 先获取nid对应的数据
form = UserModelForm(instance=row_obj) # 表达自动显示默认值
return render(request,'user_update.html',{"form":form})
form = UserModelForm(data=request.POST,instance=row_obj)# 只有加上instance才能正确修改
if form.is_valid():
form.save()
return redirect("/user/list/")
return render(request,'user_update.html',{"form":form})
用户编辑页面和用户添加页面大致相同,需要注意的是在显示表单时,我们可以通过instance
字段来使用目前获得到的数据库数据对表单进行填充
在获取到用户使用POST上传的编辑好的信息后,也是需要instance
字段来保证正确修改(不然就会在数据库中直接新建一个用户,而不是修改)
<!-- 【user_update.html】 -->
{% extends 'layout.html' %}
{% block title %}
<title>编辑用户</title>
{% endblock %}
{% block content %}
<h2 class="title">编辑用户</h2>
<div class="input-container">
<form method="post" ><!--post当前页面时,action可以不写-->
{% csrf_token %} <!-- 别忘了!! -->
{% for field in form %}
<div>
{{ field.label }} : {{ field }}
<span>{{ field.errors.0 }}</span><!-- 显示错误信息!! -->
<br><br><br>
</div>
{% endfor %}
<button class="btn btn-edit">提交</button>
</form>
</div>
{% endblock %}
6. 用户管理其他功能完善
# 【urls.py】
urlpatterns = [
#【之前的那一部分】...
path('user/list/', views.user_list),
path('user/add/', views.user_add),
path('user/delete/', views.user_del),
# 格式化的地址输入,/depart/和/update/之间必须有一个int型的数值
path('user/<int:nid>/update/', views.user_upd),
]
【user_list.html】与部门列表相差无几,可以注意的是使用了choice=gender_choice
后需要使用get_gender_display
来进行自动映射,而外键可以直接使用外键成员名即可完成自动映射
<!-- 【user_list.html】 -->
{% extends 'layout.html' %}
{% block title %}
<title>员工列表</title>
{% endblock %}
{% block content %}
<h2 class="title">员工列表</h2>
<button class="btn btn-edit"><a href="/user/add/">⊕添加员工</a></button>
<table>
<tr>
<th>ID</th>
<th>姓名</th>
<th>密码</th>
<th>年龄</th>
<th>余额</th>
<th>创建时间</th>
<th>性别</th>
<th>部门</th>
<th>操作</th>
</tr>
<tbody>
{% for obj in data_list %}
<tr>
<td>{{ obj.id }}</td>
<td>{{ obj.name}}</td>
<td>{{ obj.password }}</td>
<td>{{ obj.age}}</td>
<td>{{ obj.account }}</td>
<td>{{ obj.create_time|date:"Y-m-d"}}</td><!-- 模板语法不允许加括号,时间格式化 -->
<td>{{ obj.get_gender_display}}</td> <!-- 使用gender_choices自动映射 -->
<td>{{ obj.depart}}</td><!-- 直接写外键成员名就可以自动映射 -->
<td>
<button class="btn btn-edit" ><a href="/user/{{ obj.id }}/update/">编辑</a></button>
<button class="btn btn-delete"><a href="/user/delete/?nid={{ obj.id }}">删除</a></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
列表和删除功能与部门做法一样,不再赘述
# 【views.py】
def user_list(request):
data_list = UserInfo.objects.all()
return render(request,'user_list.html',{'data_list':data_list})
def user_del(request):
nid = request.GET.get('nid')
UserInfo.objects.filter(id=nid).delete()
return redirect("/user/list/")
效果:
编辑用户时,可以做到输入检查