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')
#上面是浏览器的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>
#这里通过查看View的源码,可以看到里面会有很多种提交方法http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']使用ajax的时候这些方法都是可以使用的。
1.4 CBV的原理说明:
那我们知道,它是如何判定是post的方法还是get方法呐?它是通过说明来找的呐?其实它是通过反射来找的。View这个类中封装了一个方法叫dispatch,就是说执行get或者post方法之前肯定先执行dispatch这个方法,我们看看,这个方法里面有啥?
这种更具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代码:
#此图此例子来自: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),
#从上图可以看到只要是index开头的都会转给views.py里面的index函数。
urls.py稍微修改后的配置:
#url(r'^index',views.index), url(r'^index$',views.index), #这就是url以index开头和结尾的
#可以看到就只能是后缀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>
然后查看一下结果:
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))
测试看一下:
#可以看到浏览器只有是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>
#不过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>
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。
#从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">
#直接引用名称
再加入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提交到另一个指定的固定的地方
如果想搞成访问什么页面就提交什么页面的形式:
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
#对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">
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哪里已经举例过了。