号外号外,基于Pycharm的Django学习,项目实战后续来啦!
前面我们已经学习了最基础的增删改查,现在主要是对前面的补充,以及尝试一些新花样。
首先数据库表结构准备起来!
class PrettyNum(models.Model):
"""靓号管理"""
mobile = models.CharField(verbose_name="手机号", max_length=11)
price = models.IntegerField(verbose_name="价格", default=0)
level_choices = (
(1, "1级"),
(2, "2级"),
(3, "3级"),
(4, "4级"),
(5, "5级")
)
level = models.SmallIntegerField(verbose_name="级别", choices=level_choices, default=1)
status_choices = (
(1, "已占用"),
(2, "未使用")
)
status = models.SmallIntegerField(verbose_name="状态", choices=status_choices, default=2)
然后自己准备点数据进行测试(按理来说,后续应该都是自己爬取或者使用数据集):
{% extends 'layout.html' %}
{% block content %}
class NumberModelForm(forms.ModelForm):
# 验证一
# mobile = forms.CharField(
# label="手机号",
# # validators必须是数组!
# validators=[RegexValidator(r'^\d{11}$', '手机号必须11位')]
# )
class Meta:
model = models.PrettyNum
fields = "__all__"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs = {"class": "form-control", "placeholder": field.label}
# 验证二 (可以使用除了正则以外的验证方法)
def clean_mobile(self):
txt_mobile = self.cleaned_data["mobile"]
if len(txt_mobile) != 11:
# 验证不通过
raise ValidationError("格式错误")
# 验证通过 将用户通过的值返回 数据库就保存的是这个数据
return txt_mobile
def number_add(request):
if request.method == "GET":
form = NumberModelForm()
return render(request, "number_add.html", {"form": form})
form = NumberModelForm(data=request.POST)
if form.is_valid():
# 如果数据合法 保存到数据库
print(form.cleaned_data)
form.save()
return redirect("/number/list/")
else:
print(form.errors)
return render(request, "number_add.html", {"form": form})
其中对字段的有效值验证有两种方法:
# 验证一
mobile = forms.CharField(
label="手机号",
# validators必须是数组!
validators=[RegexValidator(r'^\d{11}$', '手机号必须11位')]
)
# 验证二 (可以使用除了正则以外的验证方法)
def clean_mobile(self):
txt_mobile = self.cleaned_data["mobile"]
if len(txt_mobile) != 11:
# 验证不通过
raise ValidationError("格式错误")
# 验证通过 将用户通过的值返回 数据库就保存的是这个数据
return txt_mobile
对于添加页面,和用户添加差不多,前期主要是功能的实现,等到后期就是进行代码优化等了。
{% extends 'layout.html' %}
{% block content %}
新建靓号
{% endblock %}
编辑可以和新建所使用的ModelForm一样,也可以重新写,比如在编辑的时候,不允许所有字段都编辑。
class NumberEditModelForm(forms.ModelForm):
class Meta:
model = models.PrettyNum
# 不允许编辑手机号
exclude = ["mobile"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs = {"class": "form-control", "placeholder": field.label}
def number_edit(request, nid):
# 根据id去数据库获取编辑的那一行
row_object = models.PrettyNum.objects.filter(id=nid).first()
if request.method == "GET":
form = NumberEditModelForm(instance=row_object)
return render(request, "number_edit.html", {"form": form})
# 用Post提交的数据 进行数据校验
form = NumberEditModelForm(data=request.POST, instance=row_object)
if form.is_valid():
# 如果数据合法 保存到数据库
print(form.cleaned_data)
# 默认保存的是用户输入的所有数据 如果想要再保存用户输入的额外的值
# form.instance.字段名=值
form.save()
return redirect("/number/list/")
else:
print(form.errors)
return render(request, "number_edit.html", {"form": form})
现在假设我们添加或者编辑手机号,并且不允许手机号重复,那怎么办呢?
我们可以去数据库搜是否有和其是一样的,但是有时候编辑不编辑手机号,所以搜的时候还要排除自己啊。
这种验证只能在钩子函数中操作,因为其涉及数据库。
class NumberModelForm(forms.ModelForm):
# 验证一
mobile = forms.CharField(
label="手机号",
# validators必须是数组!
validators=[RegexValidator(r'^\d{11}$', '手机号必须11位')]
)
class Meta:
model = models.PrettyNum
fields = "__all__"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs = {"class": "form-control", "placeholder": field.label}
# 验证二 (可以使用除了正则以外的验证方法)
# def clean_mobile(self):
# txt_mobile = self.cleaned_data["mobile"]
# if len(txt_mobile) != 11:
# # 验证不通过
# raise ValidationError("格式错误")
# # 验证通过 将用户通过的值返回 数据库就保存的是这个数据
# return txt_mobile
# 验证二 (主要用于设计数据库操作的验证)
def clean_mobile(self):
txt_mobile = self.cleaned_data["mobile"]
# 查看数据库中该手机号是否存在
exists = models.PrettyNum.objects.filter(mobile=txt_mobile).exists()
if exists:
# 验证不通过
raise ValidationError("手机号已存在")
# 验证通过 将用户通过的值返回 数据库就保存的是这个数据
return txt_mobile
def number_add(request):
if request.method == "GET":
form = NumberModelForm()
return render(request, "number_add.html", {"form": form})
form = NumberModelForm(data=request.POST)
if form.is_valid():
# 如果数据合法 保存到数据库
print(form.cleaned_data)
form.save()
return redirect("/number/list/")
else:
print(form.errors)
return render(request, "number_add.html", {"form": form})
class NumberEditModelForm(forms.ModelForm):
mobile = forms.CharField(
label="手机号",
# validators必须是数组!
validators=[RegexValidator(r'^\d{11}$', '手机号必须11位')]
)
class Meta:
model = models.PrettyNum
fields = "__all__"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs = {"class": "form-control", "placeholder": field.label}
# 验证二 (主要用于设计数据库操作的验证)
def clean_mobile(self):
# 获取当前编辑的对象的id
# self.instance.pk
txt_mobile = self.cleaned_data["mobile"]
# 查看数据库中除编辑的该手机号之外是否存在
exists = models.PrettyNum.objects.exclude(id=self.instance.pk).filter(mobile=txt_mobile).exists()
if exists:
# 验证不通过
raise ValidationError("手机号已存在")
# 验证通过 将用户通过的值返回 数据库就保存的是这个数据
return txt_mobile
def number_edit(request, nid):
# 根据id去数据库获取编辑的那一行
row_object = models.PrettyNum.objects.filter(id=nid).first()
if request.method == "GET":
form = NumberEditModelForm(instance=row_object)
return render(request, "number_edit.html", {"form": form})
# 用Post提交的数据 进行数据校验
form = NumberEditModelForm(data=request.POST, instance=row_object)
if form.is_valid():
# 如果数据合法 保存到数据库
print(form.cleaned_data)
# 默认保存的是用户输入的所有数据 如果想要再保存用户输入的额外的值
# form.instance.字段名=值
form.save()
return redirect("/number/list/")
else:
print(form.errors)
return render(request, "number_edit.html", {"form": form})
现在想实现在页面中添加搜索功能!
常见语法如下:
def number_list(request):
# 拿到搜索的值 如果没有设置为空
data_dict = {}
search_data = request.GET.get("q", "")
if search_data:
data_dict["mobile__contains"] = search_data
# 按照level降序排列
data_list = models.PrettyNum.objects.filter(**data_dict).order_by("-level")
return render(request, "number_list.html", {"data_list": data_list, "search_data": search_data})
{% extends 'layout.html' %}
{% block content %}
但是这样的话,不懂我们页面设计的人可能就不会了,所以要设置分页工具。
我就简单的生成了15条数据,所以这里每一页我设置的3条奥!
先来测试一下:
def number_list(request):
# 拿到搜索的值 如果没有设置为空
data_dict = {}
search_data = request.GET.get("q", "")
if search_data:
data_dict["mobile__contains"] = search_data
page = int(request.GET.get("page", 1))
pagesize = 3
start = (page-1)*pagesize
end = page*pagesize
# 按照level降序排列
# data_list = models.PrettyNum.objects.filter(**data_dict).order_by("-level")[start:end]
data_list = models.PrettyNum.objects.filter(**data_dict)[start:end]
return render(request, "number_list.html", {"data_list": data_list, "search_data": search_data})
{% extends 'layout.html' %}
{% block content %}
{% endblock %}
但是这里的分页我们设置死了,但是如果其数据比较多,我们需要其自己自动跳转,所以还需要处理。
def number_list(request):
# 拿到搜索的值 如果没有设置为空
data_dict = {}
search_data = request.GET.get("q", "")
if search_data:
data_dict["mobile__contains"] = search_data
page = int(request.GET.get("page", 1))
pagesize = 3
start = (page-1)*pagesize
end = page*pagesize
# 按照level降序排列
# data_list = models.PrettyNum.objects.filter(**data_dict).order_by("-level")[start:end]
data_list = models.PrettyNum.objects.filter(**data_dict)[start:end]
# 数据总条数
total_count = models.PrettyNum.objects.filter(**data_dict).count()
# 总页码计算 c(商),d(余数) = divmod(a,b)
total_page_count, div = divmod(total_count, pagesize)
if div:
total_page_count += 1
# 页码
page_str_list = []
# range()前取后不取
for i in range(1, total_page_count+1):
ele = '{} '.format(i, i)
page_str_list.append(ele)
# 将字符串使用mark_safe()后才能变成html
page_string = mark_safe("".join(page_str_list))
return render(request, "number_list.html", {"data_list": data_list, "search_data": search_data, "page_string":page_string})
这里的页面网址跳转是解决了,但是还是有一个问题,如果页面太多了咋整?所以还是要解决一下!
def number_list(request):
# 拿到搜索的值 如果没有设置为空
data_dict = {}
search_data = request.GET.get("q", "")
if search_data:
data_dict["mobile__contains"] = search_data
page = int(request.GET.get("page", 1))
pagesize = 2
start = (page-1)*pagesize
end = page*pagesize
# 按照level降序排列
# data_list = models.PrettyNum.objects.filter(**data_dict).order_by("-level")[start:end]
data_list = models.PrettyNum.objects.filter(**data_dict)[start:end]
# 数据总条数
total_count = models.PrettyNum.objects.filter(**data_dict).count()
# 总页码计算 c(商),d(余数) = divmod(a,b)
total_page_count, div = divmod(total_count, pagesize)
if div:
total_page_count += 1
# 计算出显示当前页的前2页 后2页
plus = 2
# 设置不要出现负值
if total_page_count <= 2 * plus + 1:
# 数据库数据较少 都没有11页
start_page = 1
end_page = total_page_count
else:
# 数据库数据较多
if page <= plus:
# 不能有负值
start_page = 1
end_page = 2 * plus + 1
else:
# 设置不要超出总页码
if (page + plus) >= total_page_count:
start_page = total_page_count - 2 * plus
end_page = total_page_count
else:
start_page = page - plus
end_page = page + plus
# 页码
page_str_list = []
# 首页
first_page = '首页 '.format(1)
page_str_list.append(first_page)
# 上一页
if page>1:
prev = '上一页 '.format(page-1)
else:
prev = '上一页 '.format(1)
page_str_list.append(prev)
# range()前取后不取 加不加一一定要注意啊!
for i in range(start_page, end_page+1):
if i == page:
ele = '{} '.format(i, i)
else:
ele = '{} '.format(i, i)
page_str_list.append(ele)
# 下一页
if page
要是页面很多,能不能搞一个输入框,是输入一个页码数就跳到输入的页码呢?将page传到前端来,这样只用改前端即可奥!
{% extends 'layout.html' %}
{% block content %}
{{page_string}}
{% endblock %}
现在是实现了,但是有没有发现代码很冗余,所以我们想要把这个代码进行封装一下,相当于一个公共组件,那里想用哪里用!
# -*- coding:utf-8 -*-
# @Author : 雾里看花花里看雾(王晓曼)
# @Time : 2022/2/19 23:26
# @FileName: Pagination.py
# @Software: PyCharm
# @Blog :https://blog.csdn.net/qq_43779149
from django.utils.safestring import mark_safe
import copy
"""
分页组件使用说明:
1、筛选数据
# 拿到搜索的值 如果没有设置为空
data_dict = {}
search_data = request.GET.get("q", "")
if search_data:
data_dict["mobile__contains"] = search_data
data_list = models.PrettyNum.objects.filter(**data_dict)
2、实例化对象
page_object = Pagination(request, data_list)
page_list = page_object.data_list
page_string = page_object.html()
3、处理传参数据
content = {
"page_list": page_list,
"search_data": search_data,
"page_string": page_string,
}
return render(request, "number_list.html", content)
4、前端分页数据
{{page_string}}
"""
class Pagination(object):
def __init__(self, request, data_list, pagesize=2, page_param="page", plus=2):
"""
:param request是请求请求的对象
:param data_list是符合条件的数据
:param pagesize是每页显示多少条数据
:param page_param是url中获取的分页参数
:param plus是显示当前的前后多少页
:return page_string是html分页栏
"""
# 拿到url里面的参数
print(request.GET.urlencode())
query_dict = copy.deepcopy(request.GET)
query_dict.mutable = True
self.query_dict = query_dict
self.page_param = page_param
# query_dict.setlist('page', [3])
# print(query_dict.urlencode())
page = request.GET.get(page_param, "1")
# 如果page是十进制的数
if page.isdecimal():
page = int(page)
else:
page = 1
self.page = page
self.pagesize = pagesize
self.plus = plus
self.start = (page - 1) * pagesize
self.end = page * pagesize
# 注意有的是,有的是:不要搞错了!
self.data_list = data_list[self.start:self.end]
# 数据总条数
total_count = data_list.count()
# 总页码计算 c(商),d(余数) = divmod(a,b)
total_page_count, div = divmod(total_count, pagesize)
if div:
total_page_count += 1
self.total_page_count = total_page_count
def html(self):
# 设置不要出现负值
if self.total_page_count <= 2 * self.plus + 1:
# 数据库数据较少 都没有11页
start_page = 1
end_page = self.total_page_count
else:
# 数据库数据较多
if self.page <= self.plus:
# 不能有负值
start_page = 1
end_page = 2 * self.plus + 1
else:
# 设置不要超出总页码
if (self.page + self.plus) >= self.total_page_count:
start_page = self.total_page_count - 2 * self.plus
end_page = self.total_page_count
else:
start_page = self.page - self.plus
end_page = self.page + self.plus
# 页码
page_str_list = []
# 首页
self.query_dict.setlist(self.page_param, [1])
first_page = '首页 '.format(self.query_dict.urlencode())
page_str_list.append(first_page)
# 上一页
if self.page > 1:
self.query_dict.setlist(self.page_param, [self.page - 1])
prev = '上一页 '.format(self.query_dict.urlencode())
else:
self.query_dict.setlist(self.page_param, [1])
prev = '上一页 '.format(self.query_dict.urlencode())
page_str_list.append(prev)
# range()前取后不取 加不加一一定要注意啊!
for i in range(start_page, end_page + 1):
if i == self.page:
self.query_dict.setlist(self.page_param, [i])
ele = '{} '.format(self.query_dict.urlencode(), i)
else:
self.query_dict.setlist(self.page_param, [i])
ele = '{} '.format(self.query_dict.urlencode(), i)
page_str_list.append(ele)
# 下一页
if self.page < self.total_page_count:
self.query_dict.setlist(self.page_param, [self.page + 1])
next = '下一页 '.format(self.query_dict.urlencode())
else:
self.query_dict.setlist(self.page_param, [self.total_page_count])
next = '下一页 '.format(self.query_dict.urlencode())
page_str_list.append(next)
# 尾页
self.query_dict.setlist(self.page_param, [self.total_page_count])
last_page = '尾页 '.format(self.query_dict.urlencode())
page_str_list.append(last_page)
# 将字符串使用mark_safe()后才能变成html
page_string = mark_safe("".join(page_str_list))
return page_string
怎么使用呢?
def number_list(request):
# 拿到搜索的值 如果没有设置为空
data_dict = {}
search_data = request.GET.get("q", "")
if search_data:
data_dict["mobile__contains"] = search_data
data_list = models.PrettyNum.objects.filter(**data_dict)
page_object = Pagination(request, data_list)
page_list = page_object.data_list
page_string = page_object.html()
content = {
"page_list": page_list,
"search_data": search_data,
"page_string": page_string,
}
return render(request, "number_list.html", content)
{% extends 'layout.html' %}
{% block content %}
{{page_string}}
{% endblock %}
我们每写一个类就要写一个bootstrap样式,这样太累了吧,想想继承?
# -*- coding:utf-8 -*-
# @Author : 雾里看花花里看雾(王晓曼)
# @Time : 2022/2/20 1:02
# @FileName: BootStrap.py
# @Software: PyCharm
# @Blog :https://blog.csdn.net/qq_43779149
"""BootStrap样式类"""
class BootStrapModelForm(object):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 循环ModelForm中所有字段 给每个字段的插件设置
for name, field in self.fields.items():
# 字段中有属性保留原来的属性 没有属性才添加
if field.widget.attrs:
field.widget.attrs["class"] = "form-control"
field.widget.attrs["placeholder"] = field.label
else:
field.widget.attrs = {"class": "form-control", "placeholder": field.label}
这样就将一个BootStrap样式类写好了,需要加样式就继承该类就好啦。
由于当业务需求越来越广时,我们要编辑的视图函数和类会越来越多,如果都放在一个py文件中,就会看着很缭乱,所以我们需要想办法优化一下项目目录。
表结构如下:
class Admin(models.Model):
"""管理员"""
username = models.CharField(verbose_name="用户名", max_length=32)
password = models.CharField(verbose_name="密码", max_length=64)
为了安全起见,一般管理员的密码是不让显示出来的。
现在我们尝试一下,上面写的分页管理,进行简单的应用一下。
我们先来回顾一下分页组件使用说明:
分页组件使用说明:
1、筛选数据
# 拿到搜索的值 如果没有设置为空
data_dict = {}
search_data = request.GET.get("q", "")
if search_data:
data_dict["mobile__contains"] = search_data
data_list = models.PrettyNum.objects.filter(**data_dict)
2、实例化对象
page_object = Pagination(request, data_list)
page_list = page_object.data_list
page_string = page_object.html()
3、处理传参数据
content = {
"page_list": page_list,
"search_data": search_data,
"page_string": page_string,
}
return render(request, "number_list.html", content)
4、前端分页数据
{{page_string}}
具体可以根据使用更改:
def admin_list(request):
data_list = models.Admin.objects.all()
page_object = Pagination(request, data_list)
content = {
"data_list": page_object.data_list,
"page_string": page_object.html()
}
return render(request, "admin_list.html", content)
由于我们前面写的添加页面,基本上都是差不多的,所以这里写一个公共的添加页面。
{% extends 'layout.html' %}
{% block content %}
{{title}}
{% endblock %}
class AdminModelForm(BootStrapModelForm, forms.ModelForm):
class Meta:
model = models.Admin
fields = "__all__"
class AdminModelForm(BootStrapModelForm, forms.ModelForm):
# 增加一个确认密码输入框 并且设置密码不可见
confirm_password = forms.CharField(
label="确认密码",
# render_value=True用于密码不一致的时候 不清空密码
widget=forms.PasswordInput(render_value=True)
)
class Meta:
model = models.Admin
fields = ["username", "password", "confirm_password"]
widgets = {
# 对于密码也设置不可见
"password": forms.PasswordInput(render_value=True)
}
# 密码md5加密
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("密码不一致")
# 放在cleaned_data 到时候save到数据库
return confirm
同时还希望进行md5加密。
import hashlib
from employeesystem.settings import SECRET_KEY
def md5(data_string):
"""md5加密"""
obj = hashlib.md5(SECRET_KEY.encode("utf-8"))
obj.update(data_string.encode("utf-8"))
return obj.hexdigest()
可以看出:
def admin_add(request):
if request.method == "GET":
form = AdminModelForm()
content = {
"form": form,
"title": "新建管理员"
}
return render(request, "common.html", content)
form = AdminModelForm(data=request.POST)
if form.is_valid():
# 如果数据合法 保存到数据库
print(form.cleaned_data)
form.save()
return redirect("/admin/list/")
else:
print(form.errors)
return render(request, "common.html", {"form": form, "title": "新建管理员"})
这里我们设置只允许修改用户名。
class AdminEditModelForm(BootStrapModelForm, forms.ModelForm):
class Meta:
model = models.Admin
fields = ["username"]
def admin_edit(request, nid):
# 对象/None
row_object = models.Admin.objects.filter(id=nid).first()
if not row_object:
return render(request, "error.html", {"msg": "数据不存在"})
if request.method == "GET":
form = AdminEditModelForm(instance=row_object)
return render(request, "common.html", {"form": form, "title": "编辑管理员"})
# 用Post提交的数据 进行数据校验
form = AdminEditModelForm(data=request.POST, instance=row_object)
if form.is_valid():
# 如果数据合法 保存到数据库
print(form.cleaned_data)
# 默认保存的是用户输入的所有数据 如果想要再保存用户输入的额外的值
# form.instance.字段名=值
form.save()
return redirect("/admin/list/")
else:
print(form.errors)
return render(request, "common.html", {"form": form, "title": "编辑管理员"})
class AdminResetModelForm(BootStrapModelForm, forms.ModelForm):
# 增加一个确认密码输入框 并且设置密码不可见
confirm_password = forms.CharField(
label="确认密码",
widget=forms.PasswordInput()
)
class Meta:
model = models.Admin
fields = ["password", "confirm_password"]
widgets = {
# 对于密码也设置不可见
"password": forms.PasswordInput()
}
# 密码md5加密
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("密码不一致")
# 放在cleaned_data 到时候save到数据库
return confirm
def admin_reset(request, nid):
# 对象/None
row_object = models.Admin.objects.filter(id=nid).first()
if not row_object:
return render(request, "error.html", {"msg": "数据不存在"})
if request.method == "GET":
form = AdminResetModelForm(instance=row_object)
return render(request, "common.html", {"form": form, "title": "重置密码——{}".format(row_object.username)})
# 用Post提交的数据 进行数据校验
form = AdminResetModelForm(data=request.POST, instance=row_object)
if form.is_valid():
# 如果数据合法 保存到数据库
print(form.cleaned_data)
# 默认保存的是用户输入的所有数据 如果想要再保存用户输入的额外的值
# form.instance.字段名=值
form.save()
return redirect("/admin/list/")
else:
print(form.errors)
return render(request, "common.html", {"form": form, "title": "重置密码——{}".format(row_object.username)})
# 使用form来做的
class LoginForm(BootStrapModelForm, forms.Form):
username = forms.CharField(label="用户名", widget=forms.TextInput)
password = forms.CharField(label="密码", widget=forms.PasswordInput)
# 密码md5加密
def clean_password(self):
pwd = self.cleaned_data.get("password")
return md5(pwd)
def login(request):
if request.method == "GET":
form = LoginForm()
return render(request, "login.html", {"form": form})
form = LoginForm(data=request.POST)
if form.is_valid():
# form.cleaned_data拿到的数据 是一个字典
admin_object = models.Admin.objects.filter(**form.cleaned_data).first()
if not admin_object:
# 显示错误信息
form.add_error("username", "用户名或者密码错误")
return render(request, "login.html", {"form": form})
# 网站生成随机字符串:写到用户浏览器的cookie中 再写入到session中
request.session["info"] = admin_object.username
# 用户名 密码正确 登录成功
return redirect("/admin/list/")
# 空的时候的处理
return render(request, "login.html", {"form": form})
相应的页面和样式如下:
{% load static %}
Login
Login
body{
/* 设置背景图片url */
background-image:url(../img/flowers.jpg);
/* 设置背景图片全屏 */
background-size:100%;
}
.container{
width:300px;
margin-top:150px;
}
.panel-title{
text-align:center;
}
.btn-primary{
width:70px;
height:35px;
margin-left:80px;
margin-top:10px;
}
同样有一点,那就是如果你不登陆,那么相应的什么列表是不会让访问的,所以对于每一个函数,都要加上这样的验证信息。
注意是在app对应目录下新建一个文件夹middleware,然后再创建一个文件,编辑一个中间件(也就是一个类)后,然后要去settings.py下注册该中间件!
如果在process_request中,没有返回值,那就是可以继续走下去,如果有返回值,那就回来了。
中间件执行的顺序,是按照注册的顺序来的。
那么问题来啦,怎么使用中间件实现登录验证呢?
class AuthMiddleware(MiddlewareMixin):
def process_request(self, request):
# 排除那些不需要登录就可以访问的页面
# request.path_info表示获取当前用户请求的url
if request.path_info == "/login/":
return
# 1、读取当前访问的用户的session值
info_dict = request.session.get("info")
if info_dict:
return
# 2、没有登录过
return redirect("/login/")
那么怎么清除session呢?
def logout(request):
request.session.clear()
return redirect("/login/")
这样当我们没有登录的时候,是不能访问任何其他页面的。
于是我又把页面大致逻辑改的相对合理一点了!
当登录进去后,这里就是当前登录用户的用户名,当点击注销后就又变成了登录页面。
只有用户名和密码,存在暴力破解!
def login(request):
if request.method == "GET":
form = LoginForm()
return render(request, "login.html", {"form": form})
form = LoginForm(data=request.POST)
if form.is_valid():
# 验证码校验
# 因为要在数据库中搜索 需要使用pop 注意区分session中的字段和cleaned_data中的字段!
user_input_code = form.cleaned_data.pop("code")
# 60秒超时就可能为空 故为了比较设置一个默认空值
code = request.session.get("image_code", "")
if code.upper() != user_input_code.upper():
form.add_error("code", "验证码错误")
return render(request, "login.html", {"form": form})
# 用户名和密码校验
# form.cleaned_data拿到的数据 是一个字典
admin_object = models.Admin.objects.filter(**form.cleaned_data).first()
if not admin_object:
# 显示错误信息
form.add_error("username", "用户名或者密码错误")
return render(request, "login.html", {"form": form})
# 网站生成随机字符串:写到用户浏览器的cookie中 再写入到session中
request.session["info"] = admin_object.username
# 7天免登录 否则60秒失效
request.session.set_expiry(60*60*24*7)
# 用户名 密码正确 登录成功
return redirect("/admin/list/")
# 空的时候的处理
return render(request, "login.html", {"form": form})
生成随机码图片:
def image_code(request):
# 调用pillow函数 生成图片
img, code_string = check_code()
print(code_string)
# 写入到自己的session中 以便于后续获取验证码再进行校验
request.session["image_code"] = code_string
# 给session设置60s超时
request.session.set_expiry(60)
stream = BytesIO()
img.save(stream, 'png')
return HttpResponse(stream.getvalue())
Jquery的Ajax请求!
class Task(models.Model):
"""任务"""
level_choices = (
(1, "紧急"),
(2, "重要"),
(3, "临时"),
)
level = models.SmallIntegerField(verbose_name="级别", choices=level_choices, default=1)
title = models.CharField(verbose_name="标题", max_length=64)
detail = models.TextField(verbose_name="详细信息")
user = models.ForeignKey(verbose_name="负责人", to="Admin", on_delete=models.CASCADE)
def task_list(request):
"""任务列表"""
form = TaskModelForm()
return render(request, "task_list.html", {"form": form})
# ajax使用post请求时需要加上csrf免除
@csrf_exempt
def task_add(request):
data_dict = {"status": True}
return HttpResponse(json.dumps(data_dict))
{% extends 'layout.html' %}
{% block content %}
任务表单
{% endblock %}
{% block js %}
{% endblock %}
$.ajax({
url: 请求的url,
type: 请求的方式,
data: 请求的数据,
dataType: 请求的数据格式,
success: function(成功时返回的结果){
console.log(res);
console.log(res.status);
console.log(res.data);
}
})
如下:
# ajax使用post请求时需要加上csrf免除
@csrf_exempt
def task_add(request):
# 用户发送过来的数据进行校验(ModelForm进行校验)
form = TaskModelForm(data=request.POST)
if form.is_valid():
form.save()
data_dict = {"status": True}
return HttpResponse(json.dumps(data_dict))
data_dict = {"status": False, "error": form.errors}
return HttpResponse(json.dumps(data_dict))