Django(二) 路由和视图
路由定義
路由是客戶端訪問的url路徑與視圖函數(shù)間的一一映射關(guān)系。Django中的路由關(guān)系在urls.py文件中,基本格式如下:
urlpatterns = [url(regex,view, kwargs=None, name=None), ]參數(shù)說明: 
 regex: 匹配url路徑的正則表達式,比如r'^login/', 匹配以login/開頭的路徑,當你在瀏覽器地址欄輸入http://127.0.0.1/login/時,會調(diào)用login映射的視圖函數(shù)來處理請求。幾點提醒, 
 1. 為了避免轉(zhuǎn)義,正則的前面最好加上r; 
 2. 路徑前默認都有前導(dǎo)的反斜杠/,我們在正則中就不需要再加了,比如r'login/'就可以,而不用r'^/login/'; 
 3. 如果我們訪問127.0.0.1:8000/login,login后未加反斜杠/,將會重定向至127.0.0.1:8000/login/,這時因為默認設(shè)置APPEND_SLASH = True。另外,任何未在setting.py文件中定義的配置都由django.conf.global_settings 提供 
 4. 在進行url路徑匹配時,只要匹配成功一個,就不在向下匹配。 
 view: 映射的視圖函數(shù) 
 name: 可以為視圖函數(shù)定義別名,這個稍后介紹。 
 配置根目錄: 
 我們必須為項目配置根目錄,即當用戶訪問的url不帶路徑時,必須有相應(yīng)的視圖函數(shù)處理并返回響應(yīng),而不是錯誤頁面。加入下面的路由: 
 url(r'^$', view) 當匹配路徑為空,調(diào)用指定的視圖函數(shù)處理。
路徑分組和視圖函數(shù)傳參
分組是正則中的操作,對正則表達式加括號,就可以實現(xiàn)路徑分組。分組的目的是為了給視圖函數(shù)傳參。這里有兩種分組方式,通過位置傳參的簡單分組,和通過關(guān)鍵字傳參的命名分組。我們通過下面的栗子來介紹這兩種分組方式。 
 1.新建一個項目learn, 并創(chuàng)建應(yīng)用calc, 在calc的視圖函數(shù)中定義一個計算加法的函數(shù)add:
2.在項目的urls.py中編輯路由映射: 
 2.1 簡單分組:
2.2 命名分組:
from django.conf.urls import url from django.contrib import admin from calc import views as calc_viewsurlpatterns = [url(r'^admin/', admin.site.urls),url(r'^add/(?P<c>\d+)/(?P<b>\d+)/', calc_views.add),# 通過(?P<...>)命名分組, 將分組名a和b傳給視圖函數(shù)add中的關(guān)鍵字參數(shù)a和b,# 分組名必須和關(guān)鍵字匹配:如果分組名為x,y 而視圖的關(guān)鍵字是a,b,就會報錯。 ]3.在瀏覽器地址欄輸入http://127.0.0.1:8000/add/4/5/,你將看到以下結(jié)果: 
 
4.另外,如果不用分組,可以通過url中的query string查詢字符串來提取數(shù)字,進行計算。 
 因此url需要使用查詢字符串形式: 
 http://127.0.0.1:8000/add/?a=4&b=5 
 urls路由如下:
視圖函數(shù)中提取查詢字符串:
def add(request):a = request.GET.get('a') # get請求中查詢字符串以字典鍵值對的方式封裝在request對象中b = request.GET.get('b')c = int(a) + int(b)return HttpResponse('{a} + {b} = {c}'.format(a=a, b=b, c=str(c)))別名
通過別名還原真實url是很有幫助的,尤其是urls.py路由中的url路徑修改后,如果不使用別名,我們就不得不在所有直接使用了url路徑的地方一一修改。 
 比如,在模板的表單提交中,如果action的路徑寫死了,一旦我們修改了urls.py中的路徑,那么客戶端的表單提交路徑就是無效的,除非我們手動修改action路徑。
通過使用別名,可以避免這種情況發(fā)生。 
 1.定義別名
2.使用別名:{% url 'name' %}
<form action="{% url 'name' %}" method="post">還有以一種情況是,用戶收藏了我們的舊網(wǎng)址127.0.0.1/index/,現(xiàn)在網(wǎng)站的網(wǎng)址變了127.0.0.1/index_new/,用戶如何通過舊網(wǎng)址找到我們的網(wǎng)站。 
 解決方案是為舊網(wǎng)址寫一個跳轉(zhuǎn)函數(shù): 
 1.編輯視圖函數(shù)
2.編輯路由
from django.conf.urls import url from blog import views as blog_views # 從blog應(yīng)用中導(dǎo)入視圖函數(shù)urlpatterns = [url(r'^index_new/$', blog_views.index),url(r'^index/$',blog_views.to_new_index),# 如果url路徑是/index,那么調(diào)用視圖函數(shù)to_new_index, 跳轉(zhuǎn)至新的url ]3.這樣我們在瀏覽中輸入127.0.0.1/index/,將自動跳轉(zhuǎn)到127.0.0.1/index_new/
路由分發(fā)
前面我們都是在項目文件夾的urls.py中通過from app import views這種形式導(dǎo)入視圖函數(shù),然后定義具體路由。應(yīng)該知道的是,項目目錄中的urls.py是全局路由,我們要避免將具體應(yīng)用的路由寫在全局路由中,這樣會造成代碼耦合,而且:1. 當應(yīng)用越來越多,全局路由將越來越大,不利于維護;2.一旦某一條路由崩潰了,將導(dǎo)致整個路由都崩潰。正確的方式是在每個應(yīng)用自己的文件夾中定義自己的路由,全局路由只負責做分發(fā)。下面我們看一下如何實現(xiàn)。 
 1.在應(yīng)用文件夾中新建urls.py,編輯路由規(guī)則
2.在全局路由中作分發(fā)
from django.conf.urls import url, include # 導(dǎo)入include函數(shù) from django.contrib import adminurlpatterns = [url(r'^admin/', admin.site.urls),# include創(chuàng)建路由分發(fā)url(r'^polls/', include('polls.urls')),# 客戶端在訪問時,只要是屬于polls應(yīng)用的路徑,就會被分發(fā)到polls應(yīng)用下的路由 ]注意: 
 1. 路由分發(fā)不能加$結(jié)尾約束, 否則直接終止匹配了,造成分發(fā)失敗。 
 2. 如果要作一個空路徑的默認路由分發(fā),可以通過以下方式:
 這樣,我們在瀏覽器中輸入url:http://127.0.0.1:8000/polls/index/,全局路由匹配到是訪問polls應(yīng)用的,將這條url分發(fā)到polls下的urls.py來處理。 
 后續(xù)創(chuàng)建了新的應(yīng)用,就可以定義各自的路由,然后在全局路由中新增一條分發(fā)就可以了。
反向路由
reverse
reverse函數(shù)可以接收路由中定義的別名或視圖函數(shù)名,反向生成該視圖的url;
導(dǎo)入:from django.shortcuts import reverse
通過別名反向生成:
urlpatterns = [url(r'^index/$',views.index, name='index'), ]url = reverse('index')通過視圖函數(shù)反向生成:
def index(request): return render(request, 'index.html')url = reverse(index)接收簡單分組的參數(shù),args=():
urlpatterns = [url(r'^add/(\d+)/(\d+)/', view.add, name='add'), ]url = reverse(url = reverse('add', args=(2, 9)))接收命名分組的參數(shù),kwargs=():
urlpatterns = [url(r'^add/(?P<a>\d+)/(?P<b>\d+)/', view.add, name='add'), ]url = reverse(url = reverse('add', kwargs=(a=2, b=9)))include路由分發(fā)和namespace
在項目的urls.py中經(jīng)常通過include函數(shù)作路由分發(fā),查看源碼可以發(fā)現(xiàn)include函數(shù)的本質(zhì)返回了一個元組:(urlconf_module, app_name, namespace),urlconf_module是一個個路由組成的列表;還有有一個值是namespace名稱空間,它的作用是在別名重復(fù)時,作區(qū)分用的。因此,如果有名稱空間,那么反向路由時需要加名稱空間,比如namespace=’xxx’, name=’yyy’,那么通過reverse函數(shù)反向路由時,格式為:reverse('xxx:yyy')。如果嵌套了多層名稱空間,需要由外到里用冒號:連接起來。
模板中:{% url %}
在模板中,我們可以通過{% url %}配合別名來反向生成url:
一般形式,通過別名生成:
{% url 'name' %}接收簡單分組參數(shù):
{% url 'name' arg1, arg2 %}接收命名分組的參數(shù):
{% url 'name' arg1=xx, arg2=xx %}視圖函數(shù)
視圖函數(shù)是應(yīng)用的業(yè)務(wù)處理邏輯。它接收wsgi封裝的客戶端請求對象,返回響應(yīng)。
request請求
request請求對象是視圖函數(shù)必須接收的第一個參數(shù)。通過調(diào)用request的屬性/方法,可以獲取請求信息: 
 - request.method 獲取請求方法(GET/POST) 
 - request.GET 字典的形式存放GET請求的數(shù)據(jù),通過val = request.GET.get(key)可以獲取具體的值 
 - request.POST 字典形式存放POST請求的數(shù)據(jù),通過val = request.GET.get(key)可以獲取具體的值 
 - request.META 獲取請求的所有元信息,比如獲取訪問前的地址(執(zhí)行完某些操作,比如登錄后,可利用該地址跳轉(zhuǎn)回去):
- request.FILE 獲取上傳的文件
響應(yīng)方法
- render(request, 'template', {key: value, ... }) 渲染模板,返回HttpResponse響應(yīng)。{key:value}稱為context上下文對象,key對應(yīng)模板中的變量名,value對應(yīng)視圖中的對象,渲染時,將視圖函數(shù)中的對象嵌入模板中。
- HttpResponse(' ') 字符串對象
- redirect('path') 重定向,接收的參數(shù)是路徑。會在響應(yīng)頭中加一個location鍵,客戶端拿到這個鍵對應(yīng)的值,即url, 對url發(fā)起get請求。
注意redirect和render的區(qū)別: 
 1. 客戶端收到redirect重定向響應(yīng)后,會對重定向的url發(fā)起get請求,這時會重走路由映射,調(diào)用視圖函數(shù)。 
 2. 而render是直接渲染html模板,生成html頁面,返回給客戶端。
FBV $ CBV
簡介
路由分為兩種:FBV & CBV
一般我們寫的視圖函數(shù)屬于FBV, 請求過來執(zhí)行視圖函數(shù),函數(shù)式編程;
而CBV,請求過來,執(zhí)行的是views.py 中定義的類,面型對象的編程方式;
urlpatterns = [url(r'^admin/', admin.site.urls),url(r'^index.html$', views.IndexView.as_view()), # CBV# 早先的web服務(wù)就是提供一個靜態(tài)HTML頁面,不涉及ORM和模板渲染,這里寫成這種形式,'^index.html$' 是url偽靜態(tài) ]在CBV路由中,要特別注意類名后加as_view()
自定義CBV:
from django.views import View class IndexView(View): # 自定義的類繼承Viewdef get(self,request,*args,**kwargs):return render(request, 'index.html') def post(self,request,*args,**kwargs):return HttpResponse('ok')請求經(jīng)過路由到達自定義類后,根據(jù)請求方法,來執(zhí)行g(shù)et或post函數(shù),這一點是通過反射完成的。查看源碼可以看到所有可以反射的方法:
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] # 如果是通過Ajax發(fā)送請求,那么支持以上所以方法反射操作是在View類中的dispatch方法中完成的:
def dispatch(self, request, *args, **kwargs):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_allowedreturn handler(request, *args, **kwargs)從以上源碼可以看到,請求方法執(zhí)行結(jié)果最終是作為dispatch方法的返回值,因此上面我們自定義的CBA可以寫成如下形式:
from django.views import View class IndexView(View): # 自定義的類繼承View# 重用父類中的dispatch方法,并加入自定義邏輯,比如用戶驗證def dispatch(self, request, *args, **kwargs):if not request.session.get('user-info'):return redirect('/login.html')return super(IndexView, self).dispatch(request,*args,**kwargs)def get(self,request,*args,**kwargs):return render(request, 'index.html')def post(self,request,*args,**kwargs):return HttpResponse('ok')CBV中使用裝飾器
導(dǎo)入:
from django.utils.decorators import method_decorator三種加裝飾的方式:
假設(shè)定義了一個裝飾器deco:
def deco(func):def wrapper(*args,**kwargs):print('do something')return func(*args,**kwargs)return wrapper加到請求方法上:
class LoginView(View):@method_decorator(deco)def get(self,request):return render(request,'login.html')加到dispatch上:
class LoginView(View):@method_decorator(deco)def dispatch(self, request, *args, **kwargs):return super(LoginView,self).dispatch(request, *args, **kwargs)加到類上:
@method_decorator(deco, name='get') # 指定給get方法加# @method_decorator(deco, name='put')# 必須指定name; 如果有多個方法要裝飾,并列寫class LoginView(View):def get(self,request):return render(request,'login.html')我們知道Django的中間件中做了一個全局的csrf保護,但是有時我們希望具體對待,那么可以導(dǎo)入csrf裝飾器來實現(xiàn)這個需求:
from django.views.decorators.csrf import csrf_exempt,csrf_protect其中csrf_exempt是不應(yīng)用csrf保護;csrf_protect是應(yīng)用csrf保護;我們就可以根據(jù)需要加到具體的視圖上了:
@csrf_protect def demo(request):print('DEMO')return HttpResponse('ok')但是,在目前的Django版本中要注意,如果是CBV,那么csrf裝飾器只能加在類中的dispatch方法上:
class LoginView(View):@method_decorator(csrf_exempt)def dispatch(self, request, *args, **kwargs):return super(LoginView,self).dispatch(request, *args, **kwargs)總結(jié)
以上是生活随笔為你收集整理的Django(二) 路由和视图的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: bnu 4067 美丽的花环
- 下一篇: MSsqlserver服务快速打开和停止
