django的高级扩展

中间件

概述:一个轻量级、底层的插件,可以介入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

猜你喜欢

转载自blog.csdn.net/weixin_42750983/article/details/81949830