Django学习笔记(六)

1. 分页功能

在web页面有大量数据要显示时,分页可以使阅读更为清晰,django的分页功能依赖于Paginator类实现,该类位于包django.core.paginator中。

Paginator对象负责分页数据整体的管理:

  1. Paginator类构造方法:
    paginator = Paginator(object_list, per_page)
    两个参数object_list:需要分页数据的对象列表;per_page:每页的数据个数
    返回值:Paginator对象
  2. Paginator类的属性:
    属性说明
    count需要分页的对象总数
    num_pages分页后的页面总数
    page_range从1开始的range对象,记录当前的页码数
    per_page每页数据的个数
  3. Paginator对象的方法:
    方法说明
    paginator.page(number)number为页码信息,返回当前number页的页信息,若页码不存在,则抛出InvalidPage异常
    paginator.get_page(number)返回一个给定的基于 1 索引的 Page 对象,若页数不是数字,它返回第一页。若页码为负数或大于页数,则返回最后一页。

上面提到Paginator对象负责分页数据整体的管理,那么Page对象则负责某一页数据的管理:

  1. 创建Page对象,使用Paginator对象的page()方法返回Page对象
    page = paginator.page(页码)
  2. Page对象属性
    属性说明
    object_list当前页上的所有数据对象
    number当前页的序号,从1开始
    paginator当前Page对象相关的Paginator对象
  3. Page对象方法
    方法说明
    has_next()如果有下一页,返回True
    has_previous()如果有上一页,返回True
    has_other_pages()如果有上一页或下一页,返回True
    next_page_number()返回下一页的页码,若不存在,抛出InvalidPage异常
    previous_page_number()返回上一页的页码,若不存在,抛出InvalidPage异常

根据上面的知识,给出一个例子说明两个对象及其方法的使用方法

1.1 视图函数:

def test_page(request):
    # 以带参数的方式定义url test_page?page=1
    page_num = request.GET.get('page', 1)
    # 模拟模型层从数据库取出的对象数据
    all_data = ['这', '是', '一', '个', '页', '面']
    # 初始化paginator,每页显示一个对象
    paginator = Paginator(all_data, 1)
    # 创建Page对象
    c_page = paginator.page(int(page_num))
    return render(request, 'test_page.html', locals())

1.2 模板层:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>分页</title>
</head>
<body>

{% for foo in c_page %}
    <p>
        {{ foo }}
    </p>
{% endfor %}

{% if c_page.has_previous %}
    <a href="/test_page?page={{ c_page.previous_page_number }}">上一页</a>
{% else %}
    上一页
{% endif %}

{% for p_num in paginator.page_range %}
    {% if p_num == c_page.number %}
        {{ p_num }}
    {% else %}
        <a href="/test_page?page={{ p_num }}">{{ p_num }}</a>
    {% endif %}

{% endfor %}


{% if c_page.has_next %}
    <a href="/test_page?page={{ c_page.next_page_number }}">下一页</a>
{% else %}
    下一页
{% endif %}

</body>
</html>

效果:
分页

2. 文件上传

2.1 前端设计

实现文件上传的功能,在设计模板层时,有以下几点要求:

  1. 表单<form>文件上传时必须带有enctype="multipart/form-data"时,才会包含文件内容数据
  2. 文件上传必须为POST提交方式
  3. 表单中使用<input type=‘file’>标签上传文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件上传</title>
</head>
<body>
<!--注意加:enctype="multipart/form-data"-->
<form action="/test_upload" method="post" enctype="multipart/form-data">
	<!--csrf检查-->
	{% csrf_token %}
    <p>
        <input type="text" name="title">
    </p>
    <p>
        <input type="file" name="file_1">
    </p>
   <p>
        <input type="submit" value="上传">
   </p>

</form>

</body>
</html>

2.2 后端逻辑设计:

视图函数

不能用request.POST[‘xxx’]取得相应内容,应为file = request.FILES[‘xxx’],其中FILES的key对应页面中file框的name值,file绑定文件流的对象,可使用file.name取得文件名,使用file.file取得文件的字节流数据。

配置文件存储路径

django将用户上传的文件统称为media资源,首先需要在settings.py中设置MEDIA配置

# 值可更改,自定义
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

MEDIA_URL和MEDIA_ROOT还需要手动绑定:
在主路由中添加绑定路由,在主路由的urls.py中添加以下配置

# 配置media
from django.conf.urls.static import static
# 加载配置文件
from django.conf import settings

# 通过setting方法生成一个静态路由追加到urlpatterns中
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

以上的操作相当于配置了MEDIA_URL开头的路由,django接到该特征请求后去MEDIA_ROOT路径查找资源

文件写入方案

文件写入方式有两种,一种为Python中常见的open方式,这种方式步骤较为繁琐,且需要手动解决文件重名等问题,完整的视图函数处理逻辑如下:

def test_upload(request):
    if request.method == "GET":
        return render(request, 'test_file.html')
    elif request.method == 'POST':
        upload_file = request.FILES['file_1']
        print('上传的文件名为:', upload_file.name)
        # 拼接获得完整文件名
        filename = os.path.join(settings.MEDIA_ROOT, upload_file.name)
        # 开始写入文件
        with open(filename, 'wb') as f:
            data = upload_file.file.read()
            f.write(data)
        return HttpResponse('接收文件:' + upload_file.name + '成功')

第二种方式需要借助ORM框架,使用数据表的一个字段来存储,字段类型设置为FileField(upload=‘子目录名’),具体步骤为:

  1. 先创建一个应用,在其model.py中创建一个模型类来存储上传文件,注意的是创建文件字段时设置upload_to属性,指定要存储的子目录,这里设置为picture,则最后上传文件的存储位置就是media/picture目录,由此可知,借助ORM框架的文件上传并不是指文件存储在数据库中,模型类的FileField字段实际存储的是文件的路径。
class Content(models.Model):
    title = models.CharField('文章名字', max_length=11)
    # 指明子目录名字,在此示例中,上传文件存储到picture文件夹下
    picture = models.FileField(upload_to='picture')
  1. 接收文件后,直接调用模型类的objects.create()方法创建记录即可
    第二种方式的视图函数如下:
def test_upload(request):
    if request.method == "GET":
        return render(request, 'test_file.html')
    elif request.method == 'POST':
        title = request.POST['title']
        myfile = request.FILES['file_1']
        # 创建模型类对象
        Content.objects.create(title=title, picture=myfile)
        return HttpResponse('--文件上传成功--')

2.3 效果

选择文件页面:
选择页面
上传成功:
上传成功
数据库存储文件
数据库存储
服务器端存储图片:
图片存储

3. 邮件发送

3.1 邮箱相关的协议

邮件的常用的三个协议:SMTP、IMAP和POP3,这三个协议在本科时期计算机网络课程中已有接触,它们均属于应用层协议。
SMTP用于发送邮件,属于推送协议。
IMAP和POP3则用于邮件拉取协议,二者区别主要有以下几点:

  1. IMAP具备摘要浏览的功能,可预览部分摘要再下载整个邮件,而POP3必须下载全部邮件,没有摘要功能
  2. IMAP为双向协议,客户端可向服务器端发出反馈,而POP3为单向协议,客户端操作无法同步服务器

三个协议协同配合完成邮件发送的过程,例如,用户A使用QQ邮箱向用户B的新浪邮箱中发送邮件,过程如下:
邮件发送过程
首先是用户A使用邮件客户端将邮件通过SMTP协议发送到QQ邮箱服务器,QQ邮箱服务器再次使用SMTP协议将邮件转发到新浪邮箱服务器,而用户B使用邮件客户端,可选择自主通过IMAP协议或者POP3从新浪邮箱服务器中接收邮件。整个过程中,django实际起到的作用就是充当邮件客户端的身份,完成转发或接收邮件的功能。

这样一来,配置django可以使其承担邮件客户端的角色就成为了问题的关键。

3.2 django发送邮件

要配置邮件发送,使用的协议为SMTP,要给django授权一个邮箱,使用该邮箱给对应的收件人发送邮件,django.core.mail中封装了SMTP协议,使用步骤如下:

  1. 给django授权一个邮箱(以QQ邮箱为例),具体步骤自行百度
  2. 配置django,在settings.py中进行相关的配置
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'  # 固定写法
EMAIL_HOST = 'smtp.qq.com'  # SMTP地址
EMAIL_PORT = 25  # SMTP端口
EMAIL_HOST_USER = 'xxxxxxxxxx@qq.com'  # 发送邮件的邮箱
EMAIL_HOST_PASSWORD = 'xxxxxxxxxx'  # 授权码
EMAIL_SUBJECT_PREFIX = '[邮件测试] '  # 为邮件Subject-line前缀,默认是'[django]'
EMAIL_USE_TLS = False # 与SMTP服务器通信时,是否启动TLS链接(安全链接)默认false
  1. 调用函数发送邮件
from django.core import mail

mail.send_mail(
    # 邮件标题
    subject='test1',
    # 邮件内容
    message='一个测试邮件',
    # 发送者(当前配置邮箱)
    from_email='xxxxxx@qq.com',
    # 接收者邮件列表
    recipient_list=['xxxxxxx@qq.com']
)

使用django shell进行测试发送,可以正常接收到发送的邮件
测试邮件
更多的邮件配置可参考django官方文档:发送邮件

3.3 邮件报警练习

使用中间件抓取视图函数的异常,以邮件的形式将异常信息发送给指定的联系人,要求如下:

  • 邮件主题:异常警告
  • 内容:必须带有异常信息,其他自定义
  • 收件人:自定义

实现方法:需要利用django的中间件处理

中间件介绍:

中间件是django请求/响应处理中的一个框架,是一个“插件”系统,用于对django输入/输出做全局改变,中间件以类的形式体现,每个中间件组件负责做一些特定的功能。

中间件类必须继承自django.utils.deprecation.MiddlewareMixin类,中间件类必须继承下列五个方法中的一个或多个,其中最常用的是前四个:

  • process_request(self, request)
    执行路由之前被调用,返回None或者HttpResponse对象
  • process_view(self, request, callback, callback_args, callback_kwargs)
    调用视图之前被调用,在每个请求上调用,返回None或者HttpResponse对象
  • process_response(self, request, response)
    所有响应返回浏览器时要调用,在每个请求上调用,返回一个HttpResponse对象
  • process_exception(self, request, exception)
    当处理过程中抛出异常时调用,返回一个HttpResponse对象
  • process_template_response(self, request, response)
    在视图函数执行完毕且视图返回对象中有render()方法时被调用,该方法必须返回一个实现了render方法的响应对象

中间件中的大多数方法在返回None时表示忽略当前操作,继续进入下一项事件,当返回HttpResponse对象时表示此次请求结束,直接返回客户端响应。

中间件的编写
一般在项目目录同级下建立一个目录middleware来存放中间件代码,在文件内需要引入django.utils.deprecation.MiddlewareMixin类,中间件类必须继承MiddlewareMixin类,下面给出一个中间件类的示例:

from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin

class MyMW(MiddlewareMixin):

    def process_request(self, request):
        print('MyMW process_request')

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print('MyMW process_view')

    def process_response(self, request, response):
        print("MyMW process_response")
        return response

中间件类编写完成后,需要在项目的settings.py中注册自定义的中间件,即在MIDDLEWARE数组中加入新建中间件类的路径
注册中间件
自定义的中间件类只有配置后才会起作用,中间件被调用时按照向上到下再由下至上的顺序,具体调用过程在Django请求处理流程一图中有详细的响应顺序

3.3 邮件报警练习的解决

要求在视图函数出现异常时发送邮件,显然可以采用中间件方法process_exception(self, request, exception)来捕获异常,编写中间件类并在settings.py中注册

class ExceptionMW(MiddlewareMixin):
    def process_exception(self, request, exception):
        import traceback
        print(exception)
        mail.send_mail(
            subject='网站出现异常......',
            # 调用traceback的方法返回异常信息
            message=traceback.format_exc(),
            from_email='xxxxxx@qq.com',
            recipient_list=['xxxxxxxx@qq.com']
        )
        return HttpResponse('---网页异常----')

测试时任意编写一个有异常的视图函数,如

def exception_view(request):
    Except
    return HttpResponse('提供异常')

将视图函数路径配置完毕,访问发出此请求后一定会出现异常,异常会被中间件方法process_exception捕获,在方法内发送异常的详细信息到指定的邮箱中,并且从process_exception方法中直接返回HttpResponse显示网页异常,结束请求过程。
请求结束
邮件内容:
异常邮件报警

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值