17

上传文件

概述

当Django在处理文件上传时,文件的数据被存储在request.FILES属性中

FILES只有在请求的方法为POST且提交的form表单带有enctype="multipart/form-data"属性的情况才会包含数据。否则,FILES将为一个空的类似字典的对象

FILES中的键为input的name属性值

上传界面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件上传</title>
</head>
<body>
    <form method="post" action="/upfile/" enctype="multipart/form-data">
        {% csrf_token %}
        <input type="file" name="file">
        <input type="file" name="file">
        <input type="file" name="pic">
        <input type="submit" value="上传">
    </form>
</body>
</html>

文件存储路径

在static目录下创建名为media的目录

配置路径:

settings

# 文件上传的路径
MEDIA_URL = '/media/'   # 访问资源的地址如 http://127.0.0.1:5000/media/1.jpg
# 写相对路径也可以
MEDIA_ROOT=os.path.join(BASE_DIR,"static/media")

request.FILES数据结构

{
    'file': [
        <InMemoryUploadedFile: a.txt (text/plain)>, 
        <InMemoryUploadedFile: b.txt (text/plain)>
    ], 
    'pic': [
        <InMemoryUploadedFile: c.jpg (image/jpeg)>
    ]
}

视图实现

from django.conf import settings
def upfile(request):
    if request.method == "GET":
        return render(request, "upfile.html")
    else:
        # print(request.FILES)
        for name in request.FILES:
            files = request.FILES.getlist(name)
            # print(files)
            for file in files:
                #处理每个文件
                filePath = r"%s\%s"%(settings.MEDIA_ROOT,file.name)
                with open(filePath, "wb") as fp:
                    # 将文件数据切片写入描述符
                    for part in file.chunks():
                        fp.write(part)
        return HttpResponse("上传文件成功")

图片生成缩略图

# 生成缩略图

from PIL import Image
im = Image.open('c.jpg')
print(im.format, im.size, im.mode)
# JPEG (840, 1024) RGB
im.thumbnail((100,200))
im.save("c1.jpg", "JPEG")

分页

Paginator对象

clipboard.png

Page对象

clipboard.png

示例

from django.core.paginator import Paginator
def students(request, num):
    allStudents = Student.objects.all()
    #分页 每页6条数据
    paginator = Paginator(allStudents, 6)
    print(paginator.count, paginator.num_pages, paginator.page_range)
    pageStus = paginator.page(num)
    return render(request, "students.html", {"stus":pageStus})
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>学生信息</title>
</head>
<body>
<h1>学生信息</h1>
<ul>
    {% for stu in stus %}
        <li>{{ stu.name }}--{{ stu.grade }}--{{ stu.age }}</li>
    {% endfor %}
</ul>
{% if stus.has_previous %}
    <a href="/students/{{ stus.previous_page_number }}">上一页</a>
{% endif %}


{% for index in stus.paginator.page_range %}
    {% if index == stus.number %}
        {{ index }}
    {% else %}
    <a href="/students/{{ index }}/">{{ index }}</a>
    {% endif %}
{% endfor %}
{% if stus.has_next %}
<a href="/students/{{ stus.next_page_number }}">下一页</a>
{% endif %}
</body>
</html>

Ajax

问题

使用视图通过上下文向模板中传递数据,需要先加载完成模板的静态页面,再执行模板模型代码,生成最后的HTML代码,返回给浏览器,这个过程将页面与数据集成到了一起,扩展性差

解决

通过ajax的方式获取数据,再通过DOM操作将数据呈现到界面上

异步:如发邮件(注册的时候提示邮件已经发送成功,其实并没有) 能提高用户体验。 如豆瓣,防止页面卡死。

前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>展示学生</title>
    <script type="text/javascript" src="/static/js/jquery-3.1.1.min.js"></script>
</head>
<body>
    <button id="btn">显示学生信息</button>
    <ul id="stus">

    </ul>

    <script type="text/javascript">
        $(document).ready(function () {
            $("#btn").bind("click", function () {
                /*
                $.get("/allStudents/",{"a":1,"b":2},function (data, status) {
                    console.log("************************2");
                    console.log(data, status);
                    stus = data["data"];
                    for (var i = 0; i < stus.length; i++){
                        stu = stus[i];
                        $li = $("<li>"+stu.name+"---"+stu.age+"</li>");
                        $("#stus").append($li)
                    }
                });
                */
                $.ajax({
                    url:"/allStudents/",
                    type:"get",
                    data:{"a":1,"b":2,"c":3},
                    dataType:"json",
                    success:function (data, status) {
                        console.log("************************2");
                        console.log(data, status);
                        stus = data["data"];
                        for (var i = 0; i < stus.length; i++){
                            stu = stus[i];
                            $li = $("<li>"+stu.name+"&&&"+stu.age+"</li>");
                            $("#stus").append($li)
                        }
                    }
                });
                console.log("************************1");
            });
        });
    </script>
</body>
</html>
def allStudents(request):
    if request.method == "GET":
        if not request.is_ajax():
            return render(request, "allStudents.html")
        else:
            stus = Student.objects.all()
            arr = []
            for stu in stus:
                arr.append({"name":stu.name, "age":stu.age})
            return JsonResponse({"data":arr})

富文本

安装:pip install django-tinymce

在站点中使用

激活应用

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myApp',
    'tinymce',
]

配置

TINYMCE_DEFAULT_CONFIG={
    'theme':'advanced',
    'width':600,
    'height':400
}

模型

from tinymce.models import HTMLField
class Article(models.Model):
    title = models.CharField(max_length=20)
    content = HTMLField()

admin.py

admin.site.register(Article)

在自定义页面中使用

<!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':600,
            'height':400
        });
    </script>
</head>

<body>
    <form method="post" action="/tinymce/">
        {% csrf_token %}
        <input type="text" name="title" value=""><hr/>
        <textarea name="article"></textarea><hr/>
        <input type="submit" value="提交">
    </form>
</body>
</html>

Celery

问题

用户发起request请求,并等待response返回。在本次views中,可能需要执行一段耗时的程序,那么用户会等待很长时间,造成不好的用户体验

网站每小时需要同步数据(天气预报信息),但是http是需要触发的,难道要一个小时请求一次吗?

解决方案

使用celery:将耗时的程序放到celery中执行 使用celery定时执行

celery

任务task:就是一个python函数

队列queue:将需要执行的任务加入队列中

工人worker:在一个新的进程中负责执行队列中的任务

代理broker:负责调度,在布置环境中使用redis

安装

pip  install  celery==3.1.25
pip  install  celery-with-redis==3.0
pip  install  django-celery==3.2.1

配置

激活应用

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myApp',
    'tinymce',
    'djcelery',
]

配置redis

#celery
import djcelery
djcelery.setup_loader()
#配置redis数据库
#redis://:密码@ip:端口/库
BROKER_URL='redis://:sunck@127.0.0.1:6379/0'
#配置任务文件
CELERY_IMPORTS=("myApp.task")

创建任务文件,并分装任务

# 在应用目录下创建名为task.py的文件
# -*- coding:utf-8 -*-
from celery import task
import time

@task
def longIO():
    print("开始耗时操作……")
    time.sleep(5)
    print("结束耗时操作……")

迁移

python manage.py migrate

生成celery所需要的表

添加celery.py文件

将已存在的celery.py添加到工程目录下的与工程目录同名的目录下

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))

导入celery

在工程目录下的与工程目录同名的目录中的__init__.py文件中添加from .celery import app as celery_app

在视图使用任务

from myApp.task import longIO
def registe(request):
    longIO.delay()
    return HttpResponse("sunck is a good man")

启动redis

redis-server.exe     redis.windows.conf

启动worker

python  manage.py  celery  worker  --loglevel=info

启动Django服务

python  manage.py  runserver

rottengeek
476 声望78 粉丝

后续python相关内容移步简书与知乎(python进阶,爬虫,数据分析,机器学习,自然语言处理,知识图谱)