django[五] 视图层view - 迁
视图函数及快捷方式
视图函数,简称视图,本质上是一个简单的Python函数,它接受Web请求并且返回Web响应。
响应的内容可以是HTML网页、重定向、404错误,XML文档或图像等任何东西。但是,无论视图本身是个什么处理逻辑,最好都返回某种响应。
视图函数的代码写在哪里也无所谓,只要它在你的Python目录下面。但是通常我们约定将视图放置在项目或应用程序目录中的名为views.py的文件中。
简单的视图
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
返回错误
HttpResponse的许多子类对应着除了200(代表“OK”)以外的一些常用的HTTP状态码。
为了标示一个错误,可以直接返回那些子类中的一个实例,而不是普通的HttpResponse。像下面这样:
from django.http import HttpResponse, HttpResponseNotFound
def my_view(request):
# ...
if foo:
return HttpResponseNotFound('<h1>Page not found</h1>')
else:
return HttpResponse('<h1>Page was found</h1>')
Django为404错误提供了一个特化的子类HttpResponseNotFound。由于一些状态码不太常用,所以不是每个状态码都有一个特化的子类。
也可以向HttpResponse的构造器传递HTTP状态码,来创建你想要的任何状态码的返回类。 像下面这样:
from django.http import HttpResponse
def my_view(request):
# ...
# Return a "created" (201) response code.
return HttpResponse(status=201)
关键是在返回中提供status=201参数。别的什么303之类的错误都可以参照上面的例
Http404异常
class django.http.Http404
这是一个Django内置的异常类。可以在需要的地方认为弹出它,Django会捕获它,并且带上HTTP404错误码返回你当前app的标准错误页面或者自定义错误页面。像下面这样:
from django.http import Http404
from django.shortcuts import render
from polls.models import Poll
def detail(request, poll_id):
try:
p = Poll.objects.get(pk=poll_id)
except Poll.DoesNotExist:
raise Http404("Poll does not exist")
return render(request, 'polls/detail.html', {'poll': p})
为了在Django返回404时显示自定义的HTML,可以创建一个名为404.html的HTML模板,并将其放置在模板树的顶层。 当DEBUG设置为False时,此模板将被自动使用
render()
render(request, template_name, context=None, content_type=None, status=None, using=None)[source]
结合一个给定的模板和一个给定的上下文字典,返回一个渲染后的HttpResponse对象。
必需参数:
- request:视图函数处理的当前请求,封装了请求头的所有数据,其实就是视图参数request。
- template_name:要使用的模板的完整名称或者模板名称的列表。如果是一个列表,将使用其中能够查找到的第一个模板。
可选参数:
- context:添加到模板上下文的一个数据字典。默认是一个空字典。可以将认可需要提供给模板的数据以字典的格式添加进去。这里有个小技巧,使用Python内置的locals()方法,可以方便的将函数作用于内的所有变量一次性添加。
- content_type:用于生成的文档的MIME类型。 默认为
DEFAULT_CONTENT_TYPE设置的值。 - status:响应的状态代码。 默认为200。
- using:用于加载模板使用的模板引擎的NAME。
范例:
下面的例子将渲染模板myapp/index.html,MIME类型为application/xhtml+xml:
from django.shortcuts import render
def my_view(request):
# View code here...
return render(request, 'myapp/index.html', {
'foo': 'bar',
}, content_type='application/xhtml+xml')
redirect()
redirect(to, permanent=False, args, *kwargs)[source]
根据传递进来的url参数,返回HttpResponseRedirect。
参数to可以是:
- 一个模型:将调用模型的
get_absolute_url()函数,反向解析出目的url; - 视图名称:可能带有参数:reverse()将用于反向解析url;
- 一个绝对的或相对的URL:将原封不动的作为重定向的目标位置。
默认情况下是临时重定向,如果设置permanent=True将永久重定向。
范例:
# 调用对象的get_absolute_url()方法来重定向URL
from django.shortcuts import redirect
def my_view(request):
...
object = MyModel.objects.get(...)
return redirect(object)
# 传递视图名,使用reverse()方法反向解析url
def my_view(request):
...
return redirect('some-view-name', foo='bar')
# 重定向到硬编码的URL
def my_view(request):
...
return redirect('/some/url/')
# 重定向到一个完整的URL
def my_view(request):
...
return redirect('https://example.com/')
# 所有上述形式都接受permanent参数;如果设置为True,将返回永久重定向
def my_view(request):
...
object = MyModel.objects.get(...)
return redirect(object, permanent=True)
get_object_or_404
get_object_or_404(klass, args, *kwargs)[source]
这个方法,非常有用,请一定熟记。常用于查询某个对象,找到了则进行下一步处理,如果未找到则给用户返回404页面。
在后台,Django其实是调用了模型管理器的get()方法,只会返回一个对象。不同的是,如果get()发生异常,会引发Http404异常,从而返回404页面,而不是模型的DoesNotExist异常。
必需参数:
- klass:要获取的对象的Model类名或者Queryset等;
**kwargs:查询的参数,格式应该可以被get()接受。
范例:
# 从MyModel中使用主键1来获取对象
from django.shortcuts import get_object_or_404
def my_view(request):
my_object = get_object_or_404(MyModel, pk=1)
# 这个等同于
from django.http import Http404
def my_view(request):
try:
my_object = MyModel.objects.get(pk=1)
except MyModel.DoesNotExist:
raise Http404("No MyModel matches the given query.")
# 除了传递Model名称,还可以传递一个QuerySet实例
queryset = Book.objects.filter(title__startswith='M') get_object_or_404(queryset, pk=1) # 等同于 get_object_or_404(Book, title__startswith='M', pk=1)
get_list_or_404
get_list_or_404(klass, args, *kwargs)[source]
这其实就是get_object_or_404多值获取版本
HttpRequest对象
每当一个用户请求发送过来,Django将HTTP数据包中的相关内容,打包成为一个HttpRequest对象,并传递给每个视图函数作为第一位置参数,也就是request,供我们调用
属性
1. HttpRequest.scheme 字符串类型,表示请求的协议种类,'http'或'https'。 2. HttpRequest.body bytes类型,表示原始HTTP请求的正文。它对于处理非HTML形式的数据非常有用:二进制图像、XML等。如果要处理常规的表单数据,应该使用HttpRequest.POST。 还可以使用类似读写文件的方式从HttpRequest中读取数据,参见HttpRequest.read()。 3. HttpRequest.path 字符串类型,表示当前请求页面的完整路径,但是不包括协议名和域名。例如:"/music/bands/the_beatles/"。这个属性,常被用于我们进行某项操作时,如果不通过,返回用户先前浏览的页面。非常有用! 4. HttpRequest.path_info 在某些Web服务器配置下,主机名后的URL部分被分成脚本前缀部分和路径信息部分。path_info 属性将始终包含路径信息部分,不论使用的Web服务器是什么。使用它代替path可以让代码在测试和开发环境中更容易地切换。 例如,如果应用的WSGIScriptAlias设置为/minfo,那么HttpRequest.path等于/music/bands/the_beatles/ ,而HttpRequest.path_info为/minfo/music/bands/the_beatles/。 5. HttpRequest.method 字符串类型,表示请求使用的HTTP方法 request.method == POST 6. HttpRequest.encoding 字符串类型,表示提交的数据的编码方式(如果为None 则表示使用DEFAULT_CHARSET设置)。 这个属性是可写的,可以通过修改它来改变表单数据的编码。任何随后的属性访问(例如GET或POST)将使用新的编码方式。 7. HttpRequest.content_type Django1.10中新增。表示从CONTENT_TYPE头解析的请求的MIME类型。 8. HttpRequest.content_params Django 1.10中新增。包含在CONTENT_TYPE标题中的键/值参数字典。 9 HttpRequest.GET 一个类似于字典的对象,包含GET请求中的所有参数。 详情参考QueryDict文档。 10. HttpRequest.POST 一个包含所有POST请求的参数,以及包含表单数据的字典。 详情请参考QueryDict文档。 如果需要访问请求中的原始或非表单数据,可以使用HttpRequest.body属性。 注意:请使用if request.method == "POST"来判断一个请求是否POST类型,而不要使用if request.POST。 POST中不包含上传文件的数据。 11. HttpRequest.COOKIES 包含所有Cookie信息的字典。 键和值都为字符串。可以类似字典类型的方式,在cookie中读写数据,但是注意cookie是不安全的,因此,不要写敏感重要的信息。 12. HttpRequest.FILES 一个类似于字典的对象,包含所有上传的文件数据。 FILES中的每个键为<input type="file" name="" />中的name属性值。 FILES中的每个值是一个UploadedFile。 要在Django中实现文件上传,就要靠这个属性! 如果请求方法是POST且请求的<form>中带有enctype="multipart/form-data"属性,那么FILES将包含上传的文件的数据。 否则,FILES将为一个空的类似于字典的对象,属于被忽略、无用的情形。 13. HttpRequest.META 包含所有HTTP头部信息的字典。 可用的头部信息取决于客户端和服务器,下面是一些示例: CONTENT_LENGTH —— 请求正文的长度(以字符串计)。 CONTENT_TYPE —— 请求正文的MIME类型。 HTTP_ACCEPT —— 可接收的响应Content-Type。 HTTP_ACCEPT_ENCODING —— 可接收的响应编码类型。 HTTP_ACCEPT_LANGUAGE —— 可接收的响应语言种类。 HTTP_HOST —— 客服端发送的Host头部。 HTTP_REFERER —— Referring页面。 HTTP_USER_AGENT —— 客户端的user-agent字符串。 QUERY_STRING —— 查询字符串。 REMOTE_ADDR —— 客户端的IP地址。想要获取客户端的ip信息,就在这里! REMOTE_HOST —— 客户端的主机名。 REMOTE_USER —— 服务器认证后的用户,如果可用。 REQUEST_METHOD —— 表示请求方法的字符串,例如"GET" 或"POST"。 SERVER_NAME —— 服务器的主机名。 13. HttpRequest.resolver_match 代表一个已解析的URL的ResolverMatch实例。
由中间件设置的属性
1. HttpRequest.session SessionMiddleware中间件:一个可读写的,类似字典的对象,表示当前会话。我们要保存用户状态,回话过程等等,靠的就是这个中间件和这个属性。 2. HttpRequest.site CurrentSiteMiddleware中间件:get_current_site()方法返回的Site或RequestSite的实例,代表当前站点是哪个。 Django是支持多站点的,如果你同时上线了几个站点,就需要为每个站点设置一个站点id。 3. HttpRequest.user AuthenticationMiddleware中间件:表示当前登录的用户的AUTH_USER_MODEL的实例,这个模型是Django内置的Auth模块下的User模型。如果用户当前未登录,则user将被设置为AnonymousUser的实例。
方法
1. HttpRequest.get_host()[source] 根据HTTP_X_FORWARDED_HOST和HTTP_HOST头部信息获取请求的原始主机。 如果这两个头部没有提供相应的值,则使用SERVER_NAME和SERVER_PORT。 例如:"127.0.0.1:8000" 注:当主机位于多个代理的后面,get_host()方法将会失败。解决办法之一是使用中间件重写代理的头部 2. HttpRequest.get_port()[source] 使用META中HTTP_X_FORWARDED_PORT和SERVER_PORT的信息返回请求的始发端口。 3. HttpRequest.get_full_path()[source] 返回包含完整参数列表的path。例如:/music/bands/the_beatles/?print=true 4. HttpRequest.build_absolute_uri(location)[source] 返回location的绝对URI形式。 如果location没有提供,则使用request.get_full_path()的值。 5. HttpRequest.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)[source] 从已签名的Cookie中获取值,如果签名不合法则返回django.core.signing.BadSignature。 6. HttpRequest.is_secure()[source] 如果使用的是Https,则返回True,表示连接是安全的。 7. HttpRequest.is_ajax()[source] 如果请求是通过XMLHttpRequest生成的,则返回True。 这个方法的作用就是判断,当前请求是否通过ajax机制发送过来的。 8. HttpRequest.read(size=None)[source] 9. HttpRequest.readline()[source] 10. HttpRequest.readlines()[source] 11. HttpRequest.xreadlines()[source] 12. HttpRequest.iter() 上面的几个方法都是从HttpRequest实例读取文件数据的方法。
QueryDict对象
在HttpRequest对象中,GET和POST属性都是一个django.http.QueryDict的实例。也就是说你可以按本文下面提供的方法操作request.POST和request.GET
request.POST或request.GET的QueryDict都是不可变,只读的。如果要修改它,需要使用QueryDict.copy()方法,获取它的一个拷贝,然后在这个拷贝上进行修改操作。
from django.http import QueryDict
>>> QueryDict('a=1&a=33&c=42')
<QueryDict: {'a': ['1', '33'], 'c': ['42']}>
# 更新
>>> QueryDict('a=1',mutable=True)
<QueryDict: {'a': ['1']}>
>>> q=QueryDict('a=1',mutable=True)
>>> q.update({'a':33333})
>>> q
<QueryDict: {'a': ['1', 33333]}>
.....略
HttpResponse对象
HttpResponse类定义在django.http模块中。
HttpRequest对象由Django自动创建,而HttpResponse对象则由程序员手动创建.
我们编写的每个视图都要实例化、填充和返回一个HttpResponse对象。也就是函数的return值。
使用方法
# 传递一个字符串
>>> from django.http import HttpResponse
>>> response = HttpResponse("Here's the text of the Web page.")
>>> response = HttpResponse("Text only, please.", content_type="text/plain")
# 设置头部字段
>>> response = HttpResponse()
>>> response['Age'] = 120
>>> del response['Age']
# 告诉浏览器将响应视为文件附件
>>> response = HttpResponse(my_data, content_type='application/vnd.ms-excel')
>>> response['Content-Disposition'] = 'attachment; filename="foo.xls"'
属性
HttpResponse.flush() 清空HttpResponse实例的内容。 HttpResponse.delete_cookie(key, path='/', domain=None) 删除Cookie中指定的key。 由于Cookie的工作方式,path和domain应该与set_cookie()中使用的值相同,否则Cookie不会删掉。 HttpResponse.set_cookie(key, value='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=False) 设置一个Cookie。 参数与Python标准库中的Morsel.Cookie对象相同。
HttpResponse的子类
Django包含了一系列的HttpResponse衍生类(子类),用来处理不同类型的HTTP响应。与HttpResponse相同, 这些衍生类存在于django.http之中。 class HttpResponseRedirect[source]:重定向,返回302状态码。已经被redirect()替代。 class HttpResponsePermanentRedirect[source]:永久重定向,返回301状态码。 class HttpResponseNotModified[source]:未修改页面,返回304状态码。 class HttpResponseBadRequest[source]:错误的请求,返回400状态码。 class HttpResponseNotFound[source]:页面不存在,返回404状态码。 class HttpResponseForbidden[source]:禁止访问,返回403状态码。 class HttpResponseNotAllowed[source]:禁止访问,返回405状态码。 class HttpResponseGone[source]:过期,返回405状态码。 class HttpResponseServerError[source]:服务器错误,返回500状态码。
JsonResponse类
JsonResponse是HttpResponse的一个子类,是Django提供的用于创建JSON编码类型响应的快捷类。
它从父类继承大部分行为,并具有以下不同点:
它的默认Content-Type头部设置为application/json。
它的第一个参数data,通常应该为一个字典数据类型。 如果safe参数设置为False,则可以是任何可JSON 序列化的对象。
encoder默认为django.core.serializers.json.DjangoJSONEncoder,用于序列化数据。
>>> from django.http import JsonResponse
>>> response = JsonResponse({'foo': 'bar'})
>>> response.content
b'{"foo": "bar"}'
若要序列化非dict对象,必须设置safe参数为False:
>>> response = JsonResponse([1, 2, 3], safe=False)
如果不传递safe=False,将抛出一个TypeError。
如果你需要使用不同的JSON 编码器类,可以传递encoder参数给构造函数:
>>> response = JsonResponse(data, encoder=MyJSONEncoder)
StreamingHttpResponse类
StreamingHttpResponse类被用来从Django响应一个流式对象到浏览器。如果生成的响应太长或者是占用的内存较大,这么做可能更有效率。 例如,它对于生成大型的CSV文件非常有用。
StreamingHttpResponse不是HttpResponse的衍生类(子类),因为它实现了完全不同的应用程序接口。但是,除了几个明显不同的地方,两者几乎完全相同。
FileResponse
文件类型响应。通常用于给浏览器返回一个文件附件。
FileResponse是StreamingHttpResponse的衍生类,为二进制文件专门做了优化。
FileResponse需要通过二进制模式打开文件,如下:
>>> from django.http import FileResponse
>>> response = FileResponse(open('myfile.png', 'rb'))
文件上传
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>upload file</title>
</head>
<body>
<form action="/upload_file/" method="POST" enctype="multipart/form-data" title="fansu">
{% csrf_token %}
<input type="text" name="title" size="30">
<input type="file" name="file" title="11111">
<input type="submit" value="提交">
</form>
</body>
</html>
UploadedFile UploadedFile是类文件对象,具有以下方法和属性: UploadedFile.read() 读取整个上传文件的数据,文件较大时慎用。 UploadedFile.multiple_chunks(chunk_size=None) 判断文件是否足够大,一般为2.5M UploadedFile.chunks(chunk_size=None) 返回一个生成器对象,当multiple_chunks()为True时应该使用这个方法来代替read(). UploadedFile.name 上传文件的name。 UploadedFile.size 上传文件的大小。 UploadedFile.content_type 上传文件时的content_type报头,例如(e.g. text/plain or application/pdf). UpladedFile.charset 编码
利用form处理上传文件
首先,写一个form模型,它必须包含一个FileField:
# forms.py
from django import forms
class UploadFileForm(forms.Form):
title = forms.CharField(max_length=50)
file = forms.FileField()
处理这个表单的视图将在request.FILES中收到文件数据,可以用request.FILES['file']来获取上传文件的具体数据,其中的键值‘file’是根据file = forms.FileField()的变量名来的。
注意:request.FILES只有在请求方法为POST,并且提交请求的<form>具有enctype="multipart/form-data"属性时才有效。 否则,request.FILES将为空。
下面是一个接收上传文件的视图范例:
from .forms import UploadFileForm
from django.http import HttpResponseRedirect, JsonResponse
from django.shortcuts import render
from django.urls import reverse
def upload_file(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES) # 注意获取数据的方式
if form.is_valid():
handle_uploaded_file(request.FILES['file'])
return HttpResponseRedirect(reverse('/upload_file/'))
else:
return JsonResponse(form.errors, safe=False)
else:
form = UploadFileForm()
return render(request, 'upload.html', {'form': form})
def handle_uploaded_file(f):
with open(f.name, 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
遍历UploadedFile.chunks(),而不是直接使用read()方法,能确保大文件不会占用系统过多的内存。
利用modelForm
models.py
from django.db import models
# Create your models here.
class ImageFile(models.Model):
image = models.ImageField(upload_to='static/images/%Y/%m/%d')
forms.py
from .models import ImageFile
class ImageFileForm(forms.ModelForm):
class Meta:
model = ImageFile
fields = '__all__'
views.py
from .forms import ImageFileForm
def upload_img(request):
if request.method == 'POST':
form = ImageFileForm(request.POST or None, request.FILES or None)
if form.is_valid():
image = form.save()
return JsonResponse(image.image.url, safe=False)
else:
return JsonResponse(form.errors)
else:
form = ImageFileForm()
return render(request, 'upload_img.html', {'form': form})
区别就是使用了modelform后就不用写文件保存的部分了
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>upload file</title>
</head>
<body>
<form action="/upload_img/" method="POST" enctype="multipart/form-data" title="fansu">
{% csrf_token %}
<input type="text" name="title" size="30">
<input type="file" name="image" title="11111">
<input type="submit" value="提交">
</form>
</body>
</html>
生成CSV及PDF文件
CSV
Python自带处理CSV文件的标准库csv。csv模块的CSV文件创建功能作用于类似于文件对象创建,并且Django的HttpResponse对象也是类似于文件的对象
def some_streaming_csv_view(request):
# Create the HttpResponse object with the appropriate CSV header.
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
writer = csv.writer(response)
writer.writerow(['First row', 'Foo', 'Bar', 'Baz'])
writer.writerow(['Second row', 'A', 'B', 'C', '"Testing"', "Here's a quote"])
return response
响应对象的MIME类型设置为text/csv,告诉浏览器,返回的是一个CSV文件而不是HTML文件。
响应对象设置了附加的Content-Disposition协议头,含有CSV文件的名称。文件名随便取,浏览器会在“另存为...”对话框等环境中使用它。
要在生成CSV的API中使用钩子非常简单:只需要把response作为第一个参数传递给csv.writer。csv.writer方法接受一个类似于文件的对象,而HttpResponse对象正好就是这么个东西。
对于CSV文件的每一行,调用writer.writerow,向它传递一个可迭代的对象比如列表或者元组。
CSV模板会为你处理各种引用,不用担心没有转义字符串中的引号或者逗号。只需要向writerow()传递你的原始字符串,它就会执行正确的操作。
当处理大尺寸文件时,可以使用Django的StreamingHttpResponse类,通过流式传输,避免负载均衡器在服务器生成响应的时候断掉连接,提高传输可靠性。
在下面的例子中,利用Python的生成器来有效处理大尺
def some_streaming_csv_view1(request):
"""A view that streams a large CSV file."""
# Generate a sequence of rows. The range is based on the maximum number of
# rows that can be handled by a single sheet in most spreadsheet
# applications.
rows = (["Row {}".format(idx), str(idx)] for idx in range(65536))
pseudo_buffer = Echo()
writer = csv.writer(pseudo_buffer)
response = StreamingHttpResponse((writer.writerow(row) for row in rows),
content_type="text/csv")
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
return response
可以通过开源的Python PDF库ReportLab来实现PDF文件的动态生成
pip install reportlab
ReportLab的API可以处理于类似于文件(file-like)的对象。下面是一个 “Hello World”的例子
from reportlab.pdfgen import canvas
from django.http import HttpResponse
def some_view(request):
# 创建带有PDF头部定义的HttpResponse对象
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'
# 创建一个PDF对象,并使用响应对象作为它要处理的‘文件’
p = canvas.Canvas(response)
# 通过PDF对象的drawString方法,写入一条信息。具体参考模块的官方文档说明。
p.drawString(100, 100, "Hello world.")
# 关闭PDF对象
p.showPage()
p.save()
return response
相关说明:
- 响应对象的MIME类型为
application/pdf。 这会告诉浏览器,文档是个PDF文件而不是HTML文件。 - 响应对象设置了附加的
Content-Disposition协议头,含有PDF文件的名称。 - 文件名可以是任意的,浏览器会在“另存为...”对话框等中使用。
Content-Disposition以'attachment'开头,强制让浏览器弹出对话框来提示或者确认。- Canvas函数接受一个类似于文件的对象,而HttpResponse对象正好合适。
- 最后,在PDF文件上调用showPage()和save()方法非常重要。
- 注意:ReportLab并不是线程安全的。
遇到复杂pdf,可以考虑使用io库作为PDF文件的临时保存地点。这个库提供了一个类似于文件的对象接口,非常实用。
from io import BytesIO
from reportlab.pdfgen import canvas
from django.http import HttpResponse
def some_view(request):
# Create the HttpResponse object with the appropriate PDF headers.
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="somefilename.pdf"'
buffer = BytesIO()
# Create the PDF object, using the BytesIO object as its "file."
p = canvas.Canvas(buffer)
# Draw things on the PDF. Here's where the PDF generation happens.
# See the ReportLab documentation for the full list of functionality.
p.drawString(100, 100, "Hello world.")
# Close the PDF object cleanly.
p.showPage()
p.save()
# Get the value of the BytesIO buffer and write it to the response.
pdf = buffer.getvalue()
buffer.close()
response.write(pdf)
return response

浙公网安备 33010602011771号