柴少的博客 也许终将一事无成,却不能甘于平庸。

Django之FBV、CBV以及路由系统(四)


一、FBV与CBV

1.1 什么是FBV和CBV

FBV   => function base view  基于函数视图  =>  /index/ -->指向函数
CBV  => class base view  基于类视图  =>   /index/--> 指向类

#前面写的那么多例子都是FBV,就不再写例子了。

1.2 CBV

语法:

url.py
       index -> view.类名.as_view()  #这个是固定用法 
view.py
       from django.views import View  #导入View模块,并且继承这个类
       class  类名(View):             
            def get(self,request):  #接收客户端是get请求的,则执行是get方法
                   .... 
            def post(self,request): #接收客户端是post请求的,则执行是post方法
                   ....

1.3 CBV的例子

先来一个最简单的小例子:

urls.py的配置:

url(r'^cbv.html/',views.Cbv.as_view()),

#这里的Cbv是一个class 类,要想使用cbv方法,这个路径后面还得必须有一个as_view()这个是必须的固定格式

views.py的配置:

from django.views.generic import View #这个是导入的模块,原来的django版本从django.views 里面可以直接导入View,但是现在需要加一个generic才可以
class Cbv(View):   #这里必须要继承View这个类,只有继承了这个url那里的as_view()才会有这个方法
    def get(self,request):   #当是get方法就会进入这个函数
        return HttpResponse('cbv-get')
    def post(self,request):   #当是post方法就会进入这个函数
        return HttpResponse('cbv-post')

图片.png

#上面是浏览器的get方式访问返回的结果。注:类要继承 View ,类中函数名必须小写。

稍微再修改一下,整一个post的测试:

urls.py的配置:

url(r'^cbv/',views.Cbv.as_view()),

views.py的配置:

class Cbv(View):
    def get(self,request):
        #return HttpResponse('cbv-get')
        return render(request, 'post.html')
    def post(self,request):
        return HttpResponse('cbv-post')

post.html的设置:

<body>
<form action="/cbv/" method="post">
    <input type="text" name="username">
    <input type="submit" value="提交">
</form>
</body>

图片.png

图片.png

#这里通过查看View的源码,可以看到里面会有很多种提交方法http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']使用ajax的时候这些方法都是可以使用的。

1.4  CBV的原理说明:

那我们知道,它是如何判定是post的方法还是get方法呐?它是通过说明来找的呐?其实它是通过反射来找的。View这个类中封装了一个方法叫dispatch,就是说执行get或者post方法之前肯定先执行dispatch这个方法,我们看看,这个方法里面有啥?

图片.png

这种更具url来匹配方法的是通过反射方法(getattr)来做的。请求过来后先走dispatch这个方法,这个方法存在View类中。

    def dispatch(self, request, *args, **kwargs):        # Try to dispatch to the right method; if a method doesn't exist,
        # defer to the error handler. Also defer to the error handler if the
        # request method isn't on the approved list.
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)        else:
            handler = self.http_method_not_allowed        return handler(request, *args, **kwargs)

所以在执行get的或者post方法之前肯定执行dispatch方法,因为通过这个方法反射找到我的get或者post方法。那如果我们在子类里面重写了dispatch方法呐?

class Home(View):
    #重写dispatch方法
    def dispatch(self, request, *args, **kwargs):
        print("before")
        #调用父类中方法
        handle = super(Home,self).dispatch(request,*args,**kwargs)
        #super() 函数是用于调用父类(超类)的一个方法。super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,
        #会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。
        print("after")
        return handle
 
    def get(self,request):
        print(request.method)
        return render(request,'home.html')
 
    def post(self,request):
        print(request.method,"POST")
        return render(request,'home.html')

既然我先执行的是 dispatch方法,那我重写了,也是先执行dispatch方法,那我就可以在请求get方法或者post方法之后做一些事情,这个功能就像装饰器。dispatch相当于助理一样,既有传递的作用,又有自己的想法。

定制dispatch:
如果需要批量对方法,例如get,post等方法做一些操作的时候,这里我们可以手动写一个dispatch,这个dispatch就和装饰器的效果一样。因为请求来的时候总是先走的dispatch。

from django.views.generic import Viewclass Cbv(View):
        # 重写父类dispatch方法。程序定制时可以使用
        # 父类的dispatch方法,查找get、post等方法,基于反射实现的。
    def dispatch(self, request, *args, **kwargs):
        print('操作前的操作')
        obj = super(Cbv,self).dispatch(request, *args, **kwargs)
        print('操作后的操作代码')        
        return obj   
    def get(self,request):
        # return HttpResponse('cbv-get')
        return render(request,'login.html')   
    def post(self,request):
        return HttpResponse('cbv-post')

这次我们在通过浏览器访问的时候,发现不管get或者post方法,都会走print代码:

图片.png

#此图此例子来自:http://blog.51cto.com/sgk2011/2061658

二、路由系统

官网:https://docs.djangoproject.com/en/2.0/topics/http/urls/

Django请求声明周期:

url里的路径对应views里的视图函数,视图函数对应templates里的html文件,当用户请求时通过路径调用相对应的视图函数,视图函数返回给用户一个字符串,
请求生命周期就是请求先到达url对应关系(匹配) --> 然后到达views的视图函数 --> 返回给用户字符串(或者打开一个HTML文件,读取内容,将内容返回给用户)

Django大致工作流程:

1、客户端发送请求(get/post)经过web服务器、Django中间件、 到达路由分配系统 
2、路由分配系统根据提取request中携带的的url路径(path)与视图函数映射关系列表中,匹配到1个视图函数,foo(request)执行;
3、视图函数 使用原生SQL或者ORM去数据库拿到数据,在服务端进行渲染(模板+数据渲染)
4、视图函数return一个response对象返回客户端

2.1 单一路由对应

urls.py的配置:

url(r'^index',views.index),

图片.png

#从上图可以看到只要是index开头的都会转给views.py里面的index函数。

urls.py稍微修改后的配置:

#url(r'^index',views.index),
url(r'^index$',views.index),   #这就是url以index开头和结尾的

图片.png

图片.png

#可以看到就只能是后缀index才可以,其他的不行了。

当然这样来一下也可以:

urls.py

#url(r'^index',views.index),
#url(r'^index$',views.index),
url(r'^index/',views.index),

views.py的设置:

USER_DICT = {
      '1':{'name':'root1','email':'root@live.com'},
      '2':{'name':'root2','email':'root@live.com'},
      '3':{'name':'root3','email':'root@live.com'},
      '4':{'name':'root4','email':'root@live.com'},
}
USER_LIST = [
    {'name':'root1'},
    {'name':'root2'},
    {'name':'root3'},
    {'name':'root4'},
]
def index(request):
     return  render(request, 'index.html', {'user_dict': USER_DICT,'user_list':USER_LIST})

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <ul>
        {% for k in user_dict.keys %}
          <li>
              {{ k }}
          </li>
        {% endfor %}
    </ul>
    <ul>
        {% for val in user_dict.values %}
          <li>
              {{ val }}
          </li>
        {% endfor %}
    </ul>
    <ul>
        {% for k,val in user_dict.items %}
          <li>
              {{ k }}-{{ val }}
          </li>
        {% endfor %}
    </ul>
    <ul>
        {% for num in user_list %}
          <li>
              {{ num }}-{{ num.name }}
          </li>
        {% endfor %}
    </ul>
</body>
</html>

然后查看一下结果:

图片.png

图片.png

2.2 基于正则的路由

先来简单例子:

urls.py的配置:

url(r'^test/(\d+)',views.test),   #这是匹配test/下面数字组成的url

views.py的配置:

def test(request,num):   #注意这里又传入了一个形参,因为(\d+)需要一个形参传递进来
    return HttpResponse("Haha i am test!The num is: %s" %(num))

测试看一下:

图片.png

图片.png

图片.png

#可以看到浏览器只有是test/数字结尾的url才会有输出,也就是前端浏览器把数字转交给url路由进行批评,匹配成功后会带着客户端传递过来的这个参数值转交给views视图函数。

再来一个抓前端页面nid值得例子:

urls.py设置:

url(r'^detail/',views.detail),

views.py设置:

def detail(request):
    nid = request.GET.get('nid')
    detail_info = USER_DICT[nid]
    return render(request,'detail.html',{'detail_info':detail_info})

detail.html设置:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
   <h1>详细信息</h1>
   <h6>用户名:{{ detail_info.name }}</h6>
   <h6>用户邮箱:{{ detail_info.email }}</h6>
</body>
</html>

图片.png

#不过url:http://127.0.0.1:8000/detail/?nid=4   #这就相当于一个动态url,当然可能想利于seo优化就得用下面的方式了。

把上面的例子稍微改一下:

urls.py:

#url(r'^detail/',views.detail),
url(r'^detail-(\d+).html',views.detail),

views.py:

def detail(request,nid):
    detail_info = USER_DICT[nid]
    return render(request,'detail.html',{'detail_info':detail_info})

index.html:

<body>
    <ul>
        {% for k,val in user_dict.items %}
          <li>
              <a target="_blank" href="/detail-{{ k }}.html">{{ val.name }}</a>
          </li>
        {% endfor %}
    </ul>
</body>

图片.png

图片.png

2.3 按照顺序放置的动态路由

#上面都是只传递了一个参数,这里传递两个参数。

#url(r'^detail-(\d+)-(d+).html',views.detail), #这种不推荐,这是写死的方式,只会把第一个参数传给views里面第一个,第二个参数传递给views函数里面的第二个。

def index(request,nid,uid):   #如这种第一个nid获取第一个(\d+)的值,uid获取第二个(\d+)传过来的值。

2.4 传参形势的路由

url(r'^detail-(?P<nid>\d+)-(?P<uid>\d+).html',views.detail),
#这种是比较灵活的,不管views里面nid和uid位置的设定,利用正则表达式的分组方法,将url以参数的形式传递到函数,可以不按顺序排列。

#正则表达式的分组,相当于一个字典, key=nid, value=\d+。 {"nid":"\d+"}

比如下面的例子:

urls.py:

url(r'^detail-(?P<nid>\d+)-(?P<uid>\d+).html',views.detail),   #这样个\d+就是nid,第二个\d+就是uid

views.py:

def detail(request,uid,nid):   #这里估计将uid和nid颠倒一下位置
    print(nid,uid)
    detail_info = USER_DICT[nid]
    return render(request,'detail.html',{'detail_info':detail_info})

访问url:http://127.0.0.1:8000/detail-2-3.html

#从上面的url可以看出2赋值给了urls里面的nid,3赋值给了urls里面的uid。

图片.png

#从django的后台打印可以看出,虽然views.py里面def detail(request,uid,nid): 虽然颠倒了uid和nid的顺序,但是接受到urls传递过来的参数值确没有随着顺序的颠倒而颠倒。

2.5 对路由映射设置名称

url(r'^indexxxxxx/',views.index,name='indexx'), #给这个url定义一个名称indexx,以后url再发生变化,前台的html页面action哪里就不用跟着进行修改了。

index.html:

<form action="{% url 'indexx' %}" method="POST">

#直接引用名称

图片.png

再加入nid一下:

url(r'^indexxxxxx/(\d+)/',views.index, name='indexx'),   #加入了/数字nid的传递

views.py设置:

def index(request,*args):   #*args简单来说就是类似于一个列表,也就是支持传递多个未知的参数
    return render(request,'index.html',{'user_dict':USER_DICT})

index.html里面的设置:

<form action="{% url 'indexx' 3 %}" method="POST">

#里面的数值只能是一个固定的值,就是url提交到另一个指定的固定的地方

图片.png

如果想搞成访问什么页面就提交什么页面的形式:

views.py里面的设置:

def index(request,nid):
    print(request.path_info)  #这里可以不需要,打印以下,但是这里主要是request.path_info通过这个来获取访问的页面
    return render(request,'index.html',{'user_dict':USER_DICT})

index.html:

<form action="{{ request.path_info }}" method="POST">

#直接调用这个request.path_info

图片.png

图片.png

#对URL路由关系进行命名, ***** 以后可以根据此名称生成自己想要的URL *****

2.6 逆向解析url

urls.py:

url(r'^index/(?P<nid>\d+)/(?P<uid>\d+)/',views.index),
url(r'^indexxxxxx/(?P<nid>\d+)/(?P<uid>\d+)/',views.index, name='indexx'),

views.py:

def index(request,nid,uid):
    #v = reverse('indexx', args=(90,88,)) #也可以这样搞
    v = reverse('indexx', kwargs={"nid":1, 'uid': '99'}) #因为传过来的是两个值,可以这样定义一下
    print(v)
    return render(request,'index.html',{'user_dict':USER_DICT})

index.html:

<form action="{% url 'indexx' nid=1 uid=3 %}" method="POST">

图片.png

图片.png

2.7 URL路由分类,include路由分发(多级路由)

urls.py:

from django.conf.urls import include
urlpatterns = [
    url(r'^cmdb/',include("cmdb.urls")), #分发到cmdb下的urls
    url(r'^openstack/',include("openstack.urls")), #分发到openstack下的urls
]

http://120ni.51niux.com/post/15.html    #创建多app哪里已经举例过了。

作者:chaishaopeng 分类:Django学习 浏览:966 评论:0
留言列表
发表评论
来宾的头像