啟動項目命令
python manage.py runserver
分頁功能封裝到類中去
封裝的類的代碼
"""
自定義的分頁組件,以后如果想要使用這個分頁組件,你需要做:
def pretty_list(request):# 靚號列表data_dict = {}search_data = request.GET.get('q', "")if search_data:data_dict["mobile__contains"] = search_datafrom app01.utils.pagination import Pagination# 根據自己的情況去篩選自己的數據queryset = models.PrettyNum.objects.filter(**data_dict).order_by("-level")# 實例化分頁對象1page_object = Pagination(request, queryset)context = {"queryset": page_object.page_queryset,"search_data": search_data, # 分完頁的數據"page_string": page_object.html() # 頁碼}return render(request, 'pretty_list.html', context)在html頁面中{% for obj in queryset %}{{ obj.xx }}{% endfor %}<ul class="pagination">{{ page_string }}</ul>
"""
from django.utils.safestring import mark_safe
class Pagination(object):def __init__(self, request, queryset, page_size=10, page_param="page", plus=5):from django.http.request import QueryDictimport copyquery_dict = copy.deepcopy(request.GET)query_dict._mutable = Trueself.query_dict = query_dictself.page_param = page_parampage = request.GET.get(page_param, "1")if page.isdecimal():page = int(page)else:page = 1self.page = pageself.page_size = page_sizeself.start = (page - 1) * page_sizeself.end = page * page_sizeself.page_queryset = queryset[self.start:self.end]total_count = queryset.count()# 總頁碼total_page_count, div = divmod(total_count, page_size)if div:total_page_count += 1self.total_page_count = total_page_countself.plus = plusdef html(self):if self.total_page_count <= 2 * self.plus + 1:# 數據庫中的數據比較少,都沒有達到11頁start_page = 1end_page = self.total_page_count + 1else:# 當前頁<5時if self.page <= self.plus:start_page = 1end_page = 2 * self.plus + 1else:if (self.page + self.plus) > self.total_page_count:start_page = self.total_page_count - 2 * self.plusend_page = self.total_page_countelse:# 數據庫中的數據比較多>11頁start_page = self.page - self.plusend_page = self.page + self.plus# 頁碼page_str_list = []self.query_dict.setlist(self.page_param, [1])print(self.query_dict.urlencode())# 上一頁if self.page > 1:self.query_dict.setlist(self.page_param, [self.page - 1])prev = '<li><a href="?{}">上一頁</a></li>'.format(self.query_dict.urlencode())else:self.query_dict.setlist(self.page_param, [1])prev = '<li><a href="?{}">上一頁</a></li>'.format(self.query_dict.urlencode())page_str_list.append(prev)for i in range(start_page, end_page):self.query_dict.setlist(self.page_param, [i])if i == self.page:ele = '<li class="active"><a href="?{}">{}</a></li>'.format(self.query_dict.urlencode(), i)else:ele = '<li><a href="?{}">{}</a></li>'.format(self.query_dict.urlencode(), i)page_str_list.append(ele)page_str_list.append('<li><a href="?{}">首頁</a></li>'.format(self.query_dict.urlencode()))# 下一頁if self.page < self.total_page_count:self.query_dict.setlist(self.page_param, [self.page + 1])prev = '<li><a href="?{}">下一頁</a></li>'.format(self.query_dict.urlencode())else:self.query_dict.setlist(self.page_param, [self.total_page_count])prev = '<li><a href="?{}">下一頁</a></li>'.format(self.query_dict.urlencode())page_str_list.append(prev)self.query_dict.setlist(self.page_param, [self.total_page_count])page_str_list.append('<li><a href="?{}">尾頁</a></li>'.format(self.query_dict.urlencode()))search_string = """<li><form style="float: left;margin-left: -1px" method="get"><input type="text" name="page"style="position: relative;float: left;display: inline-block;width: 80px;border-radius: 0;"class="form-control" placeholder="頁碼"><button style="border-radius: 0" class="btn btn-default" type="submit">跳轉</button></form></li>"""page_str_list.append(search_string)page_string = mark_safe("".join(page_str_list))return page_string
小bug,搜索+分頁情況下
分頁時候保留原來的搜索條件
http://127.0.0.1:8000/pretty/list/?q=888&page=1
在用戶列表頁面使用分頁功能
ModelForm引入時間樣式(自動生成了id為id_create_time)
{% extends 'layout.html' %}{% block css %}<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/bootstrap-datepicker/1.9.0/css/bootstrap-datepicker.min.css">
{% endblock %}{% 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 %}{% block js %}<script src="https://cdn.bootcdn.net/ajax/libs/bootstrap-datepicker/1.9.0/js/bootstrap-datepicker.js"></script><script src="https://cdn.bootcdn.net/ajax/libs/bootstrap-datepicker/1.9.0/locales/bootstrap-datepicker.zh-CN.min.js"></script><script>$(function(){$('#id_create_time').datepicker({format: 'yyyy-mm-dd',startDate: '0',language: "zh-CN",autoclose: true});})</script>
{% endblock %}
引入時間樣式(自定義組件id為dt)
{% extends 'layout.html' %}
{% load static %}{% block css %}<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/bootstrap-datepicker/1.9.0/css/bootstrap-datepicker.min.css">
{% endblock %}{% 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="user"></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="ac"></div><div class="form-group"><label>入職時間</label><input id="dt" type="text" class="form-control" placeholder="入職時間" name="ctime"></div><div class="form-group"><label>性別</label><select class="form-control" name="gd">{% for item in gender_choices %}<option value="{{ item.0 }}">{{ item.1 }}</option>{% endfor %}</select></div><div class="form-group"><label>部門</label><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 %}{% block js %}<script src="https://cdn.bootcdn.net/ajax/libs/bootstrap-datepicker/1.9.0/js/bootstrap-datepicker.js"></script><script src="https://cdn.bootcdn.net/ajax/libs/bootstrap-datepicker/1.9.0/locales/bootstrap-datepicker.zh-CN.min.js"></script><script>$(function(){$('#dt').datepicker({format: 'yyyy-mm-dd',startDate: '0',language: "zh-CN",autoclose: true});})</script>
{% endblock %}
在html中增加屬性的方法1
在html中增加屬性的方法2
在html中增加屬性的方法3
UserEditModelForm類繼承了BootStrapModelForm類的方法,BootStrapModelForm繼承了forms.ModelForm的方法所以使用繼承能夠更好封裝代碼功能
?封裝每部分的功能,拆分到每個模塊中去
運行創建數據庫代碼的命令語句
python manage.py makemigrations
python manage.py migrate
兩種方式實現密碼輸入框
實現密碼不一致不會被清空
密碼加密
實現重置密碼功能
管理員添加,編輯,刪除和重置密碼功能實現
admin.py代碼
from django.core.exceptions import ValidationError
from django.shortcuts import render, redirect
from app01 import models
from app01.utils.pagination import Paginationdef admin_list(request):""" 管理員列表 """data_dict = {}search_data = request.GET.get('q', "")if search_data:data_dict["username__contains"] = search_dataqueryset = models.Admin.objects.filter(**data_dict)page_object = Pagination(request, queryset)context = {"queryset": page_object.page_queryset,"page_string": page_object.html(),"search_data": search_data}return render(request, 'admin_list.html', context)from django import forms
from app01.utils.bootstrap import BootStrapModelForm
from app01.utils.encrypt import md5class AdminModelForm(BootStrapModelForm):confirm_password = forms.CharField(label="確認密碼",widget=forms.PasswordInput(render_value=True))class Meta:model = models.Adminfields = ["username", "password", "confirm_password"]widgets = {"password": forms.PasswordInput(render_value=True)}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 confirm != pwd:raise ValidationError("密碼不一致")return confirmclass AdminEditModelForm(BootStrapModelForm):class Meta:model = models.Adminfields = ['username']class AdminResetModelForm(BootStrapModelForm):confirm_password = forms.CharField(label="確認密碼",widget=forms.PasswordInput(render_value=True))class Meta:model = models.Adminfields = ['password', 'confirm_password']widgets = {"password": forms.PasswordInput(render_value=True)}def clean_password(self):pwd = self.cleaned_data.get("password")md5_pwd = md5(pwd)exists = models.Admin.objects.filter(id=self.instance.pk, password=md5_pwd).exists()if exists:raise ValidationError("密碼不能與之前的密碼相同")return md5_pwddef clean_confirm_password(self):pwd = self.cleaned_data.get("password")confirm = md5(self.cleaned_data.get("confirm_password"))if confirm != pwd:raise ValidationError("密碼不一致")return confirmdef admin_add(request):""" 添加管理員 """title = "新建管理員"if request.method == "GET":form = AdminModelForm()return render(request, 'change.html', {"form": form, "title": title})form = AdminModelForm(data=request.POST)if form.is_valid():form.save()return redirect('/admin/list/')return render(request, 'change.html', {'form': form, "title": title})def admin_edit(request, nid):""" 編輯管理員 """row_object = models.Admin.objects.filter(id=nid).first()if not row_object:# return render(request, 'error.html', {"msg": "數據不存在"})return redirect('/admin/list/')title = "編輯管理員"if request.method == "GET":form = AdminEditModelForm(instance=row_object)return render(request, 'change.html', {"form": form, "title": title})form = AdminEditModelForm(data=request.POST, instance=row_object)if form.is_valid():form.save()return redirect('/admin/list/')return render(request, 'change.html', {"form": form, "title": title})def admin_delete(request, nid):""" 刪除管理員 """models.Admin.objects.filter(id=nid).delete()return redirect('/admin/list/')def admin_reset(request, nid):""" 重置密碼 """row_object = models.Admin.objects.filter(id=nid).first()if not row_object:return redirect('/admin/list/')title = "重置密碼 - {}".format(row_object.username)if request.method == "GET":form = AdminResetModelForm()return render(request, 'change.html', {"form": form, "title": title})form = AdminResetModelForm(data=request.POST, instance=row_object)if form.is_valid():form.save()return redirect('/admin/list/')return render(request, 'change.html', {"form": form, "title": title})
admin_list.html代碼
{% extends 'layout.html' %}{% block content %}
<div class="container"><div style="margin-bottom: 10px" class="clearfix"><a class="btn btn-success" href="/admin/add/" target="_blank"><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" name="q" class="form-control" placeholder="關鍵字"value="{{ search_data }}"><span class="input-group-btn"><button class="btn btn-default" type="submit"><span class="glyphicon glyphicon-search" aria-hidden="true"></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><th>操作</th></tr></thead><tbody>{% for obj in queryset %}<tr><th>{{ obj.id }}</th><td>{{ obj.username }}</td><td>******</td><td><a href="/admin/{{ obj.id }}/reset/">重置密碼</a></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 }}/delete/">刪除</a></td></tr>{% endfor %}</tbody></table></div><div class="clearfix"><ul class="pagination" style="float:left;">{{ page_string }}</ul></div>
</div>
{% endblock %}
change.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 %}
表示字段不能為空
提示用戶名或密碼輸入錯誤
django自動幫我們把session功能定義好了,使用只需要這樣一個語句
登錄成功后:
cookie,隨機字符串
session,用戶信息
在其他需要登錄才能訪問的頁面中,都需要加入:
def index(request):info = request.session.get("info")if not info:return redirect('/login/')
目標:在18個視圖函數中統一加上
info = request.session.get("info")
if not info:return redirect('/login/')
編寫中間件
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirectclass AuthMiddleware(MiddlewareMixin):def process_request(self, request):# 0.排除那些不需要登錄就能訪問的頁面# request.path_info == "/login/"if request.path_info == "/login/":return# 1.讀取當前訪問的用戶的session信息,如果能讀到,說明已登錄過,就可以繼續向后走info_dict = request.session.get("info")if info_dict:return# 2.沒有登錄過,重新回到登錄頁面return redirect('/login/')
應用中間件
MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware','django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware','app01.middleware.auth.AuthMiddleware',
]
從session中取到用戶名
{{ request.session.info.name }}
設置圖像驗證碼
import random
from PIL import Image, ImageDraw, ImageFont, ImageFilterdef check_code(width=120, height=40, char_length=5, font_file='ZixunHappyBold.ttf', font_size=28):code = []img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))draw = ImageDraw.Draw(img, mode='RGB')def rndChar():"""生成隨機字母:return:"""return chr(random.randint(65, 90))def rndColor():"""生成隨機顏色:return:"""return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255))# 寫文字font = ImageFont.truetype(font_file, font_size)for i in range(char_length):char = rndChar()code.append(char)h = random.randint(0, 4)draw.text([i * width / char_length, h], char, font=font, fill=rndColor())# 寫干擾點for i in range(40):draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())# 寫干擾圓圈for i in range(40):draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())x = random.randint(0, width)y = random.randint(0, height)draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor())# 畫干擾線for i in range(5):x1 = random.randint(0, width)y1 = random.randint(0, height)x2 = random.randint(0, width)y2 = random.randint(0, height)draw.line((x1, y1, x2, y2), fill=rndColor())img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)return img, ''.join(code)if __name__ == '__main__':# 1. 直接打開# img,code = check_code()# img.show()# 2. 寫入文件# img,code = check_code()# with open('code.png','wb') as f:# img.save(f,format='png')# 3. 寫入內存(Python3)# from io import BytesIO# stream = BytesIO()# img.save(stream, 'png')# stream.getvalue()# 4. 寫入內存(Python2)# import StringIO# stream = StringIO.StringIO()# img.save(stream, 'png')# stream.getvalue()pass
設置七天免登錄功能
圖片驗證碼account.py
from django import forms
from django.shortcuts import render, redirect, HttpResponse
from app01 import models
from app01.utils.bootstrap import BootStrapForm
from app01.utils.encrypt import md5
from app01.utils.code import check_codeclass LoginForm(BootStrapForm):username = forms.CharField(label="用戶名",widget=forms.TextInput,required=True)password = forms.CharField(label="密碼",widget=forms.PasswordInput(render_value=True),required=True)code = forms.CharField(label="驗證碼",widget=forms.TextInput,required=True)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():# 驗證碼的校驗user_input_code = form.cleaned_data.pop('code')code = request.session.get('image_code', "")if code.upper() != user_input_code.upper():form.add_error("code", "驗證碼錯誤")return render(request, 'login.html', {'form': form})# 驗證成功,獲取到的用戶名和密碼1# 去數據庫校驗用戶名和密碼是否正確,獲取用戶對象admin_object = models.Admin.objects.filter(**form.cleaned_data).first()if not admin_object:form.add_error("password", "用戶名或密碼錯誤!")return render(request, 'login.html', {'form': form})# 用戶名和密碼正確# 網站生成隨機字符串;寫到用戶瀏覽器的cookie中,在寫入到session中;request.session["info"] = {'id': admin_object.id, 'name': admin_object.username}request.session.set_expiry(60 * 60 * 24 * 7)return redirect("/admin/list/")return render(request, 'login.html', {'form': form})from io import BytesIOdef image_code(request):""" 生成圖片驗證碼 """# 調用pillow函數,生成圖片img, code_string = check_code()# 寫入到自己的session中(以便于后續獲取驗證碼再進行校驗)request.session['image_code'] = code_string# 給session設置60s超時request.session.set_expiry(60)stream = BytesIO()img.save(stream, 'png')return HttpResponse(stream.getvalue())def logout(request):""" 注銷 """request.session.clear()return redirect('/login/')
login.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><link rel="stylesheet" href="/static/plugins/bootstrap-3.3.5/css/bootstrap.css"><style>.account {width: 400px;border: 1px solid #dddddd;border-radius: 5px;box-shadow: 5px 5px 20px #aaa;margin-left: auto;margin-right: auto;margin-top: 100px;padding: 20px 40px;}.account h2 {margin-top: 10px;text-align: center;}</style>
</head>
<body>
<div class="account"><h2>用戶登錄</h2><form method="post" novalidate>{% csrf_token %}<div class="form-group"><label>用戶名</label>{{ form.username }}<span style="color: red;">{{ form.username.errors.0 }}</span></div><div class="form-group"><label>密碼</label>{{ form.password }}<span style="color: red;">{{ form.password.errors.0 }}</span></div><div class="form-group"><label for="id_code">圖片驗證碼</label><div class="row"><div class="col-xs-7">{{ form.code }}<span style="color: red;">{{ form.code.errors.0 }}</span></div><div class="col-xs-5"><img id="image_code" src="/image/code/" style="width: 125px;"></div></div></div><input type="submit" value="登 錄" class="btn btn-primary"></form>
</div>
</body>
</html>