Django基础三之视图函数
一 Django的视图函数view
一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应。
响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片。
无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你当前项目目录下面。除此之外没有更多的要求了——可以说“没有什么神奇的地方”。为了将代码放在某处,大家约定成俗将视图放置在项目(project)或应用程序(app)目录中的名为views.py
的文件中。
一个简单的视图
下面是一个以HTML文档的形式返回当前日期和时间的视图:
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
html = "It is now %s." % now
return HttpResponse(html)
让我们来逐行解释下上面的代码:
首先,我们从
django.http
模块导入了HttpResponse
类,以及Python的datetime
库。接着,我们定义了
current_datetime
函数。它就是视图函数。每个视图函数都使用HttpRequest
对象作为第一个参数,并且通常称之为request
。注意,视图函数的名称并不重要;不需要用一个统一的命名方式来命名,以便让Django识别它。我们将其命名为
current_datetime
,是因为这个名称能够比较准确地反映出它实现的功能。这个视图会返回一个
HttpResponse
对象,其中包含生成的响应。每个视图函数都负责返回一个HttpResponse
对象。
Django使用请求和响应对象来通过系统传递状态。
当浏览器向服务端请求一个页面时,Django创建一个HttpRequest对象,该对象包含关于请求的元数据。然后,Django加载相应的视图,将这个HttpRequest对象作为第一个参数传递给视图函数。
每个视图负责返回一个HttpResponse对象。视图层,熟练掌握两个对象即可:请求对象(request)和响应对象(HttpResponse)
二 CBV和FBV
FBV(function base views) 就是在视图里使用函数处理请求。
之前都是FBV模式写的代码,所以就不写例子了。
CBV(class base views) 就是在视图里使用类处理请求。
Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。所以Django在后来加入了Class-Based-View。可以让我们用类写View。这样做的优点主要下面两种:
- 提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)
- 可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性
如果我们要写一个处理GET方法的view,用函数写的话是下面这样。
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
如果用class-based view写的话,就是下面这样:
#类写法:
class LoginView(View):
def get(self,request):
print('get方法执行了')
return render(request,'login2.html')
def post(self,request):
username=request.POST.get('username')
password=request.POST.get('password')
print('post方法执行了')
print(username,password)
return HttpResponse('cg')
Django的url是将一个请求分配给可调用的函数的,而不是一个class。针对这个问题,class-based view提供了一个as_view()
静态方法(也就是类方法),调用这个方法,会创建一个类的实例,然后通过实例调用dispatch()
方法,dispatch()
方法会根据request的method的不同调用相应的方法来处理request(如get()
,post()
等)。到这里,这些方法和function-based view差不多了,要接收request,得到一个response返回。如果方法没有定义,会抛出HttpResponseNotAllowed异常。
注意:使用CBV时,urls.py中也做对应的修改::
from django.conf.urls import url,include
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login2/$', LoginView.as_view()),
]
CBV传参,和FBV类似,有名分组,无名分组
url写法:无名分组的
url(r'^cv/(\d{2})/', views.Myd.as_view(),name='cv'),
url(r'^cv/(?P\d{2})/', views.Myd.as_view(name='xxx'),name='cv'),#如果想给类的name属性赋值,前提你的Myd类里面必须有name属性(类属性,定义init方法来接受属性行不通,但是可以自行研究一下,看看如何行通,意义不大),并且之前类里面的name属性的值会被覆盖掉
三 使用Mixin(了解)
我觉得要理解django的class-based-view(以下简称cbv),首先要明白django引入cbv的目的是什么。在django1.3之前,generic view也就是所谓的通用视图,使用的是function-based-view(fbv),亦即基于函数的视图。有人认为fbv比cbv更pythonic,窃以为不然。python的一大重要的特性就是面向对象。而cbv更能体现python的面向对象。cbv是通过class的方式来实现视图方法的。class相对于function,更能利用多态的特定,因此更容易从宏观层面上将项目内的比较通用的功能抽象出来。关于多态,不多解释,有兴趣的同学自己Google。总之可以理解为一个东西具有多种形态(的特性)。cbv的实现原理通过看django的源码就很容易明白,大体就是由url路由到这个cbv之后,通过cbv内部的dispatch方法进行分发,将get请求分发给cbv.get方法处理,将post请求分发给cbv.post方法处理,其他方法类似。怎么利用多态呢?cbv里引入了mixin的概念。Mixin就是写好了的一些基础类,然后通过不同的Mixin组合成为最终想要的类。
所以,理解cbv的基础是,理解Mixin。Django中使用Mixin来重用代码,一个View Class可以继承多个Mixin,但是只能继承一个View(包括View的子类),推荐把View写在最右边,多个Mixin写在左边。
四 给视图加装饰器
使用装饰器装饰FBV
FBV本身就是一个函数,所以和给普通的函数加装饰器无差:
from django.shortcuts import render, HttpResponse, redirect
def wrapper(f):
def innser(*args, **kwargs):
print('执行前')
ret = f(*args, **kwargs)
print('执行后')
return ret
return innser
@wrapper
def index(request):
print('12321')
return render(request, 'index.html')
使用装饰器装饰CBV
类中的方法与独立函数不完全相同,因此不能直接将函数装饰器应用于类中的方法 ,我们需要先将其转换为方法装饰器。
Django中提供了method_decorator装饰器用于将函数装饰器转换为方法装饰器。
def wrapper(f):
def innser(*args, **kwargs):
print('执行前')
ret = f(*args, **kwargs)
print('执行后')
return ret
return innser
from django.views import View
from django.utils.decorators import method_decorator
@method_decorator(wrapper,name='get')
class LoginView(View):
# 使用CBV时要注意,请求过来后会先执行dispatch()这个方法,如果需要批量对具体的请求处理方法,如get,post等做一些操作的时候,这里我们可以手动改写dispatch方法,这个dispatch方法就和在FBV上加装饰器的效果一样。
# @method_decorator(wrapper)
def dispatch(self, request, *args, **kwargs):
# print('之前')
ret=super().dispatch(request, *args, **kwargs)
# print('之后')
return ret
# @method_decorator(wrapper)
def get(self,request):
print('get方法执行了')
return render(request,'login2.html')
# @method_decorator(wrapper)
def post(self,request):
username=request.POST.get('username')
password=request.POST.get('password')
print('post方法执行了')
print(username,password)
return HttpResponse('cg')
另外给cbv添加装饰器的时候(先作为了解):
直接添加在dispatch里面,这样每个函数都会执行
from django.utils.decorators import method_decorator @method_decorator(login_test) def dispatch(self, request, *args, **kwargs): res = super(IndexView, self).dispatch(request, *args, **kwargs) return res
添加在每一个函数中
from django.utils.decorators import method_decorator @method_decorator(login_test) def get(self, request, *args, **kwargs): return render(request, 'index.html')
直接添加在类上,后面的name表示只给get添加装饰器
from django.utils.decorators import method_decorator @method_decorator(login_test, name='get')
get是给get方法加 (以这种方式如果想给多个方法加装饰器,需要写多层装饰器,因为name这个参数的值必须是个字符串,并且不能同时写两个方法)
@method_decorator(login_test, name='post') post是给post方法加 class IndexView(View): def get(self,request): pass
添加装饰器前必须导入from django.utils.decorators import method_decorator
添加装饰器的格式必须为@method_decorator(),括号里面为装饰器的函数名
给类添加是必须声明name
注意csrf-token装饰器的特殊性,在CBV模式下它只能加在dispatch上面(后面再说)
下面这是csrf_token的装饰器:
@csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置csrfToken全局中间件。
@csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
注意:from django.views.decorators.csrf import csrf_exempt,csrf_protect
五 request对象官方文档
请求相关的属性方法(request--HttpRequest对象)
def index(request): #http相关请求信息---封装--HttpRequest对象
if request.method == 'GET':
print(request.body) #获取post请求提交过来的原始数据
print(request.GET) #获取GET请求提交的数据
# print(request.META) # 请求头相关信息,就是一个大字典
print(request.path) #/index/ 路径
print(request.path_info) #/index/ 路径
print(request.get_full_path()) #/index/?username=dazhuang&password=123
return render(request,'index.html')
else:
print(request.body) # b'username=dazhuang'
print(request.POST) #获取POST请求提交的数据
return HttpResponse('男宾三位,拿好手牌!')
六 response对象
与由Django自动创建的HttpRequest对象相比,HttpResponse对象是我们的职责范围了。我们写的每个视图都需要实例化,填充和返回一个HttpResponse。
HttpResponse类位于django.http模块中。
HttpResponse --- 回复字符串的时候来使用
render --- 回复一个html页面的时候使用
redirect -- 重定向
示例:
def login(request):
if request.method == 'GET':
return render(request,'login.html')
else:
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'taibai' and password == 'dsb':
# return render(request,'home.html')
return redirect('/home/') #重定向
else:
return HttpResponse('滚犊子,赶紧去充钱!!!')
#首页
def home(request):
return render(request,'home.html')
HttpResponse.content:响应内容
HttpResponse.charset:响应内容的编码
HttpResponse.status_code:响应的状态码
redirect() :给浏览器了一个30x的状态码
参数可以是:
- 一个模型:将调用模型的
get_absolute_url()
函数
2.一个视图,可以带有参数:将使用urlresolvers.reverse
来反向解析名称
3.一个绝对的或相对的URL,将原封不动的作为重定向的位置。
默认返回一个临时的重定向;传递permanent=True
可以返回一个永久的重定向。
示例:
你可以用多种方式使用redirect()
函数。
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
else:
username = request.POST.get('username')
password = request.POST.get('password')
if username == 'anwen@123' and password == '123':
return redirect('/app01/home/') ##重定向到/app01/home/路径,这也是发送了一个请求,别忘了在上面引入这个redirect类,和render、Httpresponse在一个地方引入
else:
return HttpResponse('登录失败!')
def home(request):
return render(request, 'home.html')
#app01里的 urls.py
from django.conf.urls import url
from app01 import views
urlpatterns=[
url(r'^$',views.login),
url(r'^home/',views.home),
]
上面几个文件搞好之后,我们重启Django项目,然后登陆页面的输入网址,注意,你输入的网址端口要和你启动的django项目的端口一样。
| 一点击提交按钮,你看一下network里面发送了几个请求:两个请求,一个是login请求,一个index请求。 |
扩展阅读:** key两次请求,关于301和302:
1)301和302的区别。
301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取
(用户看到的效果就是他输入的地址A瞬间变成了另一个地址B)——这是它们的共同点。
他们的不同在于。301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;
302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。 SEO302好于301
2)重定向原因:
(1)网站调整(如改变网页目录结构);
(2)网页被移到一个新地址;
(3)网页扩展名改变(如应用需要把.php改成.Html或.shtml)。
这种情况下,如果不做重定向,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户得到一个404页面错误信息,访问流量白白丧失;再者某些注册了多个域名的
网站,也需要通过重定向让访问这些域名的用户自动跳转到主站点等。
临时重定向(响应状态码:302)和永久重定向(响应状态码:301)对普通用户来说是没什么区别的,它主要面向的是搜索引擎的机器人。
A页面临时重定向到B页面,那搜索引擎收录的就是A页面。
A页面永久重定向到B页面,那搜索引擎收录的就是B页面。
用redirect可以解释APPEND_SLASH的用法!