高级应用三
高级应用
一、Admin后台管理
1、概述
什么是admin?
admin是后台可视化界面管理系统 方便模型的操作与维护
2、配置
-
settings.py
INSTALLED_APPS = [
'django.contrib.admin',
]
汉化
LANGUAGE_CODE = 'zh-Hans'TIME_ZONE = 'Asia/Shanghai'
-
创建用户进行访问
python manage.py createsuperuser
依次输入用户名 邮箱 密码 -
路由访问
127.0.0.1:8000/admin/
3、配置后台
班级与学生模型类
-
models.py
from django.db import models# 创建班级模型 class Grade(models.Model): gname = models.CharField(max_length=10, default='python36') gnum = models.SmallIntegerField(default=30) gboynum = models.SmallIntegerField(default=15) ggirlnum = models.SmallIntegerField(default=15) def __str__(self): return self.gname class Meta: db_table = 'grade' # 创建学生模型 class Student(models.Model): sname = models.CharField(max_length=10, default='张三') sage = models.SmallIntegerField(default=18) ssex = models.BooleanField(default=True) # 一对多外键关系 从表随着主表而删除 # sgrade = models.ForeignKey(Grade, on_delete=models.CASCADE) # sgrade = models.ForeignKey(Grade, on_delete=models.SET_NULL, null=True) # sgrade = models.ForeignKey(Grade, on_delete=models.PROTECT) sgrade = models.ForeignKey(Grade, on_delete=models.SET_DEFAULT, default=1) def __str__(self): return self.sname class Meta: db_table = 'student'
-
设置为中文显示(字段在后台进行中文显示)
class Grade(models.Model):
...
ggirlnum = models.SmallIntegerField(default=15, verbose_name='女生人数')
class Meta:
db_table = 'grade'
verbose_name = '班级'
class Student(models.Model):
... -
admin.py
from django.contrib import admin
from App.models import Grade, Student# class StudentInfo(admin.StackedInline): # 竖着 class StudentInfo(admin.TabularInline): # 横着 model = Student extra = 2 @admin.register(Grade) class GradeAdmin(admin.ModelAdmin): inlines = [StudentInfo] # 字段显示 list_display = ['pk', 'gname', 'gnum', 'gboynum', 'ggirlnum'] # 过滤字段 list_filter = ['gname'] # 搜索字段 search_fields = ['gname'] # 分页 list_per_page = 5 # 每页显示五条数据 # 更改添加修改样式 # fields = ['gname', 'gnum', 'gboynum', 'ggirlnum'] # 添加修改分组(fieldsets和fields不能同时存在) fieldsets = [ ('组一', {'fields':['gboynum', 'ggirlnum']}), ('组二', {'fields':['gname', 'gnum']}) ] @admin.register(Student) class StudentAdmin(admin.ModelAdmin): def show_sex(self): if self.ssex: return '男' else: return '女' show_sex.short_description = '性别' list_display = ['pk', 'sname', 'sage', show_sex, 'sgrade'] # Register your models here. # admin.site.register(Grade, GradeAdmin) # admin.site.register(Student, StudentAdmin)
二、邮件发送
https://docs.djangoproject.com/zh-hans/3.2/topics/email/#django.core.mail.send_mass_mail
1、配置
基础阶段里面的邮箱配置
2、发送邮件
def test_send_mail(req):
from django.core.mail import send_mail
send_mail(
'主体',
'邮件内容',
settings.EMAIL_HOST_USER,
['793390457@qq.com'],
fail_silently=False,
)
return HttpResponse('发送邮件')
3、群发
from django.core.mail import send_mass_mail
message1 = ('Subject here', 'Here is the message', 'from@example.com', ['first@example.com', 'other@example.com'])
message2 = ('Another Subject', 'Here is another message', 'from@example.com', ['second@test.com'])
send_mass_mail((message1, message2), fail_silently=False)
send_mass_mail()和send_mail()之间的主要区别在于,send_mail()每次执行时都会打开到邮件服务器的连接,而send_mass_mail()对其所有邮件使用单个连接。这使得send_mass_mail()的效率略微提高。
4、使用文本和html发送
from django.core.mail import EmailMultiAlternatives
subject, from_email, to = 'hello', 'from@example.com', 'to@example.com'
text_content = 'This is an important message.'
html_content = '<p>This is an <strong>important</strong> message.</p>'
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
msg.attach_alternative(html_content, "text/html")
msg.send()
5、使用html发送
from django.core.mail import send_mass_mail, EmailMessage
msg = EmailMessage('主体', '<b>邮件内容</b>', settings.EMAIL_HOST_USER, ['793390457@qq.com','948807313@qq.com'])
msg.content_subtype = "html" # Main content is now text/html
msg.send()
三、分页
1、Paginator对象
- 创建对象
Paginator() - 属性
count 对象总数
num_pages 页面总数
page_range 页码列表 - 方法
page(num)
2、page对象
通过Paginator的方法page 得到page对象
- 属性
object_list 当前页上所有数据
number 当前页的页码值
paginator 获取paginator 对象 - 方法
has_next 是否有下一页
has_previous 是否有上一页
next_page_num 返回下一页页码
previous_page_num 返回上一页页码
3、实例
-
views.py
def index(req):
nowPage = req.GET.get('page',1)
stu = Student.objects.all()
p = Paginator(stu, 2)
# print(p)# 通过页码获取当前页的对象 try: # 判断页码是否合法 if int(nowPage) >= int(p.num_pages): nowPage = p.num_pages page = p.page(nowPage) except: page = p.page(p.num_pages) return render(req, 'index.html', {'page': page})
-
index.html
Title
首页<ol> {% for s in page.object_list %} <li>{{ s.sname }}</li> {% endfor %} </ol> <nav aria-label="Page navigation"> <ul class="pagination pagination-lg"> <li> <a {% if page.has_previous %}href="{% url 'App:index' %}?page={{ page.previous_page_number }}"{% endif %} aria-label="Previous"> <span aria-hidden="true">«</span> </a> </li> {% for index in page.paginator.page_range %} <li {% if index == page.number %}class="active"{% endif %}><a href="{% url 'App:index' %}?page={{ index }}">{{ index }}</a></li> {% endfor %} {# <li><a href="#">2</a></li>#} <li> <a {% if page.has_next %}href="{% url 'App:index' %}?page={{ page.next_page_number }}"{% endif %} aria-label="Next"> <span aria-hidden="true">»</span> </a> </li> </ul> </nav> </body> </html>
四、中间件
1、说明
中间件就是在请求与响应过程中 进行安全验证的功能 每个中间件组件都会负责做一些特定的功能
2、本质
python类
3、使用场景
- 白名单、黑名单
- url访问过滤
- 缓存
浏览器->中间件->url路由地址分发->中间件->视图函数->中间件->响应->
4、方法
- process_request(self, req)
在执行视图之前被调用(分配url匹配视图) 每个请求都会被调用 返回None或者response对象 - process_view(self, req, view_func, view_args, view_kwargs)
调用视图之前进行调用 每个表请求都会调用 返回None或者response对象 - process_response(self, req, response)
所有响应在返回浏览器之前都会被调用 返回response - process_exception(self, req, exception)
当视图抛出异常时进行调用
5、自定义中间件
-
App同级创建middleware文件夹
App/
middleware/
myMiddly.py -
myMiddly.py中创建MyMiddle类
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirectclass MyMiddle(MiddlewareMixin): def process_request(self, req): print(req.path) print(req.method) print(req.META['REMOTE_ADDR']) # if req.META['REMOTE_ADDR'] == '127.0.0.1': # return HttpResponse('您被拦截了') def process_exception(self, req, exception): print(exception) return redirect('/a/')
-
settings.py
middleware中添加
'middleware.myMiddle.MyMiddle',
五、用户认证
1、说明
auth_user 是用来维护用户信息的关系模式
2、auth_user
CREATE TABLE `auth_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`password` varchar(128) NOT NULL,
`last_login` datetime(6) DEFAULT NULL,
`is_superuser` tinyint(1) NOT NULL,
`username` varchar(150) NOT NULL,
`first_name` varchar(150) NOT NULL,
`last_name` varchar(150) NOT NULL,
`email` varchar(254) NOT NULL,
`is_staff` tinyint(1) NOT NULL,
`is_active` tinyint(1) NOT NULL,
`date_joined` datetime(6) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
3、用户模型属性方法
- is_staff
boolean 用户是否可以访问后台界面 默认False - is_active
boolean 用户是否活跃 是否当前删除用户 默认True - is_authenticated()
用户是否通过验证 (是否登录)
request.user.is_authenticated() - make_password()
密码加密 django自带的hash加密 - check_password()
验证密码是否正确 - set_password()
修改密码 - authenticated()
验证用户名和密码是否正确
authenticated(username=username, password=password) 结果bool - create_user() 创建用户
User.objects.create_user(username, email, password) - request.user
获取当前用户登录对象 - login_required
设置当前视图只能登录后进行访问 - login()
处理登录
login(req, user) - logout()
退出登录
logout(req)
4、注册
-
register.html
注册页面
{{ msg }}
-
views.py
# 注册
def register(req):
message = ''
if req.method == 'POST':
username = req.POST.get('username')
email = req.POST.get('email')
userpass = req.POST.get('userpass')
u = User.objects.filter(username=username).exists()
if not u:
User.objects.create_user(username=username, email=email, password=userpass)
return redirect('/login/')
else:
message = '该用户以存在'
return render(req, 'register.html', {'msg': message})
自己完善python正则验证用户名 密码 邮箱
完善正则验证
5、登录
-
views.py
from django.contrib.auth import authenticate, login# 登录 def user_login(req): if req.method == 'POST': username = req.POST.get('username') userpass = req.POST.get('userpass') u = authenticate(username=username, password=userpass) if u: login(req, u) return redirect('/') else: return redirect('/login/') return render(req, 'login.html')
-
login.html
登录页面
{% csrf_token %}
*(为必填)
<div class="menu">密码:</div> <div class="kong"> <input id="text2" type="password" name="userpass" onblur="check()"> <span id="div2" class="tian" style="margin-top: 5px">*(为必填)</span> </div> {% comment %} <div class="menu">确认密码:</div> <div class="kong"> <input id="text3" type="password" name="01" onblur="check()"> <span id="div3" class="tian" style="margin-top: 5px">*(为必填)</span> </div> <div class="menu">邮箱地址:</div> <div class="kong"> <input id="text4" type="text" name="email" onblur="check()" required> <span id="div4" class="tian" style="margin-top: 5px">*(为必填)</span> </div> {% endcomment %} </div> <div class="can"> <input id="i111" type="submit" name="002" value="注 册"> <p style="width: 200px;display: inline-block;"></p> <input id="i222" type="button" name="004" value="取 消"> </div> </form> <script type="text/javascript"> //刷新or取消 document.getElementById('i77').onclick = function(){ location.reload(); } document.getElementById('i222').onclick = function(){ location.reload(); } //用户名验证 function checkname(){ var div = document.getElementById("div1"); div.innerHTML = ""; var name1 = document.tijiao.text1.value; if (name1 == "") { div.innerHTML = "用户名不能为空!"; document.tijiao.text1.focus(); return false; } if (name1.length < 4 || name1.length > 16) { div.innerHTML = "长度4-16个字符"; document.tijiao.text1.select(); return false; } var charname1 = name1.toLowerCase(); for (var i = 0; i < name1.length; i++) { var charname = charname1.charAt(i); if (!(charname >= 0 && charname <= 9) && (!(charname >= 'a' && charname <= 'z')) && (charname != '_')) { div.innerHTML = "用户名包含非法字符"; document.form1.text1.select(); return false; } } return true; } //密码验证 function checkpassword(){ var div = document.getElementById("div2"); div.innerHTML = ""; var password = document.tijiao.text2.value; if (password == "") { div.innerHTML = "密码不能为空"; {#document.tijao.text2.focus();#} return false; } if (password.length < 4 || password.length > 16) { div.innerHTML = "密码长度为4-16位"; document.tijiao.text2.select(); return false; } return true; } function checkrepassword(){ var div = document.getElementById("div3"); div.innerHTML = ""; var password = document.tijiao.text2.value; var repass = document.tijiao.text3.value; if (repass == "") { div.innerHTML = "密码不能为空"; document.tijiao.text3.focus(); return false; } if (password != repass) { div.innerHTML = "密码不一致"; document.tijiao.text3.select(); return false; } return true; } //邮箱验证 function checkEmail(){ var div = document.getElementById("div4"); div.innerHTML = ""; var email = document.tijiao.text4.value; var sw = email.indexOf("@", 0); var sw1 = email.indexOf(".", 0); var tt = sw1 - sw; if (email.length == 0) { div.innerHTML = "邮箱不能为空"; document.tijiao.text5.focus(); return false; } if (email.indexOf("@", 0) == -1) { div.innerHTML = "必须包含@符号"; document.tijiao.text5.select(); return false; } if (email.indexOf(".", 0) == -1) { div.innerHTML = "必须包含.符号"; document.tijiao.text5.select(); return false; } if (tt == 1) { div.innerHTML = "@和.不能一起"; document.tijiao.text5.select(); return false; } if (sw > sw1) { div.innerHTML = "@符号必须在.之前"; document.tijiao.text5.select(); return false; } else { return true; } return ture; } function check(){ if (checkname() && checkpassword() && checkrepassword() && checkEmail()) { return true; } else { return false; } } </script> </body> </html>
-
index.html
Title
首页
{% if request.user.is_authenticated %}
欢迎:{{ request.user.username }}|退出登录
{% else %}
登录|注册
{% endif %}
6、退出登录
-
views.py
from django.contrib.auth import logout# 退出登录 def user_logout(req): logout(req) return redirect('/')
7、修改密码
@login_required(login_url='/login/')
def update_password(req):
if req.method == 'POST':
old_userpass = req.POST.get('old_userpass')
new_userpass = req.POST.get('new_userpass')
username = req.user.username
u = authenticate(username=username, password=old_userpass)
if u:
# 修改密码
u.set_password(new_userpass)
u.save()
return redirect('/logout/')
else:
return redirect('/update_password/')
return render(req, 'update_password.html')
配置全局login_url
settings.py
LOGIN_URL = '/login/'
views.py
@login_required
def update_password(req):
return redirect('/update_password/')
8、配置优美前端页面
安装扩展库:
- pip install django-bootstrap3
bootstrap官网:https://www.bootcss.com/
9、message组件(消息闪现)
- 说明
当某个动作处理完进行消息通知 - 方法
- message.debug
- message.info
- message.success
- message.warning
- message.error
- 导入
from django.contrib import messages - 使用
def index(req):
messages.success(req, 'index的消息')
return render(req, 'index.html')
10、自定义用户表
-
给auth_user表 添加字段
-
models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
# Create your models here.class User(AbstractUser): iphone = models.CharField(max_length=11, default='111111') icon = models.CharField(max_length=20, default='default.jpg') class Meta: db_table = 'user'
-
配置 将模型类设置为自定义的模型
AUTH_USER_MODEL = 'App.User' -
执行迁移
python manage.py makemigrations
python manage.py migrate -
添加自定义认证(可以使用用户名和手机号码都能登录)
App下创建auth.py
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
from App.models import Userclass MyBackend(ModelBackend): def authenticate(self, request, username=None, password=None, **kwargs): try: u = User.objects.get(Q(username=username)|Q(iphone=username)|Q(email=username)) except: return None if u.check_password(password): return u
-
settings.py
AUTHENTICATION_BACKENDS = ['App.auth.MyBackend'] -
views.py
from App.models import User
User.objects.create_user(username=username, email=email, password=userpass, iphone='15611833906')
六、文件上传
1、概述
文件上传到服务器端 存储在request.FILES中
2、注意
- form表单更改enctype
enctype="multipart/form-data" - 文件上传必须使用post方式
- 注意name名称
- 限制文件上传类型
https://blog.csdn.net/u013379933/article/details/77119796
- 多文件上传
multiple
3、配置文件上传路径
- 在settings.py
STATICFILE_DIRS = [os.path.join(BASE_DIR, 'static')] # (前面模板中配置静态资源文件时就进行配置的)
# 文件上传路径
MEDIA_ROOT = os.path.join(BASE_DIR, r'App/static/upload')
4、文件上传的属性和方法
属性
- file.name 文件名称
- file.size 文件大小
方法
- file.read() 读取文件上传的所有数据 当前方法适合于小文件
- file.chunks() 按块返回文件 在循环迭代中 将大块文件拆分成小块写入到文件中
- file.multiple_chunks()
当前方法根据文件大小返回True或False 当fileSize大于2.5M 返回True 否则返回False 大小可调整
5、原始文件上传(单文件上传)
-
views.py
# 原始单文件上传
def upload_one(req):
if req.method == 'POST':
# 获取文件上传对象
file = req.FILES.get('file')
# print(file.name)
# print(file.size)
path = os.path.join(settings.MEDIA_ROOT, file.name)
try:
with open(path, 'wb') as f:
if file.multiple_chunks():
for ff in file.chunks():
f.write(ff)
else:
f.write(file.read())
messages.success(req, '文件上传成功!')
except:
messages.success(req, '文件上传失败!') -
upload.html
{% extends 'common/base.html' %}
{% block title %}
文件上传
{% endblock %}
{% block content %}
文件上传
{% csrf_token %}
{% endblock %}
6、完整上传
-
views.py
# 文件类型限制
def allowed_file(suffix):
return suffix in settings.ALLOWED_FILES# 文件名称随机 uuid def random_name(suffix): u = str(uuid.uuid4()) return u+'.'+suffix # 缩放 pillow def img_zoom(path, perfix='s_', w=200, h=150): img = Image.open(path) img2 = img.resize((w, h)) # 拆分新路径 拼凑新名字 fileList = os.path.split(path) new_path = os.path.join(fileList[0], perfix + fileList[1]) img2.save(new_path) # 完整上传 # 作业: 文件存储路径为/年/月/日/a.jpg def upload_one(req): if req.method == 'POST': # 获取文件上传对象 file = req.FILES.get('file') # print(file.name) # print(file.size) # 后缀获取 suffix = file.name.split('.')[-1] # 类型限定 if not allowed_file(suffix): messages.error(req, '文件类型不允许') return redirect('/upload_one/') # 生成随机文件名 name = random_name(suffix) # 拼凑地址 path = os.path.join(settings.MEDIA_ROOT, name) try: with open(path, 'wb') as f: if file.multiple_chunks(): for ff in file.chunks(): f.write(ff) else: f.write(file.read()) # 处理图片缩放 img_zoom(path) img_zoom(path, 'm_', 400, 200) messages.success(req, '文件上传成功!') except: messages.success(req, '文件上传失败!') return render(req, 'own_center/upload.html')
7、多文件上传
-
views.py
def upload_many(req):
if req.method == 'POST':
file_list = req.FILES.getlist('file')
try:
for file in file_list:
path = os.path.join(settings.MEDIA_ROOT, file.name)
with open(path, 'wb') as fw:
if file.multiple_chunks():
for ff in file.chunks():
fw.write(ff)
else:
fw.write(file.read())
messages.success(req, '文件上传成功!')
except:
messages.success(req, '文件上传失败!')
return render(req, 'own_center/upload_many.html') -
upload_many.html
{% extends 'common/base.html' %}
{% block title %}
文件上传
{% endblock %}
{% block content %}
多文件上传
{% endblock %}
本文来自博客园,作者:寻月隐君,转载请注明原文链接:https://www.cnblogs.com/QiaoPengjun/articles/15948898.html