【Django】 視圖層說明
【Django視圖層】
視圖層的主要工作是銜接HTTP請求,Python程序和HTML模板,使他們能夠有機互相合作從模型層lou到數據並且反饋。說到視圖層的工作就有以下幾個方面要說
■ URL映射
對於一般的,通過django.conf.urls.url設置url路徑,並且關聯視圖函數,甚至把url方法的參數寫成正則表達式從而可以給視圖函數傳遞多個參數的事情就不多說了。比如:
url(r‘^single/([0-9]{4})/([0-9]{2})/([0-9]+)/$‘,views.single)
這個url映射就可以方便地把日期給映射到視圖函數中,讓其根據日期數據得到數據庫中存儲的數據,然後進行界面的渲染。
更高級一點的映射方式是,可以給正則表達式中的子模式添加名字。就像是format函數的{0}和{var_name}一樣,在這裏的正則表達式中可以寫類似於r‘^test/?P<year>([0-9]{4})/?P<month>([0-9]{2})/$‘這樣在視圖函數中就可以加上year=xxxx,month=yy這樣來指定變量名了(當然request參數還是放在第一個且沒有參數名)
include說得好聽一點其實是Django支持分布式URL映射的一種體現。即URL映射規則不一定要寫在一起,可以寫在不同的模塊中,通過include方法來進行統一的輸出。
反向解析也是一個比較重要的點。我們知道在Django的後臺Python代碼中可以調用reverse(url_name)的方法來進行反向解析,然而在模板中我們也常常要使用反向解析。模板中應該寫{% url ‘url_name‘ %}來體現反向解析。反向解析通常還涉及到如何進行帶參數的反向解析,前端可以: {% url ‘url_name‘, args1,args2 %},而後臺可以reverse(‘url_name‘, args=(args1,))這樣。需要註意的是這裏說的參數並不是像flask中url_for函數調用出來的GET請求參數,而是之前URL映射規則正則表達式中的子模式,把它看成一個參數的。
■ 視圖函數
視圖函數一定要有返回,一般可以有三種返回方式,分別是直接構造HTTP的BODY內容、用數據渲染HTML模板、返回錯誤或重定向。
返回HTTP內容用HttpResponse,渲染用render,重定向用redirect或者HttpResponseRedirect,而返回錯誤一般是HttpResponse(status=404)這樣的。為了方便,Django也預定義了一些特別的類讓開發者直接返回,包括:
HttpResponseNotFound 404
HttpResponseRedirect 302
HttpResponseBadRequest 400
HttpResponseForbidden 403
■ 模板語法
Django的模板和Jinja2很類似但不完全一致。
{{}} 和Jinja2一樣都是變量替換,在其中寫上變量名或者方法名
{{|}} 過濾器,將過濾器放到|符號的右邊,在模板層面上對傳遞過來的值進行進一步的修正。
說到過濾器有必要講講Jinja2和Django模板語法的一個很大的差別。Jinja2把模板語法調教得和Python很接近,比如方法都是直接用括號來調用的,但是Django中一般調用方法都不加括號,直接寫個方法名即可。比如前端驗證用戶是否登錄的話用{% if request.user.is_authenticated %}不用加括號。那如果一些方法比如default這個過濾器方法需要值怎麽辦?就這樣: {% value | default:"(N/A)" %}。
過濾器多種多樣,這裏也不想寫了,可以看下書的214頁和官方文檔以了解用法。
流程控制和模板繼承和Jinja2類似的,都是{% for xx xxxx %}{% endfor %}、{% if xxx %}{% endif %}、{% extends "xxx.html" %}這樣。
■ 表單編程
Django自帶了對表單的處理工具使得我們可以方便地構建表單。下面將進一步地說明Django的表單是如何作用的。
Django為所有繼承自Form的類維護了一個bound狀態,當一個表單對象在實例化的時候被賦予過數據則這個表單對象就是被綁定過的,其bound屬性是True。反之當沒有綁定過數據的自然就是unbound狀態的表單。只有unbound狀態的表單可以被賦予數據而只有bound狀態的表單可以來驗證數據。
● 表單數據驗證
狹義上來說,表單數據驗證就是指在服務器端用Python代碼來驗證輸入數據的合法性(不包括前端的表單驗證)。這種意義上的表單驗證分成兩種,分別是字段屬性驗證和自定義邏輯驗證。
字段屬性驗證是指在創建表單類的時候就指定的字段的一些固有屬性,比如CharField會指定max_length屬性等等。這些驗證實際上是HTML對輸入的約束,不涉及到js或者django代碼,而是HTML自帶的一種機制。比如max_length實際上是在<input>標簽中添加了maxlength屬性,其表現是輸入達到最大值之後再敲鍵盤也輸入不進新內容了。不同的瀏覽器對這些約束的體現也不盡相同,總體而言一般簡單的約束就是可以通過字段自帶的一些屬性來進行。
對於一些較復雜的驗證邏輯(比如看輸入中含不含有某些值等等),可以通過自定義邏輯來實現,這需要重載表單類的clean方法,比如:
class Moment(forms.Form): class Meta: model = Moment fields = ‘__all__‘ def clean(self): cleaned_data = super(Moment,self).clean() #先調用一次父類clean方法 content = cleaned_data.get(‘content‘) if content is None: raise ValidationError(‘輸入不能為空‘) elif content.find(‘敏感詞‘) >= 0: raise ValidationError(‘輸入中含有敏感詞‘) return cleaned_data #最終返回驗證完後的數據
從中也可以看到,在驗證的時候對於判斷為驗證通過的不用做什麽特殊處理,而對於驗證不通過的我們要raise forms.ValidationError來提示。如此通過raise一個錯誤來提示確實比較方便,不過有些醜,其效果就是在表單上方添加一個ul.errorlist裏面寫上我們raise出的錯誤信息。而且不帶任何CSS,直接是一個原生的li的風格。。
如果想要改變這種醜醜的,那麽可以考慮1.修改CSS,2.自己加上一些JS來處理。
其實說到自定義表單驗證,其實表單類中重載clean方法究其原理還是把表單數據通過GET方法傳送到後臺讓後臺判斷的。因為這個clean方法的代碼一般是寫在forms.py文件中的,姑且稱是從表單類角度出發的驗證手段吧,另外一種在views.py中也可以寫驗證啊,只不過這些驗證代碼放在這裏的話會讓views.py代碼略顯臃腫一點。但是放在views.py中有一個好處就是我們可以使用django.contrib.messages了。這個組件是類似於flask中的flash消息那樣的東西。比如:
def user_login(request): form = LoginForm() if request.POST: form = loginForm(request.POST) #處理表單數據,然而再此之前可以先做驗證 if form.is_valid(): username = request.POST.get(‘username‘) if ‘frank‘ in username: messages.error(request,‘frank你被禁止登錄了‘) return redirect(reverse(‘psw_login‘)) #user = authenticated(xxxx)一些登錄用戶之類的操作
ctx = {} ctx.update(csrf(request)) ctx[‘form‘] = form return render(request,‘login.html‘,ctx)
順便一提在前端該寫的東西:
{% if messages %} {% for message in messages %} <div class="alert alert-warning"> <button class="close" type="button" data-dismiss="alert">×</button> <h4>{{ message }}</h4> </div> {% endfor %} {% endif %}
這只是個參考,精髓在{{ message }}上面。想咋用咋用。
需要註意的是,在messges.error向前端發送了警告信息之後,後面應該盡量跟return redirect到當前頁面,因為messages和flash消息類似,是一個消息隊列的形式,加入這次pop出去的消息沒有第一時間渲染到頁面上而後來又新加了幾個消息的話,搞不好會在某次渲染的時候渲染出多個消息(當前隊列中的所有消息)。另外把這個return換成raise ValidationError也是不行的,因為這是在views裏面,函數不能沒有返回值。關於messages的更多用法我想有機會在下面繼續詳細地說說。
■ 個性化管理員站點
Django項目都默認添加了/admin這個路徑的管理臺。如果認為管理臺的功能不能滿足當前的需求的話那麽可以通過繼承Django定義的管理員數據模型,模板,站點類來開發出個性化的數據模型管理功能。這部分功能其實就是我們之前那篇文章中提到過的記性類屬性fields,fieldset等的設置。
● ModelAdmin模型
通過繼承django.contrib.admin.ModelAdmin類可以自定義一個數據管理模型,在其中修改一些屬性值來體現出和默認情況下管理臺的一些不同。這部分操作一般在模塊下的models.py文件中進行,ModelAdmin類可以改變的常用屬性有以下這些:
empty_value_display 設置成一個字符串以改變空值的顯示方式
field和exclude 上篇中提到過,通過白/黑名單方式設置一部分供編輯的字段
fieldsets 上篇中提到過,配置字段的分組以美化界面
list_editable 設置字段列表,規定模型中的哪些部分的字段在展示界面就可以編輯,沒有被設置的字段將不能被編輯
list_per_page 整數,指定每頁展示的實例數量最大值,默認值是100
search_fields 設置字段列表,在搜索界面中管理員可以按照這些字段的值來搜索
ordering 設置字段列表,定義管理頁面中模型的排序方式
為了能夠正確地顯示出管理臺界面,這裏還需要提兩點。1.正如之前所說的那樣,在得到重載完成的ModelAdmin之後,需要在模塊的admin.py中進行註冊。不過這裏的註冊和一般models.py中的模型註冊還不太一樣。一般模型就是admin.site.register(模型名)即可。這裏需要admin.site.register(關聯模型名,重載ModelAdmin名)這樣來做,否則會報MediaDefiningClass is not iterable之類的錯誤。 2. 如果啟動應用還是報錯的話請註意留意報錯信息。比如有可能是重載後的ModelAdmin類是一定需要list_display屬性等等。。
● 頁面模板
如果說上面通過重載ModelAdmin類進行的修改只是小打小鬧的話,直接修改頁面的模板可以說是比較徹底的做法了。Django管理臺相關的模板都放在了Django的模塊目錄(Windows的話通常在$PYTHON_HOME\site-packages\django)下的contrib/admin/templates/admin目錄下。這些模板也都是按照Django的模板語法來的,所以可以根據這些模板的結構來重新定制化自己的頁面。自己的頁面模板可以放在templates/admin下,這個admin需要自己創建。
下面以修改login.html為示例進行演示:在項目目錄下創建templates/admin/login.html模板:
{% extends ‘admin/login.html‘ %}
{#content_title這個block是自帶的login.html模板中定義的#}
{% block content_title %}
歡迎來到Django管理界面
{% endblock %}
此時再訪問admin頁面時,在用戶名輸入框的上方就有一個中文的提示語了。
● 其他修改
和ModelAdmin類似的,在django.contrib.admin中還有一個AdminSite類。這個類代表的是整個管理的模塊,可以進行一些設置如set_header等,需要註意的是註冊的時候就不再是簡單的admin.site.register了,而是要像下面這樣:
from django.contrib.admin import AdminSite class MyAdminSite(AdminSite): site_header = ‘我的Django‘ admin_site = MyAdminSite() admin_site.register(xxx,xxx) #site變成了我們自定義的站點類實例,用它來註冊各種模型
另外嘛就是可以把項目的urls.py中的^admin/的admin改成其他的內容來改變管理臺的url。
【Django】 視圖層說明