七、进阶知识(二)
7.1 内建用户系统
定义
- Django带有一个用户认证系统。它处理用户账号、组权限及基于cookie的用户会话
- 用户可以直接使用Django自带的用户表
- 官方文档:https://docs.djangoproject.com/en/2.2/topics/auth
基本字段
模型类位置 from django.contrib.auth.models import User
字段 | 含义 |
---|---|
username | 用户名 |
password | 密码 |
邮箱 | |
first_name | 名 |
last_name | 姓 |
is_superuser | 是否是管理员账号(/admin) |
is_staff | 是否可以访问admin管理界面 |
is_active | 是否是活跃用户,默认为True。一般不删除用户,而是将用户的is_active设为False |
last_login | 上一次的登录时间 |
date_joined | 用户创建的时间 |
创建用户
-
创建普通用户create_user
from django.contrib.auth.models import User user = User.objects.create_user(username='用户名',password='密码',email='邮箱',...)
-
创建超级用户create_superuser
from django.contrib.auth.models import User user = User.objects.create_superuser(username='用户名',password='密码',email='邮箱',...)
删除用户
from django.contrib.auth.models import User
try:
user = User.objects.get(username='用户名')
user.is_active = False # 记当前用户无效
user.save()
print("删除普通用户成功!")
except:
print("删除普通用户失败")
校验密码
from django.contrib.auth import authenticate
user = authenticate(username=username,password=password)
说明:如果用户名密码校验成功则返回对应的user对象,否则返回None
修改密码
from django.contrib.auth.models import User
try:
user = User.objects.get(username='xiaonao')
user.set_password('654321')
user.save()
return HttpResponse("修改密码成功!")
except:
return HttpResponse("修改密码失败!")
登陆状态保持
from django.contrib.auth import login
def login_view(request):
user = authenticate(username=username, password=password)
login(request, user)
登陆状态校验
from django.contrib.auth.decorators import login_required
@login_required
def index_view(request):
#该视图必须为用户登录状态下可以访问
#当前登录用户可通过request.user获取
login_user = request.user
...
登陆状态取消
from django.contrib.auth import logout
def logout_view(request):
logout(request)
内建用户表
方案一:通过建立新表,跟内建表做1对1
方案二:继承内建抽象类
步骤:
-
添加新的应用
-
定义模型类继承AbstractUser
-
settings.py中指明AUTH_USER_MODEL=‘应用名.类名’
注意:此操作要在第一次Migrate之前进行
示例 - user/models.py - 添加user应用
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your model here.
class UserInfo(AbstractUser):
phone = models.CharField(max_length=11, default='')
settings.py添加配置
AUTH_USER_MODEL = 'user.UserInfo'
添加用户
from user.models import UserInfo
UserInfo.objects.create_user(username='zc', password='123456', phone='13488871101')
7.2 文件上传
- 定义:用户可以通过浏览器将图片等文件传至网站
- 场景:用户上传头像、上传流程性的文档[pdf,txt等]
上传规范
文件上传必须为POST提交方式
表单<form>
中文件上传时必须带有enctype="multipart/form-data"时才会包含文件内容数据
表单中用<input type=“file” name=“xxx”>标签上传文件
视图函数中,用request.FILES取文件框的内容
file = request.FILES[‘xxx’]
说明:
- FILES的key对应页面中file框的name值
- file绑定文件流对象
- file.name 文件名
- file.file 文件的字节流数据
配置文件的访问路径和存储路径
在settings.py中设置MEDIA相关配置;Django把用户上传的文件,统称为media资源
Django把用户上传的文件,统称为media资源
# file: settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL和MEDIA_ROOT需要手动绑定
步骤:主路由中添加路由
from django.conf import settings
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
说明:等价于做了MEDIA_URL开头的路由,Django接到该特征请求后去MEDIA_ROOT路径查找资源
文件写入方案1:传统的open方式
@csrf_exempt
def upload_view(request):
if request.method == 'GET':
return render(request, 'test_upload.html')
elif request.method == 'POST':
a_file = request.FILES['myfile']
print("上传文件名是:", a_file.name)
filename = os.path.join(settings.MEDIA_ROOT, a_file.name)
with open(filename, 'wb') as f:
data = a_file.file.read()
f.write(data)
return HttpResponse("接收文件:" + a_file.name + "成功")
文件写入方案1:借助ORM
字段:FileField(upload=‘子目录名’)
@csrf_exempt
def upload_view_dj(request):
if request.method == 'GET':
return render(request, 'test_upload.html')
elif request.method == 'POST':
title = request.POST['title']
a_file = request.FILES['myfile']
Content.objects.create(desc=title,myfile=a_file)
return HttpResponse('----upload is ok-----')
7.3 django发送邮件
业务场景
- 业务告警
- 邮件验证
- 密码找回
相关协议 - SMTP
- SMTP全称是"Simple Mail Transfer Protocol",即简单邮件传输协议(25号端口)
- 它是一组用于从源地址到目的地址传输邮件的规范,通过它来控制邮件的中转
- 属于推送协议
相关协议 - IMAP
- IMAP全称是Internet Mail Access Protocol,即交互式邮件访问协议,是一个应用层协议
- 用来从本地邮件客户端(Outlook Express、Foxmail、Mozilla Thunderbird等)访问远程服务器上的邮件
- 属于“拉取”协议
相关协议 - POP3
- POP3是Post Office Protocol 3的简称,即邮局协议的第3个版本,是TCP/IP协议族中的一员(默认端口为110)
- 本协议主要用于支持使用客户端远程管理在服务器上的电子邮件
- 属于“拉取”协议
IMAP VS POP3
两者均为“拉取”型协议,负责从邮件服务器中下载邮件
- IMAP具备摘要浏览功能,可预览部分摘要,再下载整个邮件
- IMAP为双向协议,客户端操作可反馈给服务器
- POP3必须下载全部邮件,无摘要功能
- POP3为单向协议,客户端操作无法同步服务器
Django发邮件
- Django中配置邮件功能,主要为SMTP协议,负责发邮件
- 原理
- 给Django授权一个有效
- Django用该邮箱给对应收件人发送邮件
- django.core.mail封装了电子邮件的自动发送SMTP协议
授权步骤
- 申请QQ号
- 用QQ号登录QQ邮箱并修改设置
- 用申请到的QQ号和密码登陆到https://mail.qq.com/
- 修改
QQ邮箱->设置->账户->"POP3/IMAP"......服务
Django配置
EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST='smtp.qq.com' # 腾讯QQ邮箱SMTP服务器地址
EMAIL_PORT=25 # SMTP服务的端口号
EMAIL_HOST_USER='xxxx@qq.com' # 发送邮件的QQ邮箱
EMAIL_HOST_PASSWORD='******' # 在QQ邮箱->设置->账户->"POP3/IMAP......服务"里得到的在第三方登录QQ邮箱授权码
EMAIL_USE_TLS=False # 与SMTP服务器通信时,是否启动TLS链接(安全链接)默认False
函数调用
from django.core import mail
mail.send_mail(
subject, #题目
message, #消息内容
from_email, #发送者[当前配置邮箱]
recipient_list=['xxxqq.com'] #接受者邮箱列表
)
练习 - 邮件告警
用中间件实现抓取视图函数的异常,并以邮件的形式将异常信息发送给指定联系人
邮件主体:‘mysite7异常告警’
内容:自定义即可,要求带有异常信息
收件人要求可灵活配置
7.4 项目部署-uwsgi
基础概念
项目部署是指在软件开发完毕后,将开发机器上运行的软件实际安装到服务器上进行长期运行
-
在安装机器上安装和配置同版本的环境[py,数据库等]
-
django项目迁移
sudo scp /home/zc/django/mysite1 root@88.77.66.55:/home/root/xxx 请输入root密码:
-
用uWSGI替代python3 manage.py runserver方法启动服务器
-
配置nginx反向代理服务器
-
用nginx配置静态文件路径,解决静态文件路径问题
uWSGI网关接口配置
WSGI(Web Server Gateway Interface)Web服务器网关接口,是Python应用程序或框架和Web服务器之间的一种接口,被广泛使用。
使用python manage.py runserver通常只在开发和测试环境中使用。
当开发结束后,完善的项目代码需要在一个高效稳定的环境中运行,这时可以使用WSGI
uWSGI定义
uWSGI是WSGI的一种,它实现了http协议、WSGI协议以及uwsgi协议
uWSGI功能完善,支持协议众多,在python web圈热度极高
uWSGI主要以学习配置为主
uWSGI安装
Ubuntu执行sudo pip3 install uwsgi==2.0.18
https://pypi.tuna.tsinghua.edu.cn/simple
检查是否安装成功
- sudo pip3 freeze|grep -i ‘uwsgi’
- 如果成功安装,则会输出uWSGI=2.0.18
配置uWSGI
添加配置文件 项目同名文件夹/uwsgi.ini
如:mysite1/mysite1/uwsgi.ini
文件以**[uwsgi]**开头,有如下配置项:
套接字方式的IP地址:端口号【此模式需要有nginx】
socket=127.0.0.1:8000
Http通信方式的IP地址:端口号
http=127.0.0.1:8000
项目当前工作目录
chdir=/home/tarena/…/my_project
项目中wsgi.py文件的目录,相对于当前工作目录
wsgi-file=my_project/wsgi.py
进程个数
process=4
每个进程的线程个数
threads=2
服务的pid记录文件
pidfile=uwsgi.pid
服务的日志文件位置
daemonize=uwsgi.log
开启主进程管理模式
master=true
特殊说明:Django的settings.py需要做如下配置
- 修改settings.py将DEBUG=True改为DEBUG=False
- 修改settings.py将ALLOWED_HOSTS=[]改为ALLOWED_HOSTS=[‘网站域名’]或者[‘服务监听的ip地址’]
启动uwsgi
cd到uWSGI配置文件所在目录
uwsgi --ini uwsgi.ini
停止uwsgi
cd到uWSGI配置文件所在目录
uwsgi --stop uwsgi.pid
uWSGI的运行说明
- 无论是启动还是关闭,都需要执行ps aux|grep 'uwsgi’确认是否符合预期
- 启动成功后,进程在后台执行,所有日志均输出在配置文件所在目录的uwsgi.log中
- Django中代码有任何修改,需要重新启动uwsgi
uWSGI测试
在浏览器端输入http://127.0.0.1:8000/url进行测试
注意,此时端口8000被uWSGI进程监听,并非runserver
如果当前有预期返回,则uWSGI启动成功
uWSGI常见问题汇总
-
启动失败:端口被占用
原因:有其他进程占用uWSGI启动的端口
解决方案:可执行sudo lsof -i:端口号查询出具体进程;杀掉进程后,重新启动uWSGI即可
-
停止失败:stop无法关闭uWSGI
原因:重复启动uWSGI,导致pid文件中的进程号失准
解决方案:ps出uWSGI进程,手动kill掉
7.5 项目部署-nginx
什么是Nginx
- Nginx是轻量级的高性能Web服务器,提供了诸如HTTP代理和反向代理、负载均衡等一系列重要特性
- C语言编写,执行效率高
- nginx作用
- 负载均衡,多台服务器轮流处理请求
- 反向代理
- 原理
- 客户端请求nginx,再由nginx将请求转发uWSGI运行的django
安装
sudo apt install nginx
如果下载速度很慢,考虑更换为国内源
vim /etc/apt/sources.list
# 更换国内源
sudo apt-get update
安装完毕后,ubuntu终端中输入nginx -v显示如下:
nginx version: nginx/1.14.0(Ubuntu)
配置
修改nginx的配置文件/etc/nginx/sites/enabled/default
sudo vim 该文件
# 在server节点下添加新的location项,指向uwsgi的ip与端口
server {
...
location / {
uwsgi_pass 127.0.0.1:8000; #重定向到127.0.0.1的8000端口
include /etc/nginx/uwsgi_params; #将所有的参数转到uwsgi下
}
}
启动/停止
$ sudo /etc/init.d/nginx start|stop|restart|status
#或
$ sudo service nginx start|stop|restart|status
启动 - sudo /etc/init.d/nginx start
停止 - sudo /etc/init.d/nginx stop
重启 - sudo /etc/init.d/nginx restart
注意:nginx配置只要修改,就需要进行重启,否则配置不生效
修改uWSGI配置
说明nginx负责接收请求,并把请求转发给后面的uWSGI
此模式下,uWSGI需要以socket模式启动
样例:
[uwsgi]
# 去掉如下
# http=127.0.0.1:8000
# 改为
socket=127.0.0.1:8000
uWSGI配置变化后,也需重启
进入到 项目同名文件夹下
$ sudo uwsgi --stop uwsgi.pid
$ sudo uwsgi --ini uwsgi.ini
常见问题排查
宗旨:看日志!
nginx日志位置:
异常信息 /var/log/nginx/error.log
正常访问信息 /var/log/nginx/access.log
uwsgi日志位置:
项目同名目录下,uwsgi.log
1, 访问127.0.0.1:80地址,502响应
502响应代表nginx反向代理配置成功,但是对应的uWSGI未启动
2, 访问127.0.0.1:80/url 404响应
1) 路由的确不在django配置中
2) nginx配置错误,未禁止掉try_files
nginx静态文件配置
-
创建新路径-主要存放Django所有静态文件,如:/home/tarena/项目名_static/
-
在Django settings.py中添加新配置
STATIC_ROOT = '/home/tarena/项目名_static/static' #注意 此配置路径为 存放所有正式环境中需要的静态文件
-
进入项目,执行python3 manage.py collectstatic命令后,Django将项目中所有静态文件复制到STATIC_ROOT中,包括Django内建的静态文件
Nginx配置中添加新配置
# file: /etc/nginx/sites-enabled/default
# 新添加location /static路由配置,重定向到指定的第一步创建的路径即可
server {
...
location /static {
# root 第一步创建文件夹的绝对路径,如:
root /home/tarena/项目名_static;
}
...
}
404/550页面
在模板文件夹内添加404.html模板,当视图出发Http404异常时将会被显示
404.html仅在发布版中(即settings.py中的DEBUG=False时)才起作用
当响应处理函数触发Http404异常时就会跳转到404界面
邮箱告警
当正式服务器上代码运行有报错时,可价格错误追溯信息发至指定邮箱
settings.py中 - 在基础邮箱授权后添加如下配置:
#关闭调试模式
DEBUG = False
#错误报告接收方
ADMINS = [('guoxiaonao', 'xxxx@example.com'),('wanglaoshi', 'xxxx@example.com')]
#发送错误报告方,默认为root@localhost账户,多数邮件服务器会拒绝
SERVER_EMAIL = 'email配置中的邮箱'
报错邮件中会显示一些错误追踪,这些错误追踪中会出现如password等敏感信息,Django已经将配置文件中的敏感信息过滤修改为多个星号,但是用户自定义的视图函数需要用户手动过滤敏感信息
可过滤如下信息:
局部变量
POST提交数据
from django.views.decorators.debug import sensitive_variables
@sensitive_variables('user', 'pw', 'cc')
def process_info(user):
pw = user.pass_word
cc = user.credit_card_number
name = user.name
...
说明:
- 若报错邮件中牵扯到user,pw,cc等局部变量的值时,则会将其替换成*****,而name变量显示其真实值
- 多个装饰器时,需要将其放在最顶部
- 若不传参数,则过滤所有局部变量的值
from django.views.decorators.debug import sensitive_post_parameters
@sensitive_post_parameters('password', 'username')
def index(request):
s = request.POST['username'] + request.POST['abcd']
#'abcd'并不存在,此时引发error
#POST中username及password的值会被替换为******