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