Django是目前比较火爆的框架,之前有在知乎刷到,很多毕业生进入大厂实习后因为不会git和Django框架3天就被踢掉了,因为他们很难把自己的工作融入到整个组的工作中。因此,我尝试自学Django并整理出如下笔记。
这篇博客主要完善了之前的一些功能,代码适合更多的引用场景。包括管理员的密码加密(md5),管理员密码不显示(forms.PasswordInput),管理员密码重置(save()),管理员密码确认(ValidationError),管理员密码修改时校验(ValidationError)。实际上,就是在之前的增删改查的大框架下根据不同的应用场景修改一些边边角角的地方。部分工作基于原来已有的工作。
1. 介绍
因为这个小任务已经进行了很多工作了,有之前任务的积累。如果直接看代码可能会被文件之间的关系整迷糊,这里梳理一下关键的文件和文件夹的内容。
static 包含各种静态文件,css样式,jquery等
templates 包含各种html文件,所有的静态页面都是这里出来的
utils 包含各种中途会用到的工具
- bootstrap_input.py 继承ModelForm,在其中嵌入Bootstrap样式
- encrypt.py 利用md5加密密码
- form.py 所有的ModelForm存这里
- page.py 分页类,负责为界面添加分页功能
views
- admin.py 管理员界面的views放这里
- depart.py 部门界面的views放这里
- num.py 号码界面的views放这里
- user.py 用户界面的views放这里
__init__.py 略
admin.py 略
apps.py 略
models.py 存放我们对数据库的数据表的定义
test.py 略
Employee_Management
- __init__.py 略
- asgi.py 略
- settings.py 略
- urls.py views内的py文件中各个函数与templates内html文件的对应关系
- wsgi.py 略
2. 管理员界面
制作界面之前,我们首先要完成数据表的建立。还是要在models.py
中创建管理员类,并通过命令行在终端中链接数据库。
# 管理员
class Admin(models.Model):
username = models.CharField(verbose_name="用户名", max_length=32)
password = models.CharField(verbose_name="密码", max_length=64)
除了数据库本身,我们还需要把界面展示到前端。这需要我们连接数据库,将数据传入到前端;完成前端界面的设计;加入前端的框架。
使用ModelForm连接数据库。utils/form.py
:
from django import forms
# 加入Bootstrap样式
class BootstrapModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs["class"] = "form-control"
field.widget.attrs["placeholder"] = field.label
# ModelForm
class AdminModelForm(BootstrapModelForm):
class Meta:
model = models.Admin
# confirm_password在输入密码时用于确认密码
fields = ["username", "password"]
widgets = {
"password": forms.PasswordInput
}
连接数据库,使用将所有的数据传到前端。其中,PAGE是我们之前自己封装的一个类,可以根据结果分页(04 号码管理部分() 1.7. 分页(封装类实现))
from django.shortcuts import render, redirect
from application01 import models
from application01.utils.page import PAGE
from application01.utils.form import AdminModelForm
def admin_list(request):
# 搜索功能
search_dict = dict()
value = request.GET.get("search", "")
if value:
search_dict["username__contains"] = value
# 如果有搜索就会读取搜索之后的结果。所有的结果都会根据id排序
admin_list = models.Admin.objects.filter(**search_dict).order_by("id")
# 分页
page_object = PAGE(request, admin_list, page_size=8)
context = {
"admin_list": page_object.page_queryset, # 分完页的数据
"search_data": value, # 搜索之后的数据
"page_string": page_object.create_html(), # 生成的页码
"search_page": page_object.search() # 搜索功能
}
# 传送到前端
return render(request, "admin_list.html", context)
admin_list.html
(看不懂首行的layout.html可以看03 前端模板嵌套中3. 模板的继承):
{% extends "layout.html" %}
{% block content %}
<div class="container">
<div style="margin-bottom: 10px">
<a class="btn btn-success" href="/admin/add">
<span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>
加入管理员
</a>
<div style="float: right; width: 300px;">
<form method="get">
<div class="input-group">
<input type="text" class="form-control" placeholder="搜索用户名" name="search" value="{{search_data}}">
<span class="input-group-btn">
<button class="btn btn-default" type="submit">
<span class="glyphicon glyphicon-search"></span>
</button>
</span>
</div>
</form>
</div>
</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>
</tr>
</thead>
<tbody>
{% for obj in admin_list %}
<tr>
<th>{{ obj.id }}</th>
<td>{{ obj.username }}</td>
<td>********</td>
<td>
<a class="btn btn-primary btn-xs" href="/admin/{{ obj.id }}/edit">编辑</a>
<a class="btn btn-danger btn-xs" href="/admin/{{ obj.id }}/del">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<ul class="pagination" style="width:100%;">
{{ page_string }}
{{ search_page }}
</ul>
</div>
{% endblock %}
效果如下:
3. 添加管理员
添加管理员类似的任务我们在之前有做过很多:添加用户、添加号码、添加部门……这次我们做出一些改变。我们在上网的时候应该都有过类似的体验:我们在注册账号时,平台为了防止我们输错密码,会加入一个“请再次输入密码”,我们也完成这个功能。此外,为了防止信息泄露,我们还要给管理员的密码加入加密功能。
首先是在utils/form.py
中修改ModelForm:
from application01.utils.encrypt import md5
from application01.utils.bootstrap_input import BootstrapModelForm
from django import forms
from django.core.validators import RegexValidator, ValidationError
class AdminModelForm(BootstrapModelForm):
# 定义“确认密码”
confirm_password = forms.CharField(
label="确认密码",
widget=forms.PasswordInput(render_value=True) # 密码不一致之后,报错之后不置空
)
class Meta:
model = models.Admin
# confirm_password在输入密码时用于确认密码
fields = ["username", "password", "confirm_password"]
widgets = {
"password": forms.PasswordInput
}
# 确定是否存在此管理员
def clean_username(self):
text_admin = self.cleaned_data["username"]
exists = models.Admin.objects.filter(username=text_admin).exists()
if exists:
raise ValidationError("管理员已存在")
return text_admin
# 加密
def clean_password(self):
pwd = self.cleaned_data.get("password")
return md5(pwd)
# 对比两次输入的密码
def clean_confirm_password(self):
pwd = self.cleaned_data.get("password")
confirm = md5(self.cleaned_data.get("confirm_password"))
if pwd != confirm:
raise ValidationError("密码不一致,请重新输入")
return confirm
其中,md5加密函数位于utils/encrypt.py
中。
# -*- coding:utf-8 -*-
from django.conf import settings
import hashlib
# 加密
def md5(data):
# SECRET_KEY是Django自带的
obj = hashlib.md5(settings.SECRET_KEY.encode("utf-8"))
obj.update(data.encode("utf-8"))
return obj.hexdigest()
views/admin.py
:
def admin_add(request):
# 如果没有信息POST过来,我们通过如下代码生成界面,等待POST
if request.method == "GET":
form = AdminModelForm()
return render(request, "admin_add.html", {"form": form})
form = AdminModelForm(data=request.POST)
# 有数据POST过来之后
# 如果数据通过了校验,保存数据
if form.is_valid():
form.save()
return redirect("/admin/list")
# 如果数据没有通过校验,还是在添加界面,同时返回错误
return render(request, "admin_add.html", {"title": "新建管理员", "form": form})
admin_add.html
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">{{ 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. 管理员编辑
编辑部分大同小异,不过我们加入了权限。后台无法修改管理员的用户名,此外,后台也无法通过平台得到管理员的密码的明文,只有重置密码的权力(6. 管理员密码重置)。为了提高系统的稳定性,我们再加入判断,防止用户输入不存在的管理员编号。
我们特地为编辑写一个ModelForm,因为我们不希望修改后的密码跟之前的密码一样,所以修改之前的clean_password
,在里面加了一个校验。utils/forms.py
:
class AdminEditModelForm(BootstrapModelForm):
confirm_password = forms.CharField(
label="确认密码",
widget=forms.PasswordInput(render_value=True) # 密码不一致之后,报错之后不置空
)
username = forms.CharField(disabled=True, label="用户名")
class Meta:
model = models.Admin
fields = ["username", "password", "confirm_password"]
def clean_password(self):
pwd = self.cleaned_data.get("password")
md5_pwd = md5(pwd)
# 校验当前密码和新输入的密码是否一致
# instance 就是传进来的对象,pk就是id
exist = models.Admin.objects.filter(id=self.instance.pk, password=md5_pwd).exists()
if exist:
raise ValidationError('密码不能与之前的密码相同')
return md5_pwd
def clean_confirm_password(self):
pwd = self.cleaned_data.get("password")
confirm = md5(self.cleaned_data.get("confirm_password"))
if pwd != confirm:
raise ValidationError("密码不一致,请重新输入")
return confirm
当然,如果你不想让用户看到明文,也可以直接给password一个widget=forms.PasswordInput(render_value=True)
。
连接数据库。views/admin.py
:
def admin_edit(request, nid):
# 判断nid存不存在
row_obj = models.Admin.objects.filter(id=nid).first()
if not row_obj:
return render(request, 'error.html', {'msg': '数据不存在'})
# 显示默认值
if request.method == 'GET':
form = AdminEditModelForm(instance=row_obj)
return render(request, "admin_add.html", {"title": "编辑管理员", "form": form})
form = AdminEditModelForm(instance=row_obj, data=request.POST)
# 校验
if form.is_valid():
form.save()
return redirect("/admin/list")
return render(request, "admin_add.html", {"title": "编辑管理员", "form": form})
效果如下:
5. 删除管理员
这部分毫无改变,其实也没啥能改的。
def admin_del(request, nid):
models.Admin.objects.filter(id=nid).delete()
return redirect('/admin/list')
6. 管理员密码重置
在现实情况中,很多情况下,即使是后台人员也不可以直接查看用户的隐私,这个时候如果用户忘记了密码,我们就没有办法直接去改它,我们往往会选择直接重置这个密码。在用户重置密码之后,将跳出提醒界面,并将密码重置为1129。
def admin_reset(request, nid):
row_obj = models.Admin.objects.filter(id=nid).first()
if not row_obj:
return render(request, 'error.html', {'msg': '数据不存在'})
# 显示默认值
row_obj.password = md5('1129')
row_obj.save()
return render(request, 'error.html', {'msg': '数据重置成功,{}现在的密码为默认密码1129'.format(row_obj.username)})
效果如下: