web请求经过路由控制器后,核心处理业务逻辑的地方就是Django的视图系统。视图接收请求对象并处理好业务逻辑后必须要返回一个HttpResponse响应对象

关于Django中视图的具体说明如下:

  1. 一个视图,是一个简单的Python函数或者类,它接受Web请求并且返回Web响应。响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片,任何东西都可以 。
  2. 无论视图本身包含什么逻辑,都要返回响应。
  3. 代码写在哪里也无所谓,只要它在你的Python目录下面。除此之外没有更多的要求了——可以说“没有什么神奇的地方”。
  4. 为了将代码放在某处,约定是将视图放置在项目或应用程序目录中的名为views.py的文件中。

通过上面的介绍大家肯定对视图的用法有了一些简单的认识,接下来着重介绍一下视图中的请求对象响应对象

请求对象

请求对象需要大家记住常用的方法与属性就可以了:

request.body # 得到用户提交数据的原始数据类型(bytes类型的数据) b'username=chao&passowrd=123'
request.method # 请求方式:POST、GET等
request.GET # get请求对象:request.GET.get("name")获取get请求的参数
request.POST # post请求对象
request.path # 请求路径:127.0.0.1:8000/blog/index/打印出来的是/blog/index/
request.path_info # 与path属性获取到的信息一样
request.META  # 获取请求头的元信息
request.META.get('HTTP_USER_AGENT') # 拿到的是浏览器的信息——有些网站的的反爬机制就是做了user_agent的验证
request.META.get('REMOTE_ADDR') # 拿到的是客户端的地址

request.get_full_path() # 获取除域名之外的全路径,包括请求参数:/blog/index/?a=1&b=2
request.is_ajax() # 判断请求是否是ajax请求,建议不用它,Django3已取消了这个方法


响应对象

Django中有三个响应对象:HttpResponserenderredirect

1、HttpResponse

HttpResponse()括号内直接跟一个字符串作为响应体,比较简单直接,所以这里主要介绍后面两种形式

2、render

render方法就是将一个模板页面中的模板语法进行渲染,最终渲染成一个html页面作为响应体。

语法:

return render(request, template_name[, context])

结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。

def index(request):
    import datetime
    n = datetime.datetime.now()
    now = n.strftime('%Y-%m-%d:%X')
    return render(request,'index.html',{'datetime':now})

参数:

(1)request: 用于生成响应的请求对象。

(2)template_name:要使用的模板的完整名称,可选的参数 。

(3)context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。

注意:

(1)浏览器并不认识模板语法,render方法找到index.html文件之后,它先检查里面有没有模板语法,如果有模板语法先将传入的值做替换,将字典的key对应的value值now作为参数传给浏览器,传完参数之后index.html才是一个html文件了,然后render再将html文件那一堆东西传给浏览器,所以浏览最后显示的是now的值。

(2)render将模板语法替换成对应的值——这个过程十分重要,如果index.html文件中有需要“嵌入”的参数,那render的第三个参数得加,如果只是一个“静态页面”,不需要加render的第三个参数。对应的templates文件中的index.html文件的p标签这样写:{{datetime}}注意此时嵌套了模板语法的index.html文件,在django中称为“模板文件”。

3、redirect

重定向这个简单,可以传递一个需要重定向的硬编码的URL:

def my_view(request):
    ...
    return redirect('/some/url/')

当然也可以传递一个完整的URL:

def my_view(request):
    ...
    return redirect('http://example.com/')

重定向常见的状态码:

# 301和302的区别

301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址可以从响应的Location首部中获取(用户看到的效果就是他输入的地址A瞬间变成了另一个地址B)——这是它们的共同点。
他们的不同在于:301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。 

常见重定向的原因:

(1)网站调整(如改变网页目录结构)。

(2)网页被移到一个新地址。

(3)网页扩展名改变(如应用需要把.php改成.Html或.shtml)。 这种情况下,如果不做重定向,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户得到一个404页面错误信息,访问流量白白丧失;再者某些注册了多个域名的网站,也需要通过重定向让访问这些域名的用户自动跳转到主站点等。

FBV与CBV

FBV(function base views) 就是在视图里使用函数处理请求。 之前的例子都是基于FBV模式写的代码。

CBV(class base views)就是在视图里使用类处理请求。Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。 所以Django在后来加入了Class-Based-View。可以让我们用类写View。 这样做的优点主要下面两个方面:

  1. 提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)。
  2. 可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性。

下面主要介绍一下FBV与CBV的区别。

如果我们要写一个处理GET方法的view:

用函数写的话是下面这样:

#路由中:
url('^now$',views.count_now)
#视图函数中:
def count_now(request):
    now_time = time.localtime()
    now_str = time.strftime('%Y-%m-%d %H:%M:%S',now_time)
    if request.method == 'GET':
        return render(request,'now.html',{'now_str':now_str})

如果用类处理的话是这样:

#路由中:
url('^now$',views.MyNow.as_view())
#视图函数中
from django.views import View
class MyNow(View):
    # get方法处理get请求
    def get(self,request):
        now_time = time.localtime()
        now_str = time.strftime('%Y-%m-%d %H:%M:%S',now_time)
        return render(request,'now.html',{'now_str':now_str})

CBV中添加类的属性

方法一:使用Python方法,可以被子类覆盖:

class MyNow(View):

    name = 'wanghw'

    def get(self,request):
        now_time = time.localtime()
        now_str = time.strftime('%Y-%m-%d %H:%M:%S',now_time)
        return render(request,'now.html',{'now_str':now_str,'name':self.name})

方法二:可以在url中指定类的属性

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path('^now/$',views.MyNow.as_view(name='whw')) # 类里面必须有name属性,并且会被传进来的这个属性值给覆盖掉
]

注意:最终name属性的值是url里面传进去的值whw

CBV路由的写法

如果在视图中定义了Index类处理/index/这条路由,那么对应的路由应该这样写:

 re_path('^index/$',views.Index.as_view()) 

CBV中的dispatch方法

在执行Http方法(get、post等)之前会先执行dispatch方法,这个结论一定要记住!

我们可以在Http方法执行之前在dispatch方法中定制一些功能。

#视图函数的写法
from django.views import View
class LoginView(View): #继承View

    # 先执行dispatch方法再执行get或者post方法
    def dispatch(self,request,*args,**kwargs)
        #执行父类的dispatch方法
        ret = super().dispatch(request,*args,**kwargs)
        return ret  #别忘了return值

    def get(self,request): #根据用户的请求方法,找到对应的方法

        return render(request,'login.html')

    def post(self,request):
        print(request.POST)
        uname = request.POST.get('username')
        pwd = request.POST.get('password')
        if uname == 'chao' and pwd == '123':
            return render(request, 'home.html')
        return HttpResponse('用户名或者密码错误')

给FBV视图加装饰器

FBV本身就是一个函数,所以和给普通的函数加装饰器无差

一定要记得,装饰器return func返执行的结果

def wrapper(func):
    def inner(*args,**kwargs):
        print(123)
        ret = func(*args,**kwargs)
        print(456)
        #注意必须return func执行的结果!
        return ret
    return inner

@wrapper
def count_now(request):
    now_time = time.localtime()
    now_str = time.strftime('%Y-%m-%d %H:%M:%S',now_time)
    if request.method == 'GET':
        return render(request,'now.html',{'now_str':now_str})

给CBV视图加装饰器

类中的方法与独立函数不完全相同,因此不能直接将函数装饰器应用于类中的方法 ,我们需要先将其转换为方法装饰器。

Django中提供了method_decorator装饰器用于将函数装饰器转换为方法装饰器。

我们还用上面的装饰器:

def wrapper(func):
    def inner(*args,**kwargs):
        print(123)
        ret = func(*args,**kwargs)
        print(456)
        #注意必须return func执行的结果!
        return ret
    return inner

注意,先引入method_decorator:

from django.utils.decorators import method_decorator

方法一

给get活post方法单独加:

class MyNow(View):
    name = 'wanghw'

    # 单独加装饰器
    @method_decorator(wrapper)
    def get(self,request):
        now_time = time.localtime()
        now_str = time.strftime('%Y-%m-%d %H:%M:%S',now_time)
        return render(request,'now.html',{'now_str':now_str,'name':self.name})

方法二

给dispatch方法(分发方法)加。这样的话相当于给get或post方法都加上了装饰器

class MyNow(View):
    name = 'wanghw'

    # 这样相当于给下面的http方法都加了装饰器
    @method_decorator(wrapper)
    def dispatch(self, request, *args, **kwargs):
        print('before')
        obj = super().dispatch(request,*args,**kwargs)
        print('after')
        #注意要return obj
        return obj

    def get(self,request):
        now_time = time.localtime()
        now_str = time.strftime('%Y-%m-%d %H:%M:%S',now_time)
        return render(request,'now.html',{'now_str':now_str,'name':self.name})

方法三

使用“装饰器嵌套”给类加——name后面的值必须是个字符串。

装饰器叠加:内层装饰器的返回值给了外层

@method_decorator(wrapper,name='get')
@method_decorator(wrapper,name='post')
class MyNow(View):
    name = 'wanghw'

    def get(self,request):
        now_time = time.localtime()
        now_str = time.strftime('%Y-%m-%d %H:%M:%S',now_time)
        return render(request,'now.html',{'now_str':now_str,'name':self.name})

    def post(self,request):
        pass

关于csrf校验的装饰器在CBV模式下的使用

注意:CSRF Token相关装饰器在CBV中只能加到dispatch方法上,或者加在视图类上然后name参数指定为dispatch方法!

csrf校验的两个装饰器为:

csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。

csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。

写法一

from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator


class HomeView(View):

    # 加在dispatch方法上
    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        return super(HomeView, self).dispatch(request, *args, **kwargs)

    def get(self, request):
        return render(request, "home.html")

    def post(self, request):
        print("Home View POST method...")
        return redirect("/index/")

写法二

from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.utils.decorators import method_decorator

# 加在类上,但是name必须指定'dispatch'
@method_decorator(csrf_exempt, name='dispatch')
class HomeView(View):

    def dispatch(self, request, *args, **kwargs):
        return super(HomeView, self).dispatch(request, *args, **kwargs)

    def get(self, request):
        return render(request, "home.html")

    def post(self, request):
        print("Home View POST method...")
        return redirect("/index/")