中间件
概述:一个轻量级、底层的插件,可以介入Django的请求和相应
本质:本质就是一个python类
方法:
__init__ --- 不需要传参,服务器相应的第一个请求的时候自动调用,用于确定是否启用改中间件
process_reuest(self,request) --- 在执行视图之前被调用(分配url匹配视图之前),每个请求都会调用,返回None或者HttpResponse对象
process_view(self,request,view_func,view_args,view_kwargs) --- 调用视图之前执行,每个请求都会调用,返回None或者HttpResponse对象
perocess_template_response(self,request,reponse) --- 在视图刚好执行完后调用,每个请求都会调用,返回None或者HttpResponse对象
process_response(self,request,response) --- 所有相应返回浏览器之前调用,每个请求都会调用,返回或者HttpResponse对象
process_exception(self,request,exception) --- 当视图出现异常时调用,返回HttpResponse对象
自定义中间件
工程目录下创建Middleware目录,在目录下创建一个python文件
from django.utils.deprecation import MiddlewareMixin
class MyMiddle(MiddlewareMixin):
def process_request(self, request):
print("get参数为:", request.GET.get("a"))
例子:
让用户不再登录状态时,不能直接登录其他页面
创建一个包utils,创建一个自定义中间件的py文件名,例如UserAuthMiddleware.py
from django.utils.deprecation import MiddlewareMixin
class MyMiddle(MiddlewareMixin):
def process_request(self, request):
path = request.path
# 如果地址是登录页面则放过
if path == '/backweb/my_login':
return None
# 如果地址中包含'blog'则放过
# 判断一个字符串是否包含在另外一个字符串中可以使用in和find()
子串 in 字符串 , 字符串.finf(子串)
if 'blog' in path:
return None
# 在sttings中设置没有登录时跳转的地址
# 没有登录,跳转此地址
<!--LOGIN_URL = '/backweb/my_login/'-->
# 获取session,如果没有则说明没有登录,跳转到登录界面
session_id = request.COOKIES.get('session_id')
if not session_id:
return HttpResponseRedirect(reverse('backweb:my_login'))
user =User.objects.filter(session_id=session_id)
if not user:
return HttpResponseRedirect(reverse('backweb:my_login'))
# 可以不用返回或者返回None
return None
在站点中settings.py中的MIDDLEWARE添加中间件:
utils.UserAuthMiddleware.MyMiddle --- 文件名.自定义py文件名.类名
上传图片和其他文件
例子1:
概述:文件上传时,文件数据存储在request.FILES属性中
注意:form表单要上传文件需要添加属性enctype="multipart/form-data"
确定存储路径:
1、在static目录下创建一个目录,用户存储接收上传的文件
2、配置settings.py文件
# 上传文件目录
MDEIA_ROOT=os.path.join(BASE_DIR,r'static\定义的目录名') --- window中路径使用\,Linux中使用/
代码:
<form method="post" action="/savefile/" enctype="multipart/form-data">
{%csrf_token%}
<input type="file" name="file"/>
<input type="submit" value="上传"/>
</form>
import os
from django.conf import settings
def savefile(request):
if request.method == "POST":
f = request.FILES["file"]
# 文件在服务器端的路径
filePath = os.path.join(settings.MDEIA_ROOT, f.name)
with open(filePath, 'wb') as fp:
for info in f.chunks():
fp.write(info)
return HttpResponse("上传成功")
else:
return HttpResponse("上传失败")
f.chunks() --- 文件流接收(分段接收)
例子2:
应用的models中,添加字段
imgae_url = models.ImageField(upload_to='upload',null=True) # 将图片存储在upload文件
需要pip install pillow才能使用
创建和工程目录同一级的media文件夹 --- 需要将其转换成类似static的静态目录
在站点的urls中配置如下:
from django.contrib.staticfiles.urls import static
urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
配置settings
# 媒体的路径 --- 图片最好不要放在static中,单独放在外面
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
页面中
<td class="center"><img src="/media/{{ article.imgae_url }}" height="50" width="50" alt=""></td>
此时,当我们上传图片时,图片将存储在media下的upload的下面,在数据库中的image_url中生成一个upload/图片名的字段值
分页
Paginator对象:
创建对象:
格式 --- Paginator(列表,整数),列表所有的数据,整数是每一页的数据
返回值:返回分页对象
属性:
count --- 对象属性
num_pages --- 页面总数
page_range --- 页码列表,例如[1,2,3,4,5],页码从1开始
方法:
page = paginator(num) --- 获得Page对象,如果提供的页码不存在,会抛出一个异常(分有多少页就有多少个Page对象)
异常:
InvalidPage --- 当向page()传递的是一个无效的页码时抛出
PageNotAnlnteger --- 当向page()传递的不是一个整数时抛出
EmptyPage --- 当向Page()传递一个有效值,但是该页面没有数据时抛出
Page对象:
创建对象:
Pagintor对象的page()方法返回得到Page对象
属性:
object_list --- 当前页面上所有的(对象)列表
number --- 当前页的页码值
paginator --- 获得当前page关联的paginator对象
方法:
has_next() --- 判断是否有下一页,如果有返回Ture
has_previous() --- 判断是否有上一页,如果有返回Ture
has_other() -- 判断是否有上一页或下一页,如果有返回Trre
next_page_number() --- 返回下一页的页码,如果下一页不存在抛出InvalidPage异常
previous_page_number() --- 返回上一页的页码,如果上一页不存在抛出InvalidPage异常
len() --- 返回当前页的数据(对象)个数
代码实例:
url(r'^studentpage/(\d+)/$', views.studentpage),
from .models import Students
from django.core.paginator import Paginator
def studentpage(request, pageid):
# 所有学生列表
allList = Students.objects.all()
paginator = Paginator(allList,6)
page = paginator.page(pageid)
return render(request,'myApp/studentpage.html',{"students":page})
<ul>
{% for stu in students %}
<li>
{{stu.sname}}-{{stu.sgrade}}
</li>
{% endfor %}
</ul>
<ul>
{% for index in students.paginator.page_range %}
{% if index == students.number %}
<li>
{{index}}
</li>
{% else %}
<li>
<a href="/studentpage/{{index}}/">{{index}}</a>
</li>
{% endif %}
{% endfor %}
</ul>
<aside class="paging">
{% if page.has_previous == True %}
<a href="{% url 'backweb:index' %}?page={{ page.previous_page_number}}">上一页</a>
{% endif %}
<a href="{% url 'backweb:index' %}">第一页</a>
{% for i in page.paginator.page_range %}
<a href="{% url 'backweb:index' %}?page={{ i}}">{{ i }}</a>
{% endfor %}
<a href="{% url 'backweb:index' %}?page={{ page.paginator.num_pages }}">最后一页</a>
{% if page.has_next == True %}
<a href="{% url 'backweb:index' %}?page={{ page.next_page_number}}">下一页</a>
{% endif %}
</aside>
Paginator对象和Page对象的关系:
paginator = page.paginator --- 根据page创建paginator对象
page = paginator(num) --- 生成page对象
思考:如果页码则会出现页码显示太长问题,可以考录每次只显示3页(自己定),后台传来的的数据放在中间也,判断如果有上一页就让上一页显示,如果有下一页就让下一页显示。
ajax
需要动态生成,请求JSON数据
代码实例:
html中:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="/static/myApp/js/jquery-3.1.1.min.js"></script>
</head>
<body>
<h1>学生信息列表</h1>
<button id="btn">显示学生信息</button>
<script type="text/javascript" src="/static/myApp/js/sunck.js"></script>
</body>
</html>
js中:
$(document).ready(function (){
document.getElementById("btn").onclick = function(){
$.ajax({
type:"get",
url:"/studentsinfo/",
dataType:"json",
success:function(data, status){
console.log(data)
var d = data["data"]
for(var i = 0; i < d.length;i++){
document.write('<p>'+d[i][0]+'</p>')
}
}
})
}
})
urls中:
url(r'^ajaxstudents/$', views.ajaxstudents),
views中:
def ajaxstudents(request):
return render(request, 'myApp/ajaxstudents.html')
from django.http import JsonResponse
def studentsinfo(request):
stus = Students.objects.all()
list = []
for stu in stus:
list.append([stu.sname, stu.sage])
return JsonResponse({"data":list})
富文本
想要使用需要安装:pip install django-tinymce
使用:
在站点中使用:
配置settings.py文件中INSSTALLED_APPS中添加'tinymce'
# 富文本
TINYMCE_DEFAULT_CONFIG = {
'theme':'advanced', --- 配置富文本的模式
'width':600, ---- 宽
'height':400, ---- 高
}
模型中:
from tinymce.models import HTMLField --- 新增大文本
class Text(models.Model):
str = HTMLField()
admin中:
from .models import Text
admin.site.register(Text)
终端使用:pyhton manage.py createsuperuser创建用户去管理表
在自定义视图中使用:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>富文本</title>
<script type="text/javascript" src="/static/tiny_mce/tiny_mce.js"></script>
<script type="text/javascript">
tinyMCE.init({
'mode':'textareas',
'theme':'advanced',
'width':800,
'height':600,
})
</script>
</head>
<body>
<form action="/saveedit/" method="post">
<textarea name="str">sunck is a good man</textarea>
<input type="submit" value="提交"/>
</form>
</body>
</html>
celery
1、用户发起请求,并且等待reponse返回,但是视图中有一些耗时操作,导致用户可能会等待很长时间才能接受response,这样用户体验很差(例如用户注册时,注册时后,会发一封邮件给你进去验证,但是发邮件这个过程是比较耗时的,如果他和服务器返回数据在同一条线上,就会发生阻塞,需要等待邮件发送完成才能接受到服务器的信息,所以需要让发送邮件的过程在另一条线上执行,让服务器线能先返回数据给用户)
2、如果网站需要每隔一段时间需要同步一次数据,但是http请求是需要出发的,也就是需要刷新网页(例如天气预报)
解决:
1、将耗时操作放在celery中执行
2、使用celery定时执行
celery:
任务task:本质一个python函数,将耗时操作封装成一个函数
队列queue:将要执行的任务放在队列里
工人woeks:负责执行队列中的任务
代理broker:负责调度,在部署环境中使用redis
安装:
pip inststall celery
pip install celert-with-redis
pip install django-celery
配置settings.py
INSTALLED_APPS中添加djcelery
import djcelery
djcelery.setup_loader()#初始化
BROKER_URL='redis://:密码@ip:端口/0' --- 使用第0个库
CELERY_IMPORTS=('应用名.task') ---task写函数的文件名
在应用目录下创建task.py文件
迁移,生成celery需要的数据库表 --- python manage.py migrate
在站点目录下创建celery.py文件并添加:
from __future__ import absolute_import
import os
from celery import Celery
from django.conf import settings
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'whthas_home.settings')
app = Celery('portal')
app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
@app.task(bind=True)
def debug_task(self):
print('Request: {0!r}'.format(self.request))
在站点中的__init__.py文件中添加
from .celery import app as celery_app
将耗时操作写在fask中:
from celery import task
import time
@task
def sunck():
print("sunck is a good man")
time.sleep(5)
print("sunck is a nice man")
在views中引入耗时操作:
from .task import sunck
def celery(request):
sunck.delay()#添加到celery中执行,不会阻塞
return render(request, 'myApp/celery.html')
启动desis
启动服务:pyhton manage.py renserver
启动worker:python manage.py celery worker --loglevel=info