django框架之視圖系統和路由系統
內容回顧:
1. tags
1. for循環
{% for name in name_list %}
{{ name }}
{% endfor %}
{% for name in name_list %}
{{ name }}
{% empty %}
空空如也
{% endfor %}
forloop { }
forloop.counter 當前循環的索引值 從1開始
forloop.counter0 當前循環的索引值 從0開始
forloop.revcounter 當前循環的索引值(倒序) 到1結束
forloop.revcounter0 當前循環的索引值(倒序) 到0結束
forloop.first 單前循環是否是第一次循環 布爾值
forloop.last 單前循環是否是最後一次循環 布爾值
forloop.parentloop 當前循環的外層循環
上臺階,可以一次上1個臺階,可以上2個臺階,可以上3個臺階。問有n個臺階,有多少種走法?
2. if判斷
{% if 條件 %}
操作
{% endif %}
{% if 條件 %}
操作
{% else %}
其他操作
{% endif %}
{% if 條件 %}
操作
{% elif 條件 %}
不同操作
{% else %}
其他操作
{% endif %}
註意事項:
1. 不能連續判斷 a > b > C 用and連接
2. 不支持算數運算 +-*/ 用filter
3. csrf_tokrn
使用:在form表單中使用
效果:添加了一個隱藏的input標簽,標簽名叫csrfmiddlewaretoken 值:隨機字符串
作用:提交POST請求
4. 註釋 {# 註釋部分 #}
2. 母板和繼承
1.為什麽要用母板和繼承:
很多頁面有重復或相似的代碼,減少代碼的重復和修改方便才用母板和繼承
2.具體步驟:
1. 創建一個母板,‘base.html‘ 將多個頁面的重復代碼提取出來
2. 在母板中定義多個block,來區分不同頁面的不同內容
3. 在子頁面中繼承母板 {% extends ‘base.html‘ %}
4. 在block中寫自己頁面獨特的內容
3. 註意事項
1. {% extends ‘base.html‘ %} 寫在第一個行
2. {% extends ‘base.html‘ %} base.html加上引號 不然當做是變量
3. 通常定義多個block,還有一個page-css 和 page-js
3. 組件
將一小部分的HTML代碼寫在一個模板中。———》 組件
在其他的頁面中 {% include ‘nav.html‘ %}
4. 靜態文件的內容
{% load static %}
"{% static ‘css/mycss.css‘ %}" ——》 /static/css/mycss.css
{% get_static_prefix %} —— 》 /static/
"{% get_static_prefix %}css/mycss.css"
5. 自定義simple_tag 和 inclusion_tag
今日內容:
視圖系統
視圖:
一個視圖函數(類),簡稱視圖,是一個簡單的Python 函數(類),它接受Web請求並且返回Web響應。
響應可以是一張網頁的HTML內容,一個重定向,一個404錯誤,一個XML文檔,或者一張圖片。
簡單的說我們通常在views寫的就是視圖。只不過我們寫的是基於函數的視圖,即FBV。把視圖寫成基於類的,就是CBV啦。
這是FBV
def add_publisher(request): new_name = ‘‘ err_msg = ‘‘ if request.method == ‘POST‘: new_name = request.POST.get(‘publisher_name‘) if new_name: publisher_obj_list = models.Publisher.objects.filter(name = new_name) if not publisher_obj_list: models.Publisher.objects.create(name = new_name) return redirect(‘/publisher/‘) else: err_msg= ‘數據已存在‘ else: err_msg = ‘數據不能為空‘ return render(request, ‘add_publisher.html‘, {‘old_name‘: new_name, ‘err_msg‘: err_msg})
改成這樣就是CBV啦:
from django.views import View class Addpublisher(View): def get(self,request): return render(request, ‘add_publisher.html‘) def post(self,request): new_name = request.POST.get(‘publisher_name‘) publisher_obj_list = models.Publisher.objects.filter(name=new_name) if not publisher_obj_list: models.Publisher.objects.create(name=new_name) return redirect(‘/publisher/‘) else: err_msg = ‘數據已存在‘ return render(request, ‘add_publisher.html‘,{‘err_msg‘:err_msg})
往深入的講,他其實是實現了View中的dispatch方法,所以這個dispatch我們可以自己定義:讓他去執行父類的dispatch方法:
from django.views import View class Addpublisher(View): def dispatch(self, request, *args, **kwargs):
print(‘處理請求之前‘) ret = super().dispatch(self, request, *args, **kwargs) print(‘處理請求之後‘)
def get(self,request):
print(‘這是get請求‘) return render(request, ‘add_publisher.html‘) def post(self,request): new_name = request.POST.get(‘publisher_name‘) publisher_obj_list = models.Publisher.objects.filter(name=new_name) if not publisher_obj_list: models.Publisher.objects.create(name=new_name) return redirect(‘/publisher/‘) else: err_msg = ‘數據已存在‘ return render(request, ‘add_publisher.html‘,{‘err_msg‘:err_msg})
其實這個dispatch相當於裝飾器的作用。
簡版的流程:
AddPublisher.as_view() ——》 view 函數
當請求來的時候才執行view
view中執行:
1. 先實例化AddPublisher,給self
2. 執行self.dispatch()
self 有dispatch 就執行自己的
沒有就是執行 父類(View)的dispatch方法
3. dispatch中執行:
先通過反射獲取到AddPublisher中定義get或者post方法。
執行get或者post方法 返回httpresponse對象
4. view接收到dispatch的返回值——httpresponse對象
5. view返回httpresponse對象
接下來我們給視圖加上裝飾器:
裝飾器的作用是在函數之前或者之後做一些操作,並且不改變函數的調用方式。
def wrapper(func): def inner(*args,**kwargs): now = time.time() ret = func(*args,**kwargs) print(‘函數執行的時間是{}‘.format(time.time()-now)) return ret return inner #增加出版社 @wrapper def add_publisher(request): new_name = ‘‘ err_msg = ‘‘ if request.method == ‘POST‘: new_name = request.POST.get(‘publisher_name‘) if new_name: publisher_obj_list = models.Publisher.objects.filter(name = new_name) if not publisher_obj_list: models.Publisher.objects.create(name = new_name) return redirect(‘/publisher/‘) else: err_msg = ‘數據已存在‘ else: err_msg = ‘數據不能為空‘ return render(request, ‘add_publisher.html‘, {‘old_name‘: new_name, ‘err_msg‘: err_msg})
接下來我們給CBV加上裝飾器:
from django.views import View from django.utils.decorators import method_decorator class Addpublisher(View): # def dispatch(self, request, *args, **kwargs): # return super().dispatch(self, request, *args, **kwargs) @method_decorator(wrapper) #為get請求加上裝飾器 def get(self,request): return render(request, ‘add_publisher.html‘)
def post(self,request): new_name = request.POST.get(‘publisher_name‘) publisher_obj_list = models.Publisher.objects.filter(name=new_name) if not publisher_obj_list: models.Publisher.objects.create(name=new_name) return redirect(‘/publisher/‘) else: err_msg = ‘數據已存在‘ return render(request, ‘add_publisher.html‘,{‘err_msg‘:err_msg})
給dispatch方法加上裝飾器,get和post請求就都加上裝飾器了。
如果要在類上加裝飾器,就要這樣寫@method_decorator(wrapper,name=‘get‘)
@method_decorator(wrapper,name=‘post‘)
request對象:
request屬性:
request.method 請求方式 GTE POST
request.GET ——》 字典 URL上傳的參數
request.POST ——》 字典 form表單傳的參數
request.FILES 上傳的文件
request.path_info 返回用戶訪問url,不包括域名
request.body 請求體,byte類型 request.POST的數據就是從body裏面提取到的(gest請求沒有請求體)
我們來寫一個上傳的實例
views中的代碼:
def upload(request): if request.method == ‘POST‘: print(request.FILES) upload_obj = request.FILES[‘file_name‘] with open(upload_obj.name,‘wb‘)as f: for chunk in upload_obj.chunks(): f.write(chunk) return render(request,‘upload.html‘)
html的代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action=""method="post" enctype="multipart/form-data"> {% csrf_token %} <input type="file" name="file_name" > <button>提交</button> </form> </body> </html>
request方法:
request.path_info url路徑不包含url參數和域名
request.get_full_path() url路徑包含url參數
request.get_host() 獲取ip和端口
Response對象
response方法:
1. HttpResponse 類 字符串
2. render 返回一個HTML頁面
3. redirect 跳轉 重定向 Location:/index/
response屬性:
HttpResponse.content:響應內容
HttpResponse.charset:響應內容的編碼
HttpResponse.status_code:響應的狀態碼
JsonResponse對象:
我們先寫一個json數據:
def json_data(request): import json data = {‘name‘:‘alex‘,‘age‘:73} return HttpResponse(json.dumps(data))
然後我們在試一試django為我們提供的方法:
from django.http import JsonResponse def json_data(request): # import json data = {‘name‘:‘alex‘,‘age‘:73} # return HttpResponse(json.dumps(data)) return JsonResponse(data)
我們來說一說這兩者的區別:
如果瀏覽器拿到的是JsonResponse的格式會自動幫你解析。而且JsonResponse只會返回字典,如果要返回列表,JsonResponse(data,safe=False)
路由系統:
基本格式:
urlpatterns = [
url(正則表達式, views視圖,參數,別名),]
urlpatterns = [ url(r‘^articles/2003/$‘, views.special_case_2003), url(r‘^articles/([0-9]{4})/$‘, views.book), url(r‘^articles/([0-9]{4})/([0-9]{2})/$‘, views.book), url(r‘^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$‘, views.book),
]
我們匹配的時候加上小括號就是分組了,他就會將小括號裏的內容以參數的形式傳給views中的函數,而views中的
函數就需要參數來接受。
def book(request,*args): print(args) return HttpResponse(‘o98k‘)
我們把這樣的匹配叫做無名分組
註意事項
- urlpatterns中的元素按照書寫順序從上往下逐一匹配正則表達式,一旦匹配成功則不再繼續。
- 若要從URL中捕獲一個值,只需要在它周圍放置一對圓括號(分組匹配)。
- 不需要添加一個前導的反斜杠,因為每個URL 都有。例如,應該是^articles 而不是 ^/articles。
- 每個正則表達式前面的‘r‘ 是可選的但是建議加上。
分組命名匹配:
在Python的正則表達式中,分組命名正則表達式組的語法是(?P<name>pattern)
,其中name
是組的名稱,pattern
是要匹配的模式。
我們也可以在分組的時候加上名字,比如:
url(r‘^book/(?P<year>[0-9]{4})/[0-9]{2}‘,views.book),
這樣views中的book函數在接受的時候就需要一個同名的參數來接受不然就會報錯。
def book(request,year): print(year) return HttpResponse(‘o98k‘)
這就是命名分組!
一個公司會有不同的業務,也會有不同的app,這樣我們在寫url的時候就可能會有重復。
我們在項目名下的urls.py文件裏可以這樣寫:
from django.conf.urls import url,include from app01 import urls urlpatterns = [ url(r‘app01/‘,include(urls)) ]
剩下的就交給app01來做了。
我們現在app01建一個urls.py文件,在文件中這樣寫:
from django.conf.urls import url from app01 import views urlpatterns = [ url(r‘^book/(?P<year>[0-9]{4})/[0-9]{2}‘,views.book), ]
然後再views的文件中寫book函數:
def book(request,year): print(year) return HttpResponse(‘o98k‘)
同樣我們也可以有多個app文件。
項目名的urls.py文件下這樣寫:
from django.conf.urls import url,include from app01 import urls as app01_urls from app02 import urls as app02_urls urlpatterns = [ url(r‘app01/‘,include(app01_urls)), url(r‘app02/‘,include(app02_urls)) ]
在app01創建一個urls的py文件寫:
from django.conf.urls import url from app01 import views urlpatterns = [ url(r‘^book/(?P<year>[0-9]{4})/[0-9]{2}‘,views.book), ]
在app02創建一個urls的py文件寫:
from django.conf.urls import url from app02 import views urlpatterns = [ url(r‘^book/(?P<year>[0-9]{4})/[0-9]{2}‘,views.book), ]
然後分別寫好app01和app02下的book函數。
命名URL和URL反向解析:
我們在 url後面加上一個名字,name屬性,這樣:
url(r‘^json_data/‘,views.json_data,name=‘data‘),
就可以在views的函數中進行向解析 print(reverse(‘data‘))
具體代碼就是這樣的:
url(r‘^json_data/‘,views.json_data,name=‘josn_data‘),
在views函數中:
def json_data(request): print(reverse(‘ison_data‘)) data = {‘name‘:‘alex‘,‘age‘:73} return JsonResponse(data)
打印出來的結果就是:/json_data/。
在模板裏面可以這樣引用:
{% url ‘jaon_data‘ %}
說的簡單點就是在url起一個名字,然後reverse通過這個名字反向解析拿到url地址。
參數是這樣傳的:
無名傳參:
url(r‘^book/([0-9]{4})/[0-9]{2}‘,views.book,name=‘book‘)
函數中的參數:
reverse(‘book‘,args=(‘1995‘))
模板中的參數:
{%url ‘book‘ ‘1995‘%}
命名傳參:
url(r‘^book/(?<Pmouth>[0-9]{4})/[0-9]{2}‘,views.book,name=‘book‘)
函數中的參數:
reverse(‘book‘,kwargs={‘mouth‘:‘1995‘})
模板中的參數:
{%url ‘book‘ mouth=‘1995‘%}
namespace:
如果說qpp01下和app02下有相同name的url,那麽在視圖中反向解析的時候找的會是誰的url呢?這就需要在項目名下的include裏加一個參數
namespace=‘app01‘,namespace=‘app02‘,然後再視圖中reverse(‘app01:index‘)
reverse(‘app02:index‘)
在模板中也是一樣{%url ‘app01:index‘%}
舉個例子:
project中的urls.py
from django.conf.urls import url, include
urlpatterns = [
url(r‘^app01/‘, include(‘app01.urls‘, namespace=‘app01‘)),
url(r‘^app02/‘, include(‘app02.urls‘, namespace=‘app02‘)),
]
app01中的urls.py
from django.conf.urls import url
from app01 import views
app_name = ‘app01‘
urlpatterns = [
url(r‘^(?P<pk>\d+)/$‘, views.detail, name=‘detail‘)
]
app02中的urls.py
from django.conf.urls import url
from app02 import views
app_name = ‘app02‘
urlpatterns = [
url(r‘^(?P<pk>\d+)/$‘, views.detail, name=‘detail‘)
]
現在,我的兩個app中 url名稱重復了,我反轉URL的時候就可以通過命名空間的名稱得到我當前的URL。
語法:
‘命名空間名稱:URL名稱‘
模板中使用:
{% url ‘app01:detail‘ pk=12 %}
views中的函數中使用
v = reverse(‘app01:detail‘, kwargs={‘pk‘:11})
這樣即使app中URL的命名相同,我也可以反轉得到正確的URL了。
django框架之視圖系統和路由系統