第03章 视图层

视图基础

本章要求

  • urlconfs
  • 视图函数
  • 快捷
  • 装饰器

视图

视图一般都写在app的view.py中。并且视图的第一个参数永远都是request(一个HttpRequest对象)对象。这个对象存储了请求过来的所有信息,包括携带的参数刀诼一些头部贪睡等。在视图中,一般是完成逻辑相关操作。比如这个请求是添加一篇博客,那么可以通过request来接收这些数据,然后存储到数据库中,最后再把执行的结果返回给浏览器(体现在python中,这些代码就是python的函数)。视图函数的反回结果必须是HttpResponse对象或者子类的对象。

示例代码:

  1. from django.http import HttpResponse
  2. def book_list(request):
  3. return HttpResponse("书籍列表")

urls模块化

如果项目变得越来越大时,url会以变得越来越多。这里应该使用模块化设计,把每个app自己的相关url要放到自己的app中管理。一般我们会在app中新建一个urls.py文件来存储所有和这个app相关的子url


应该使用include函数包含子urls.py,并且这个urls.py的路径使用相对于项目的路径。

  1. urlpatterns = [
  2. path('admin/', admins.site.urls),
  3. path('book', include('book.urls')),
  4. ]

app/urls.py中,所有的url匹配也要放在urlpatterns变量中,否则找不到。

url命名

为什么需要url命名

罚历url是经常变化的,为了解决在视图或者模板中写死url的问题,需要使用url命名 来实现url重定向或者url反转 来找到url。

如何给一个url指定名称,在path函数中使用参数name指定url的名字

  1. urlpatterns = [
  2. path('admin/', admins.site.urls),
  3. path('login/', views.login, name='login'),
  4. ]

应用命名空间和实例命名空间

应用命名空间

多个应用app使用了相同的url命名时,会引起混乱,这里需要给每个应用app也起个名字。

通常,应用程序名称空间应该由包含的模块指定。如果设置了应用程序名称空间,则namespace可以使用该参数来设置不同的实例名称空间。

  1. # book
  2. # 应用命名空间的变量叫做 app_name
  3. app_name='user'
  4. urlpatterns = [
  5. path('admin/', admins.site.urls),
  6. path('login/', views.login, name='login'),
  7. ]
  8. #重定向或者反转时
  9. reverse("user:login")

实例命名空间

实例开发中,可能会这样设计,同一个app下有多个实例。可以使用多个url映射同一个app。所以这就会产生一个问题。以后在做反转时,如果使用应用命名字音,那么就会发生混乱。为了避免这个瓿。我们可以使用实例命名空间

  1. # urls.py
  2. urlpatterns = [
  3. path('admin/', admins.site.urls),
  4. # 同一个app下有两个实例,
  5. path('cms1/', include('cms.urls')),
  6. path('cms2/', include('cms.urls')),
  7. ]
  8. # views.py
  9. def index(reqeust):
  10. username= request.GET.get('username')
  11. if username:
  12. return HttpResponse('cms首页')
  13. else:
  14. # 访问同一个app时,url会在两个实例间跳转
  15. return redirect(reverse("cms:login")

解决方式:

使用include()的参数namespace参数来指定实例命名空间

  1. # urls.py
  2. urlpatterns = [
  3. path('admin/', admins.site.urls),
  4. # 同一个app下有两个实例,访问同一个app时,url会在两个实例间跳转
  5. path('cms1/', include('cms.urls', namespace='cms1')),
  6. path('cms2/', include('cms.urls', namespace='cms2')),
  7. ]
  8. # cms/views.py
  9. def index(reqeust):
  10. username= request.GET.get('username')
  11. if username:
  12. return HttpResponse('cms首页')
  13. else:
  14. # 访问同一个app时,url会在两个实例间跳转
  15. current_namespace = request.resolver_match.namespace
  16. return redirect(reverse("%s:login" % current_namespace)

注意:如果没有指定应用名字空间app_name,那么使用实例命名空间会报错

urlpatterns = [
path(‘admin/‘, admins.site.urls),
# 没有指定应用命名空间,直接指定的是实例命名空间
path(‘cms1/‘, include(‘cms.urls’, namespace=’cms1’)),

  1. #### include
  2. [https://docs.djangoproject.com/en/2.0/ref/urls/#include](https://docs.djangoproject.com/en/2.0/ref/urls/#include)
  3. [https://docs.djangoproject.com/en/2.0/topics/http/urls/#namespaces-and-include](https://docs.djangoproject.com/en/2.0/topics/http/urls/#namespaces-and-include)
  4. -
  5. `include(module,namespace = None)`
  6. -
  7. `include(pattern_list)`
  8. -
  9. `include((pattern_list,app_namespace),namespace = None)`
  10. 一个函数,它将一个完整的Python导入路径带到另一个应该“包含”在这个地方的URLconf模块。(可选)也可以指定条目将包含到的[应用程序名称空间]和[实例名称空间]。
  11. 通常,应用程序名称空间应该由包含的模块指定。如果设置了应用程序名称空间,则`namespace`可以使用该参数来设置不同的实例名称空间。
  12. `include()` 也接受作为参数的一个返回URL模式的迭代器或包含这种迭代器的2元组加上应用程序命名空间的名称。参数:
  13. - **module** - URLconf模块(或模块名称)
  14. - **namespace** - 包含URL条目的实例名称空间
  15. - **pattern_list** - 可重用的`path()`和/或`re_path()`实例。
  16. - **app_namespace** - 包含的URL条目的应用程序名称空间
  17. ####URL分发器
  18. django允许你自由地设计你的URL,不受框架束缚。你可以使用python模块`URLconf`来设计URL,它是URLpython函数的映射。
  19. ```python
  20. from django.urls import path
  21. from . import views
  22. urlpatterns = [
  23. path('articles/2003/', views.special_case_2003),
  24. path('articles/<int:year>/', views.year_archive),
  25. path('articles/<int:year>/<int:month>/', views.month_archive),
  26. path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
  27. ]

Django 如何处理一个请求

当一个用户请求Django的一个页面时,下面是django系统交定执行哪个python代码的算法:

  1. 检查根URLconf模块(由项目/settings.py中的设置值:ROOT_URLCONF指定)
  2. 检查ROOT_URLCONF指定模块中的变量urlpatterns=[]。内容为django.urls.path()和/或者django.urls.re_path()的实例。
  3. 依次匹配每个URL模式,在与请求的URL匹配的第一个模式停下来。
  4. 一旦匹配到URL,Django就导入并调用指定的视图,该视图是一个简单的python函数(或者基于类的视图)。该视图通过以下能参数传递:
    1. 一个HttpRequest实例
    2. 如果匹配的URL模式没有返回任何命名组,则来自正则表达的匹配作为位置参数提供
    3. 关键字参数由路径表达式匹配的任何命名部分组成,并由可选KWARGS参数
  5. 如果没有URL匹配,或者发生异常,Django将调用适当的错误片是视图。

QueryDict对象

我们平时的request.GETrequest.POST都是QueryDict对象,这个对象继承自dict,因此用法跟dict相差无几。其中胜利比较多的是get()getlist()方法。

  1. get():用来获取指定key值,如果没有这个key,那么会返回None
  2. getlist(): 如果浏览器上传上来的key对应的值有多个,那么就需要通过这个方法获取。

限制请求method

限制请求装饰器

django内置的视图装饰器可以给视图提供一些限制。以下介绍一些常用的内置视图装饰器


django.views.decorators.http.require_http_methods:这个装饰器需要传递一个允许访问的方法列表。比如只能通过GET访问,示例如下

  1. from django.views.decorators.http import require_http_methods
  2. @require_http_method(['GET'])
  3. def my_view(request):
  4. pass

django.views.decorators.http.require_GET:这个装饰器相当于require_http_methods(['GET'])的简写。

重定向

  • 永久性重定向301
  • 临时性重定向302
  1. from django.shortcuts import reverse, redirect
  2. def profile(request):
  3. if request.GET.get("username"):
  4. return HttpResponse("个人中心页面")
  5. else:
  6. return redirect(reverse("user:login"))

WSGIRequest对象

django在接收到http请求后,会根据http请求携带的参数以及报文信息创建一个WSGIRequest对象,并且作为视图函数第一个参数传给视图函数。也就是我们经常看到的request参数。在这个对象上我们可以找到客户端上传上来的所有信息。这个对象的完整路径是django.core.handlers.wsgi.WSGIRequest

WSGIRequest对象常用属性和方法:

WSGIRequest对象上大部分的属性都是只读的。因为这些属性是从客户端上传上来的,没有必要做修改。以下将对一些常用的属性进行说明:

  1. path:请求服务器完整”路径“,但不包含域名和参数
  2. method:当前请求的 http method
  3. GET: 一个django.http.request.QueryDict对象。类似dict,包含了所有参数
  4. POST:一个django.http.request.QueryDict对象。包含post参数
  5. FILES:一个django.http.request.QueryDict对象。
  6. COOKIES:一个标准的python字段,包含所有的cokkie键值对,都是str类型
  7. session:类似于dict,用来操作session
  8. META:存储的客户端发送上来的所有header信息
    • CONTENT_LENGTH:请求的正文的长度(是一个字符串)。
    • CONTENT_TYPE:请求的正文的MIME类型。
    • HTTP_ACCEPT:响应可接收的Content-Type。
    • HTTP_ACCEPT_ENCODING:响应可接收的编码。
    • HTTP_ACCEPT_LANGUAGE: 响应可接收的语言。
    • HTTP_HOST:客户端发送的HOST值。
    • HTTP_REFERER:在访问这个页面上一个页面的url。
    • QUERY_STRING:单个字符串形式的查询字符串(未解析过的形式)。
    • REMOTE_ADDR:客户端的IP地址。
    • REMOTE_HOST:客户端的主机名。
    • REQUEST_METHOD:请求方法。一个字符串类似于GET或者POST
    • SERVER_NAME:服务器域名。
    • SERVER_PORT:服务器端口号,是一个字符串类型。

WSGIRequest对象常用方法:

  1. is_secure():是否是采用https协议。
  2. is_ajax():是否采用ajax发送的请求。原理就是判断请求头中是否存在XMLHttpRequest
  3. get_host():服务器的域名。如果在访问的时候还有端口号,那么会加上端口号。比如www.baidu.com:9000
  4. get_full_path():返回完整的path。如果有查询字符串,还会加上查询字符串。比如/music/bands/?print=True

示例代码:

  1. """ baidu request headers
  2. Upgrade-Insecure-Requests: 1
  3. User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36
  4. X-DevTools-Emulate-Network-Conditions-Client-Id: E7FB183C37ED2035C4C94AD4A29CFF72
  5. """

QueryDit对象

request.GET.get()使用

  1. username = request.GET['username']
  2. username = reqeust.GET.get('username')
  3. username = request.GET.get('username', default='guest')
  4. page = reqeust.GET.get('page', default=1)

request.GET.getlist()使用

  1. tags = request.POST.getlist('tags')

HttpResponse对象

django服务器接收到客户端发送过来的请求后,会将提交上来的这些数据封装为一个 HttpRequest对象传给视图函数。那些视图函数在片完相关的逻辑后,也需要返回一个响应给浏览器。而这个响应,我们必须返回HttpResponseBase或者他的子类的对象。

HttpResponse则是HttpResponseBase用得最多的子类。下面介绍

常用属性:

  1. content:返回的内容
  2. status_code:返回的状态码
  3. content_type:返回内容的MIME类型,默认为text/html。常用的如下:
    • text/html
    • text/plain
    • text/css
    • text/javascript
    • multipart/form-data:文件提交‘
    • application/json:json传输
    • application/xml
  4. 设置请求头:response['X-Access-Token']='xxxxx'

常用方法:

  1. set_cookie
  2. delete_cookie
  3. writeHttpResponse是一个类似于文件的对象,可以用来写入数据到数据体中

示例代码

  1. from django.http import HttpResponse
  2. """ baidu Response Headers
  3. Bdpagetype: 2
  4. Bdqid: 0xacb0ff4b00074fd9
  5. Cache-Control: private
  6. Content-Encoding: gzip
  7. Content-Type: text/html;charset=utf-8
  8. Date: Tue, 01 May 2018 04:52:42 GMT
  9. Expires: Tue, 01 May 2018 04:52:42 GMT
  10. Server: BWS/1.1
  11. X-Ua-Compatible: IE=Edge,chrome=1
  12. """
  13. def index(request):
  14. # response = HttpResponse()
  15. # response.content = '首页'
  16. response = HttpResponse('首页', content_type='text/plain;charset=utf-8') # 效果同上
  17. # response.status_code = 200
  18. # response['PASSWORD'] = '88888888'
  19. return response

类视图

在写视图的时候,django除了使用函数作为视图,也可以使用类作为视图。使用类视图可以使用类的一些特性,比如继承等。

View

django.views.generic.base.View是主要的类视图,所有的类视图都是继承自他。如果我们写自己的类视图,也可以继承自他。然后再根据当前请求的method,来实现不同的方法。

使用类视图一般分两步:

  1. 定义类视图
  2. 映射类视图

下面举例定义一个视图只能使用get的方式来请求:

  1. from django.views import View
  2. # 定义类视图
  3. clas BookDetailView(View):
  4. def get(sekf, request, *args, **kwargs):
  5. return rencer(request, 'detail.html')

定义好类视图后,还应该在urls.py中进行映射,映射的时候就需要调用View的类方法as_view()来进行转换。

  1. urlpatterns = [
  2. path("detail/<book_id>/", views.BookDetailView.as_view(), name='detail'),
  3. ]

除了get方法,View还支持以下方法['get', 'post', 'put', 'path', 'delete', 'head', 'options', 'trace']

如果用户访问了View没有定义的方法。比如你的类视图只支持get方法,而出现了post方法,那么就会把这个请求转发给http_method_not_allowed(request, *args, **kwargs)

  1. from django.views import View
  2. clas BookDetailView(View):
  3. def get(sekf, request, *args, **kwargs):
  4. return HttpResponse("书籍详情页面")
  5. def http_method_not_allow(self,request, *args, **kwargs):
  6. return HttpResponse("您当前采用的method是:%s,本视图只支持使用post请求!" % request.method)

其实不管是get还是post请求,都会走dispatch(request, *args, **kwargs)方法,所以如果实现了这个方法,将能够对所有请求都处理到。

JsonResponse类

用来对象dump成json字符串,然后返回将json字符串封装成Response对象返回给浏览器。并且他的Content-Typeapplication/json。示例:

  1. import json
  2. from django.http import HttpResponse, JsonResponse
  3. # 传统方式 - 返回 json
  4. def josnresponse_view(request):
  5. person = {
  6. 'username': 'wangdachui',
  7. 'age': 18,
  8. 'height': 150,
  9. }
  10. person_str = json.dumps(person)
  11. response = HttpResponse(person_str, content_type='application/json')
  12. return response
  13. # django 封装方式 - 默认内容为 python dict
  14. def josnresponse_view(request):
  15. person = {
  16. 'username': 'wangdachui',
  17. 'age': 18,
  18. 'height': 150,
  19. }
  20. response = JsonResponse(person) # 封装了
  21. return response
  22. # django 封装方式 - 指定参数,传递非dict内容
  23. # in order to allow non-dict objects to be serialized set the safe parameter to false
  24. def josnresponse_view(request):
  25. person = [
  26. {
  27. 'username': 'wangdachui',
  28. 'age': 18,
  29. 'height': 150,
  30. },
  31. {
  32. 'username': 'xiaoming',
  33. 'age': 22,
  34. 'height': 120,
  35. },
  36. ]
  37. response = JsonResponse(person, safe=False) # 传递非dict内容
  38. return response

csv文件

生成csv文件

  1. from django.http import HttpResponse
  2. from django.template import loader
  3. import csv
  4. # 方式1 -手工定义csv
  5. def index(request):
  6. response = HttpResponse(content_type="text/csv")
  7. response['Content-Disposition'] = "attachment;filename='person.csv'"
  8. # with open('temp.csv', 'w') as fp:
  9. # csv.writer(fp)
  10. writer = csv.writer(response)
  11. writer.writerow(['username', 'age'])
  12. writer.wirterow(['wangdachui', 18])
  13. reture response
  14. # 方式2 - 使用template生成
  15. def template_csv_view(request):
  16. response = HttpResponse(content_type="text/csv")
  17. response['Content-Disposition'] = "attachment;filename='person.csv'"
  18. context = { # 定义 csv 内容
  19. 'rows':[
  20. ['username', 'age'],
  21. ['wangdachui', 18],
  22. ]
  23. }
  24. template = loader.get_template('template_test.txt') # 定义 csv 模板
  25. response.content = template.render(context) # 使用模板渲染csv
  26. return response
  27. # vim template_test.txt
  28. {% for row in rows %}
  29. {{ row.0 }}, {{ row.1 }}
  30. {% endfor %}

关于StreamingHttpResponse

这个类是专门用来处理流数据的。使用在处理一些大型文件的时候,不会因为服务器处理时间垞而到时连接超时。这个类还是继承自HttpResponse,并且中HttpResponse对比有以下几点区别:

  1. 这个类没有属性content,相反是streaming_content
  2. 这个类的streaming_content必须是一个可以迭代的对象。
  3. 这个类没有write()方法,如果给这个类的对象写入数据将会报错。

注意:StreamingHttpResponse会启动一个进程来和客户央保持长连接,所以会很消耗资源。所以如果不是

特殊要求,尽量少用这种方法。

  1. from django.http import HttpResponse, StreamingHttpResponse
  2. import csv
  3. def large_csv_view(request):
  4. response = StreamingHttpResponse(content_type='text/csv')
  5. response['Content-Disposition'] = "attachment;filename='large.csv'"
  6. response.streaming_content = ('useranme, age\n', 'xiongda, 18\n') # 要求是可迭代对象
  7. return response
  8. def large_csv_view(request):
  9. response = StreamingHttpResponse(content_type='text/csv')
  10. response['Content-Disposition'] = "attachment;filename='large.csv'"
  11. rows = ( "Row {}, {}\n".format(row, row) for row in range(0, 65535)
  12. response.streaming_content = rows # 要求是可迭代对象
  13. return response

TemplateView

功能:django.views.generic.base.TemplateView,这个类视图是专门用来返回模版的。

在这个类中,有两个属性是经常需要用到的,


template-name,这个属性是用来存储模版的路径,Templateview会自动的渲染这个变量指向的模版。


get_context_data,这个方法是用来返回上下文数据的,也就是在给模版传的参数的。

示例代码如下:

  1. from django.views.generic.base import Templateview
  2. class HomePageView(TemplateView):
  3. template_name="home.html"
  4. def get_context_data(self,**kwargs):
  5. context=super().get_context_data(**kwargs)
  6. context['username']="小米"
  7. return context

ListView

功能:在网站开发中,经常会出现需要列出某个表中的一些数据作为列表展示出来。比如文章列表。在django中可以使用ListView来帮我们快速实现。

  1. class ArticleListView(Listview):
  2. model=Article
  3. template_name='article_list.html'
  4. paginate_by=10
  5. context_object_name='articles'
  6. ordering='create_time'
  7. page_kwarg='page'
  8. def get_context_data(self, **kwargs):
  9. context=super(ArticlelistView, self).get_context_data(**kwargs)
  10. print(context)
  11. return context
  12. def get_queryset(self):
  13. return Article.objects.filter(id_lte=89)