跨站请求伪造攻击 CSRF
-
跨站请求伪造攻击
- 某些恶意网站上包含链接、表单按钮或者JavaScript,它们会利用登录过的用户在浏览器中的认证信息试图在你的网站上完成某些操作,这就是跨站请求伪造(CSRF,即Cross-Site Request Forgey)。
-
说明:
-
CSRF中间件和模板标签提供对跨站请求伪造简单易用的防护。
-
作用:
- 不让其它表单提交到此 Django 服务器
-
防范步骤:
- settings.py中确认 MIDDLEWARE 中 django.middleware.csrf.CsrfViewMiddleware是否打开
- 模板中,form标签下添加如下标签
{% csrf_token %}
-
如果某个视图不需要django进行csrf保护,可以用装饰器关闭对此视图的检查
from django.views.decorators.csrf import csrf_exempt @csrf_exempt def my_view(request): return HttpResponse('Hello world')
分页
- 分页是指在web页面有大量数据需要显示,为了阅读方便在每个页页中只显示部分数据。
- 好处:
- 方便阅读
- 减少数据提取量,减轻服务器压力。
- Django提供了Paginator类可以方便的实现分页功能
- Paginator类位于
django.core.paginator
模块中。
Paginator对象
-
负责分页数据整体的管理
-
对象的构造方法
- paginator = Paginator(object_list, per_page)
- 参数
- object_list 需要分类数据的对象列表
- per_page 每页数据个数
- 返回值:
- Paginator的对象
-
Paginator属性
- count:需要分类数据的对象总数
- num_pages:分页后的页面总数
- page_range:从1开始的range对象, 用于记录当前面码数
- per_page 每页数据的个数
-
Paginator方法
- page(number)
- 参数 number为页码信息(从1开始)
- 返回当前number页对应的页信息
- 如果提供的页码不存在,抛出InvalidPage异常
- page(number)
-
Paginator异常exception
- InvalidPage:总的异常基类,包含以下两个异常子类
- PageNotAnInteger:当向page()传入一个不是整数的值时抛出
- EmptyPage:当向page()提供一个有效值,但是那个页面上没有任何对象时抛出
- InvalidPage:总的异常基类,包含以下两个异常子类
Page对象
-
负责具体某一页的数据的管理
-
创建对象
Paginator对象的page()方法返回Page对象page = paginator.page(页码)
-
Page对象属性
- object_list:当前页上所有数据对象的列表
- number:当前页的序号,从1开始
- paginator:当前page对象相关的Paginator对象
-
Page对象方法
- has_next():如果有下一页返回True
- has_previous():如果有上一页返回True
- has_other_pages():如果有上一页或下一页返回True
- next_page_number():返回下一页的页码,如果下一页不存在,抛出InvalidPage异常
- previous_page_number():返回上一页的页码,如果上一页不存在,抛出InvalidPage异常
- len():返回当前页面对象的个数
-
说明:
-
Page 对象是可迭代对象,可以用 for 语句来 访问当前页面中的每个对象
-
参考文档 https://docs.djangoproject.com/en/2.2/topics/pagination/
-
分页示例:
- 视图函数
from django.core.paginator import Paginator def book(request): bks = Book.objects.all() paginator = Paginator(bks, 10) cur_page = request.GET.get('page', 1) # 得到默认的当前页 page = paginator.page(cur_page) return render(request, 'bookstore/book.html', locals())
-
模板设计
<html> <head> <title>分页显示</title> </head> <body> {% for b in page %} <div>{{ b.title }}</div> {% endfor %} {% if page.has_previous %} <a href="{% url 'book' %}?page={{ page.previous_page_number }}">上一页</a> {% else %} 上一页 {% endif %} {% for p in paginator.page_range %} {% if p == page.number %} {{ p }} {% else %} <a href="{% url 'book' %}?page={{ p }}">{{ p }}</a> {% endif %} {% endfor %} {% if page.has_next %} <a href="{% url 'book' %}?page={{ page.next_page_number }}">下一页</a> {% else %} 下一页 {% endif %} </body> </html>
文件下载
Django可直接在视图函数中生成csv文件 并响应给浏览器
import csv
from django.http import HttpResponse
from .models import Book
def make_csv_view(request):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="mybook.csv"'
all_book = Book.objects.all()
writer = csv.writer(response)
writer.writerow(['id', 'title'])
for b in all_book:
writer.writerow([b.id, b.title])
return response
- 响应获得一个特殊的MIME类型text / csv。这告诉浏览器该文档是CSV文件,而不是HTML文件
- 响应会获得一个额外的
Content-Disposition
标头,其中包含CSV文件的名称。它将被浏览器用于“另存为…”对话框 - 对于CSV文件中的每一行,调用
writer.writerow
,传递一个可迭代对象,如列表或元组
文件上传
-
文件上传必须为POST提交方式
-
表单
<form>
中文件上传时必须有带有enctype="multipart/form-data"
时才会包含文件内容数据。 -
表单中用
<input type="file" name="xxx">
标签上传文件- 名字
xxx
对应request.FILES['xxx']
对应的内存缓冲文件流对象。可通能过request.FILES['xxx']
返回的对象获取上传文件数据 file=request.FILES['xxx']
file 绑定文件流对象,可以通过文件流对象的如下信息获取文件数据
file.name 文件名
file.file 文件的字节流数据
- 名字
-
上传文件的表单书写方式
<!-- file: index/templates/index/upload.html --> <html> <head> <meta charset="utf-8"> <title>文件上传</title> </head> <body> <h3>上传文件</h3> <form method="post" action="/test_upload" enctype="multipart/form-data"> <input type="file" name="myfile"/><br> <input type="submit" value="上传"> </form> </body> </html>
-
在setting.py 中设置MEDIA相关配置;Django把用户上传的文件,统称为media资源
# file : settings.py MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
-
在当前项目文件夹下创建
media
文件夹$ mkdir media
-
上传文件的视图处理函数 方案1 传统写入
# file views.py from django.http import HttpResponse from django.conf import settings from django.views.decorators.csrf import csrf_exempt import os @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 + "成功")
-
上传文件的视图处理函数 方案2 借助orm
#test_upload/models.py from django.db import models # Create your models here. class Content(models.Model): desc = models.CharField(max_length=100) myfile = models.FileField(upload_to='myfiles') #test_upload/views.py from test_upload.models import * from django.views.decorators.csrf import csrf_exempt @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-----')
-
若要在浏览器中访问 上传的资源,runserver环境下,需要在项目得主路由下添加media路由的绑定
from django.conf import settings from django.conf.urls.static import static urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
浏览器可以访问 http://127.0.0.1:8000/media/xxxx