
http://m.survivalescaperooms.com/ganiks/
6. 模板和表單處理1 模板1.1 理解Contexts1.2 模板語言語法1.2.1 模板過濾器1.2.2 標簽Tag1.2.3 Blocks和Extends跳出當前模板和其他模板交互1.2.4 include2 表單2.1 定義表單2.1.1 基于模型的表單2.1.2 保存ModelForm2.1.3 區別于Model2.1.4 繼承表單2.2 填寫表單2.3 驗證和清理2.4 顯示表單2.4.1 顯示全部表單2.4.2 逐個顯示表單2.4.3 Widget2.4.4 重寫一個變量的默認widgetDjango的模板系統絕不僅僅是用于生成HTML,還有 log文件、Email正文、CSV文件等任何文本格式文件。
Django 把傳遞給一個渲染模板的信息稱之為 context, 實質上就是包含鍵值對的類似字典的 Context對象
準備Contexts有兩種方式:
一種是上一篇中用到的,將Contexts作為參數傳遞
render_to_response("person/detail.html", {"person":person})直接將數據作為render_to_response的參數傳遞;
或者在使用通用視圖時把extra_context參數加上
return object_list(request, queryset = Person.objects.filter(last__istartswith=last_name) }第二種方式是通過 context處理器, 這個東東類似中間件,可以定義各種函數,在模板渲染之前來把鍵值對附加到所有context上去。
這也是認證框架這樣的特性,為什么能保證特定的數據在全站范圍里都能訪問到的原因。
def breadcrumb_PRocessor(request): return { 'breadcrumb': request.path.split('/') }通常處理器都放在根目錄的 context_processors.py文件中或是app的目錄中;
另外還需要在 settings.py中激活才可以使用, 通常是在一個叫做TEMPLATE_CONTEXT_PROCESSORS 的元組中, 類似中間件的激活。
跟其他一些非xml的 模板語言如Smarty相比, Django的模板語言沒有要保持 XHTML兼容性的意思,
而是用特殊的字符來把(模板變量以及邏輯命令)和靜態內容(HTML)分開來。
跟其他模板語言一樣,Django模板系統也有 單獨命令 和 模塊級命令 , 分別是{{ variable }}, {% command %}
下面的例子渲染context內容:{"title_text":"My WebPage", "object_list": ["One", "Two", "Three"]}
<html> <head> <title>{{ title_text }}</title> </head> <body> <ul> {% for item in object_list %} <li>{{ item }}</li> {% endfor %} </ul> </body></html>陷阱:Django在模板中輸出context變量的時候,會隱式調用 unicode方法, 所以對象以及其他非字符串變量會被盡量轉換成 Unicode字符串。
如果你要輸出沒有定義 unicode 方法的對象,在模板里是看不到它們的,因為Python表示對象用的是 < > ,正好在html中啥也不是,故不顯示。
>>> print object()<object object at 0x40448>類似 UNIX 管道
if, ifequal, for, endfor, block, include, extends ...
{% ifequal object_list|length 10 %}
{% if object_list|lengthis 10 %}
模塊級命令如 if for , 可以修改它們的局部 context, 很實用
如 for 提供了 {{ forloop }}局部變量, 用法有 forloop.first .last .counter .counter0
跳出當前模板和其他模板交互
通過模板繼承和模板包含來完成代碼的組合和重用
繼承通過 blocks extends 實現;包含通過 include 實現
{% extends %} 必須在模板的頂部調用,并告知渲染引擎這個模板是從一個更高級的模板繼承而來{% blocks %} 是一個模塊級標簽,一個模板中可以定義多個block, 預備讓那些要擴展它的模板去填充的小節舉個例子,三層網站布局
/
/section2/
#base.html<html><head><title>{% block title %}My Web Site{% endblock %}</title></head></html><div id="content">{% block content %}{% endblock %}</div>section1.html{% extends "base.html" %}{% block title %} Section1 {% endblock %}page1.html{% extends "section1.html" %}{% block content %} This Page 1 {% endblock %}page2.html{% extends "section1.html" %}{% block content %} This Page 2 {% endblock %}很簡單,類似php的include,實現代碼重用
http://m.survivalescaperooms.com/ganiks/p/django-template-and-forms.html
Django提供了 forms 庫來把框架里的三個主要的組件聯系在一起:
表單處理的核心類Form 其實跟 Model 看上去是基本相同的, 它們都是處理 變量對象集合, 只是代表的意義不同,一個是Web表單,一個是數據表
有了Form類,我們不光可以輕松處理跟Model模型 100%匹配的表單,還可以隱藏或是省略特定變量,或是把多個model里的變量放在一個表單,或是處理和數據庫存儲毫無關系的表單, 非常靈活。
from django import newforms as formsclass PersonForm(forms.Form): first = forms.CharField(max_length=100, required=True) last = forms.CharField(max_length=100, required=True) middle = forms.CharField(max_length=100)Django允許你使用Form變形 ModelForm 類為任何 Model類或實例取得一個Form子類,
一個ModelForm和普通Form基本一樣,但是包含了一個Meta嵌套類(類似Model里的Meta),內含一個必須的屬性 model
下面重新定義下上面的 Form
from django import newforms as formsfrom mysite.myapp.models import Personclass PersonForm(forms.ModelForm): class Meta: model = Person完美體現了Django的DRY don't repeat yourself 的原則
基于模型創建的表單跟手動生成的表單有一個很重要的區別是, save方法
from mysite.myapp.forms import PersonFormform = PersonForm({'first':'John', 'last':'Doe'})new_person = form.save()你經常會需要在數據,從表單提交后,存儲到數據庫之前,修改它們;
而最好還是在后一個時間段修改,因為這時候你修改的是Python值,而不是POST數據。
為了能讓在save之前修改, save方法提供了一個可選的commit參數(默認是True), 控制你是否真的更新數據庫。
from mysite.myapp.forms import PersonFormform = PersonForm({'first':'John', 'last':'Doe'})new_person = form.save(commit=False)new_person.middle = 'Danger'new_person.save()如果使用 commit=False 來延遲保存的ModelForm包含了相關對象, Django會給表單(不是Model對象的結果)添加一個額外的方法 save_m2m, 讓你正確安排時間的順序。
form = PersonForm(input_including_related_objects)new_person = form.save(commit=False)new_person.middle = 'Danger'new_person.save()form.save_m2m()如果沒有最后的 save_m2m(), related objects會出問題的。
繼承模型的表單是非常方便,但是有時候我們需要排除一些變量,并不是需要所有的變量,不需要重新寫一個Form子類出來,有好幾種方法:
from django import newforms as formsfrom mysite.myapp.models import Personclass PersonForm(forms.ModelForm): class Meta: model = Person exclude = ('middle', )class PersonForm(forms.ModelForm): class Meta: model = Person fields = ('first', 'last')這樣一來,忽略的變量就不會被save方法保存,所以忽略它時要確保DB中的定義是 null=True的,否則會報錯。
有時候,還需要重寫forms層里的Field子類,驗證和顯示特定的變量,如下重新定義了first字段的長度
class PersonForm(forms.ModelForm): first = forms.CharField(max_length=10) class Meta: model = Person關于“關系表單變量” relationship form fields
還記得嗎?當時Model定義外鍵和多對多關系的時候,使用了 limit_choices_to參數, 其實等效的方法可以在表單層變量里自定義一個 queryset 參數, 用來接收一個 特定的 QuerySet 對象, 如下例子Person模型有一個指向其他Person對象的沒有限制的父ForeignKey:
class PersonForm(forms.ModelForm): class Meta: model = Personclass SmithChildForm(forms.ModelForm): parent = forms.ModelChoiceField(queryset=Person.objects.filter(last='Smith')) class = Meta: model = Person跟繼承模型一樣
from django import newforms as formsclass PersonForm(forms.Form): first = forms.CharField() last = forms.CharField()class Person2Form(forms.Form): first = forms.CharField() last = forms.CharField()class AgedPersonForm(PersonForm): age = forms.IntergerField()class MixedPerson(PersonForm, PersonForm2): # mixed extendsDjango的表單庫中有2種表單
表單有一個特性, 可以隨便往表單的數據字典里添加額外的鍵值對, 表單會自動無視那些和它們定義無關的輸入
from mysite.myapp.forms import PersonFormdef process_form(request): post = request.POST.copy() form = PersonForm(post)還可以創建一個雖然未綁定,但是模板打印時載入顯示了初始值的表單, 這個命名構造函數參數 initial 是一個字典
每個表單變量也都有一個類似的參數,允許指定它自己的默認值,不過如果有沖突的話, 表單級的 initial 會覆蓋變量級的
from django import newforms as formsfrom django.shotcuts import render_to_responseclass PersonForm(forms.Form): first = forms.CharField() last = forms.CharField(initial='Smith')#表單定義時,變量級initial參數 middle = forms.CharField()def process_form(request): if not reuqest.POST: form = PersonForm(initial={'first':'John'}) #創建表單實例時, 表單級initial參數,這里如果也定義了last, 則會覆蓋上面的Smith return render_to_response('myapp/form.html', {'form':form})使用實例層initial參數的好處在于它的值可以在表單創建的時候才被構造出來, 這樣一來,你可以引用在表單或是模型定義的時候還不知道的信息,特別是請求對象里的信息,看下面的例子
from django.shotcuts import get_object_or_404, render_to_responsefrom mysite.myapp.models import Person, PersonForm# Views's URL /person/<id>/children/adddef add_relative(request, **kwargs): if not request.POST: relative = get_object_or_404(Person, pk=kwargs['id'] form = PersonForm(initial={'last': relative.last}) return render_to_response('person/form.html', {'form':form})這例子中 parent 的 last 值是后來得到的,通過 initial 賦給form, 孩子自動填寫好父親的姓氏
http://m.survivalescaperooms.com/ganiks/p/django-template-and-forms.html
要讓表單運行驗證程序,可以顯式使用 is_valid 方法
if request.POST: form = PersonForm(request.POST) if form.is_valid: new_person = form.save() ... ...執行完驗證后,表單對象會得到兩個新屬性“之一”:
每個Django表單變量都知道自己在HTML標簽里要怎么顯示, 這種行為是通過 widget 實現的(后面介紹)
first = forms.CharField(max_length=100, required=True)<tr> <th> <label for="id_first">First:</label> </th> <td> <input id="id_first" type="text" name="first" maxlength="100" /> </td></tr>pf = PersonForm(auto_id=False, label_suffix='')<tr> <th> <label>First</label> </th> <td> <input type="text" name="first" maxlength="100" /> </td></tr>pf = PersonForm(auto_id='%s_id', label_suffix='?')<tr> <th> <label for="first_id">First?</label> </th> <td> <input id="first_id" type="text" name="first" maxlength="100" /> </td></tr>打印表單有多重方法
每個Django表單變量都知道自己在HTML標簽里要怎么顯示, 這種行為是通過 widget 實現的, 這個widget子類(比如TextInput)接受了一個 attrs字典,能直接映射到HTML標簽的屬性上去。
middle = forms.CharField(max_length=100, widget=forms.TextInput(attrs={'size':3})<input id="id_middle" maxlength="100" type="text" name="middle" size="3" />來自定義一個widget LargeTextarea, 默認擁有 40行和100列
forms.py
from django import newforms as formsclass LargeTextareaWidget(forms.Textarea): def __init__(self, *args, **kwargs): kwargs.setdefault('attrs', {}).update({'rows':40, 'cols':100}) super(LargeTextareaWidget, self).__init__(*args, **kwargs))這里的 setdefault對字典用到一個技巧,在給定的鍵存在情況下回返回現有的值, 若給定的鍵不存在,則返回提供的值;
用在這里,是確保 kwargs 關鍵字參數字典一定共用 attrs 字典, 不管原來的構造函數參數是什么, 可以 update 來更新attrs字典添加我們需要的默認值。
views.py
from django import newsforms as formsfrom mysite.myapp.forms import LargeTextareaWidgetclass ContentForm(forms.Form): name = forms.CharField() markup = forms.ChoiceField(choices=[('markdown', 'Markdown'),('textile', 'Textile')]) text = forms.Textarea(widget=LargeTextareaWidget)是不是還有優化空間?
Django就是Python, 有需要的時候, 很容易把各種類和對象替換出去, 從而達到更靈活的自定義。
forms.py
from django import newforms as formsclass LargeTextareaWidget(forms.Textarea): def __init__(self, *args, **kwargs): kwargs.setdefault('attrs', {}).update({'rows':40, 'cols':100}) super(LargeTextareaWidget, self).__init__(*args, **kwargs))class LargeTextarea(forms.Field): widget = LargeTextareaWidget現在可以更方便的使用這個自定義的widget
views.py
from django import newsforms as formsfrom mysite.myapp.forms import LargeTextareaWidgetclass ContentForm(forms.Form): name = forms.CharField() markup = forms.ChoiceField(choices=[('markdown', 'Markdown'),('textile', 'Textile')]) #text = forms.Textarea(widget=LargeTextareaWidget) text = LargeTextarea()新聞熱點
疑難解答