<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                {% raw %} 第七章-表單 *********** 這一章我們會討論一下話題: ? 表單的工作流程 ? 不可靠的輸入 ? 表單處理類視圖 ? 表單與CRUD視圖 我們把Django表單放到一邊,來討論下常規情況下的表單是個什么樣子。表單不僅長,而且有著多個需要填充的無趣的頁面。可以說表單無所不在。我們每天都用到它。表單支撐了谷歌搜索框到臉書的點贊按鈕這所有的一切。 Django把使用表單時產生的驗證和描述這類的大量繁重工作給抽象了。它也實現了多種的安全問題的最佳實踐。可是,表單在處理自身多個狀態之一時也是令人困惑的起因。 ## 表單的工作原理 表單理解起來比較困難,因為它同不止一個請求-響應循環交互。最簡單的場景是,你需要展示一個空表單,然后用戶來正確地填充和提交表單。另外一種情況是它們輸入一些無效的數據,表單需要重復的提交知道整個表單有效為止。 因此,表單表現出多種狀態: - **空表單:**在Django中此表單稱為未綁定表單 - **已填充表單:**Django中該表單稱為已綁定表單 - **有錯誤的已提交表單:**該表單稱做已綁定表單,但不是有效表單 - **沒有錯誤的已提交表單:**該表單稱做已綁定且有效的表單 注意用戶永遠不會見到表單的最后狀態。他們不必如此。提交的有效表單應當把用戶帶到表單提交成功之后的頁面。 ## Django中的表單 通過總結它們到一個層次,Django的`form`類每個字段的狀態,以及表單自身。表單擁有兩個重要的狀態屬性,一如下面所示: - *is_bound*: 如果返回值為假,則它是一個未綁定的表單,即,新的空表單,或者默認的字段值。如果返回值為真,表單被綁定,即,至少有一個用戶設置的字段。 - *is_valid()*: 如果返回值為真,已綁定表單中的所有字段都擁有有效的數據。如果返回假,至少有一個字段中存在一部分無效數據,或者表單未被綁定。 舉例來說,想象一下你需要一個接受用戶名字和年齡的簡單表單。這個類可以這樣來定義: ```python # forms.py from django import forms class PersonDetailsForm(forms.Form): name = forms.CharField(max_length=100) age = forms.IntegerField() ``` 該類可以以綁定或者不綁定方式來初始化,一如下面代碼所示: ```python >>> f = PersonDetailsForm() >>> print(f.as_p()) <p><label for="id_name">Name:</label> <input id="id_name" maxlength="100" name="name" type="text" /></p> <p><label for="id_age">Age:</label> <input id="id_age" name="age" type="number" /></p> >>> f.is_bound False >>> g = PersonDetailsForm({"name": "Blitz", "age": "30"}) >>> print(g.as_p()) <p><label for="id_name">Name:</label> <input id="id_name" maxlength="100" name="name" type="text" value="Blitz" /></p> <p><label for="id_age">Age:</label> <input id="id_age" name="age" type="number" value="30" /></p> >>> g.is_bound True ``` 要注意HTML是如何表現改變以包括它們中已綁定數據的值屬性。 表單可以只在你創建表單對象時才被綁定,即,在構造器中。用戶如何在類字典對象的最后面輸入每個表單字段的值? 要想解決這個問題,你需要理解用戶是如何與表單交互的。在下面的的圖表中,用戶打開用戶賬戶表單,首先是正確地填充,并提交它,然后用有效信息重新提交表單: ![2015-06-15 21 44 02](https://cloud.githubusercontent.com/assets/10941075/8161368/0c1fea46-13a9-11e5-9f29-036bd22f2d5a.png) 如前面的表單所示,當用戶提交表單時,在`request.POST`(它是一個`QueryDict`的實例)內部所有可調用的視圖獲取到全部的表單數據。表單使用類字典對象——以這種方式引用是因為它的行為類似于字典,來初始,并擁有一點額外的功能。 表單可以通過兩種不同的方式來定義以發送數據表單:`GET`或者`POST`。表單使用`METHOD=“GET”`定義以發送以URL編碼的表單數據,例如,當你提交谷歌搜索時,URL取得表單輸入,即,搜索字符串顯式地嵌入,比如`?q=Cat+Pictures`就是如此。`GET`方法用來冪等表單,它不會對世界狀態做出任何的最新改變。(不要太過于迂腐,多次處理有同樣的效果就像一次處理)。大多數情況下,這意味著它只在重新取回數據時被用到。 不過,不計其數的表單都是使用`METHOD=“POST”`來定義的。這樣,表單數據會一直發送HTTP請求的主體部分,而且它們對于用戶來說是不可見的。它們被用于任何涉及到邊際效應的事情,比如存儲或者更新數據。 視你所定義表單類型的不同,當用戶提交表單時,視圖會重新取回`request.GET`或者`request.POST`中的表單數據。如同早前咱么提到的那樣,它們中的哪一個都類似于字典。因此,你可以傳遞它到表單類構造器以獲取綁定的`form`對象。 >#### 注釋 **The Breach** Steve was curled up and snoring heavily in his large three-seater couch. For the last few weeks, he had been spending more than 12 hours at the office, and tonight was no exception. His phone lying on the carpet beeped. ?t first, he said so?ething incoherently, still deep in sleep. Then, it beeped again and again, in increasing urgency. >By the fifth beep, ?teve awoke with a start. He frantically searched all over his couch, and finally located his phone. The screen showed a brightly colored bar chart. Every bar seemed to touch the high line except one. He pulled out his laptop and logged into the SuperBook server. The site was up and none of the logs indicated any unusual activity. However, the external services didn't look that good. >The phone at the other end seemed to ring for eternity until a croaky voice answered, ?Hello, ?teve?? Half an hour later, ?acob was able to ?ero down the proble? to an unresponsive superhero verification service. ?Isn't that running on ?auron?? asked ?teve. There was a brief hesitation. "I am afraid so," replied Jacob. >Steve had a sinking feeling at the pit of his stomach. Sauron, a ?ainfra?e application, was their first line of defense against cyber-attacks and other kinds of possible attack. It was three in the morning when he alerted the mission control team. Jacob kept chatting with him the whole time. He was running every available diagnostic tool. There was no sign of any security breach. >Steve tried to calm him down. He reassured him that perhaps it was a temporary overload and he should get some rest. However, he knew that Jacob wouldn't stop until he found what's wrong. He also knew that it was not typical of Sauron to have a temporary overload. Feeling extremely exhausted, he slipped back to sleep. >Next ?morning, as ?steve hurried to his office building holding a bagel, he heard a deafening roar. He turned and looked up to see a massive spaceship looming towards him. Instinctively, he ducked behind a hedge. On the other side, he could hear several heavy metallic objects clanging onto the ground. Just then his cell phone rang. It was Jacob. Something had moved closer to him. As Steve looked up, he saw a nearly 10-foot-tall robot, colored orange and black, pointing what looked like a weapon directly down at him. >His phone was still ringing. He darted out into the open barely missing the sputtering shower of bullets around him. He took the call. "Hey Steve, guess what, I found out what actually happened." "I am dying to know," Steve quipped. >"Remember, we had used UserHoller's form widget to collect customer feedback? ?pparently, their data was not that clean. I ?ean several serious exploits. Hey, there is a lot of background noise. Is that the T??? Steve dived towards a large sign that said "Safe Assembly Point". "Just ignore that. Tell me what happened," he screamed. >"Okay. So, when our admin opened their feedback page, his laptop must have gotten infected. The worm could reach other systems he has access to, specifically, ?auron. I ?ust say ?acob, this is a very targeted attack. Someone who knows our security system quite well has designed this. I have a feeling something scary is coming our way." >Across the lawn, a robot picked up an SUV and hurled it towards Steve. He raised his hands and shut his eyes. The spinning mass of metal froze a few feet above hi?. ?I?portant call?? asked Hexa as she dropped the car. ?Yeah, please get ?e out of here,? ?teve begged. ## 為什么數據需要清理 終究你還是需要從表單獲取“干凈的數據”。這是否意味著用戶輸入的值是否是不干凈的呢?是的,這里有兩個理由。 首先來自外部世界的任何東西都不應該一開始就被信任。惡意用戶可以對表單輸入所有類別的探測利用,以此破壞網站的安全。因此,任何的表單數據在使用前都必須被凈化。 >#### 提示 *Best Practice* >任何時候都不能信任用戶的輸入。 ?第二,`request.POST`或者`request.GET`中的字段值只是字符串而已。即使表單字段定義為整數(比如說,年齡)或者日期(比如說,生日),瀏覽器也是把這些字段以字符串的形式發送到視圖。不可避免的是在加以使用之前你要把它們轉換到適當的Python數據類型。`form`類在進行清理時會自動地達成約定。 我們來看看實際的例子: ```python >>> fill = {"name": "Blitz", "age": "30"} >>> g = PersonDetailsForm(fill) >>> g.is_valid() True >>> g.cleaned_data {'age': 30, 'name': 'Blitz'} >>> type(g.cleaned_data["age"]) int ``` 年齡值作為字符串來傳遞(或許來自`request.POST`)到表單類。驗證之后,干凈數據包含整數形式的年齡。這完全符合用戶的期望。表單試圖抽象出字符串的傳遞,以及對用戶給出可以使用的干凈的Python數據類型的事實。 ## 顯示表單 Django表單也能夠幫助你生成表現表單的HTML。它們支持膻中不同的表現形式:`as_p`(作為段落標簽),`as_ul`(顯示為無序列表項),以及`as_table`(意料之中,顯示為表格)。 模板代碼生成HTML代碼,瀏覽器渲染這些表現已經總結為下表: 圖片:略 ?注意HTML表現僅給出了表單字段。這樣做可以在一個單獨的HTML表單中輕松地包含多個Django表單。可是,這樣做也意味著模板設計者必須有點兒公式化來寫每個表單,一如下面代碼所示: ```python <form method="post"> {% csrf_token %} <table>{{ form.as_table }}</table> <input type="submit" value="Submit" /> </form> ``` 注意,為了使HTML完整的表現,你需要添加`form`標簽周添加CSRF令牌環,`table`或者`ul`標簽,以及`submit`按鈕。 ## 是使用crispy的時候了 在模板中寫這么多公式化的表單是很無聊的事情。`django-crispy-forms`包可以寫非長干脆利落的來寫表單模板。它把所有表現和布局都放到了Django表單自身。因此,你可以寫更多的Python代碼,更少的HTML。 下面的表展示了csrispy表單的模板標簽生成更加完整的表單,而且外觀更加的接近原生的Bootstrap風格: |Template |Code |Output in Browser| |-----------|:------------|----------------:| |{% crispy_form}|```<from method="post"><input type='hidden' name='csrfmiddlewaretoken' value='...'/><div id='div_id_name' class='form-group'><label for='id_name' class="control-lable requiredField">Name<span class='asteriskField'>*</span></label><div class="controls"><input class='textinput textInput form-control ' id='id_name' maxlength="100" name='name' type="text" /></div></from>```(為了簡潔而刪去余下的HTML)| ?那么,你是如何實現更加干脆利落的表單的?你需要安裝`django-crispy-forms`包并將它加入到`INSTALLED_APPS`。如果你使用Bootstrap 3,那么你需要在設置中引用: ```python CRISPY_TEMPLATE_PACK = "bootstrap3" ``` 表單初始化需要引用類型`FormHelper`的輔助屬性。下面的代碼以最小化設計,并使用默認的布局: ```python from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit class PersonDetailsForm(forms.Form): name = forms.CharField(max_length=100) age = forms.IntegerField() def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper(self) self.helper.layout.append(Submit('submit', 'Submit')) ``` ## 理解CSRF 你也一定注意到了在表單模板中叫做`CSRF`的東西。它用來干什么的?它是一種應對針對表單的`跨站請求偽造(CSRF)`的保護機制。 它通過注入服務端生成的隨機的叫做CSRF令牌的字符工作,這個字符串對用戶的session來說是唯一的。每次表單提交時,都有一個包含此令牌的隱藏字段。這個令牌確保表單是由用戶生成而且是來自原始站點的,而不是由攻擊者使用類似字段創建的假冒表單。 在表單使用`GET`方法時并不推薦CSRF令牌,因為`GET`行為不應該改變服務器狀態。此外,表單通過`GET`提交表單會在URL中暴露CSRF令牌。因此URL在登錄或者并行嗅探時具有更高的風險,當使用`POST`方式最好在表單中使用CSRF。 ## 表單處理類視圖 基本上,我們可以通過子類化類視圖來處理表單: ```python class ClassBasedFormView(generic.View): template_name = 'form.html' def get(self, request): form = PersonDetailsForm() return render(request, self.template_name, {'form': form}) def post(self, request): form = PersonDetailsForm(request.POST) if form.is_valid(): # 成功的話,我就可以使用了form.cleaned_data。 return redirect('success') else: # 無效,重新顯示含有錯誤高亮的表單 return render(request, self.template_name, {'form': form}) ``` 上面的代碼與我們之前的見到的序列圖表相比較。三種應用場景已經被分別處理。 每一個表單都如期望的那樣遵守`Post/Redirect/Get(PRG)`模式。如果提交的表單發現是無效的,它必須發起一個重定向。這能夠阻止重復表單的提交。 不過,這樣做是并不十分符合DRY原則的編寫。表單類名稱和模板名稱屬性都已經重復了。使用`FormView`這樣的通用類視圖能夠表單處理中的冗余部分。下面的代碼會給你帶來和前面一樣的功能而且還少了幾行代碼: ```python from django.core.urlresolvers import reverse_lazy class GenericFormView(generic.FormView): template_name = 'form.html' form_class = PersonDetailsForm success_url = reverse_lazy("success") ``` 這個例子中我們需要使用`reverse_lazy`,因為URL模式在視圖文件導入時并沒有被載入。 ## 表單模式 我們來看一看使用表單時會見到的一些常用模式。 ## 模式-動態表單的生成 問題:自動地添加表單字段或者改變已經聲明的表單字段。 解決方案:在表單初始化的時候添加或者改變字段。 ### 問題細節 表單通常以一種將擁有的字段列為類字段的聲明方式。不過,有時候我們不能提前知道數量,或者這些字段的類型。這就要求表單能夠動態地生成。該模式有時稱為`動態表單`或者`運行時的表單生成`。 想象有一個旅客航班登錄系統,它允許經濟艙機票改簽為頭等艙。假如頭等艙座席有剩余,那么在用戶想要乘坐頭等艙是就需要有一個額外的選項。不過,這個額外的字段不能夠公開,因為它對于全部用戶來說是不可見的。這樣的動態表單就可以通過該模式來處理。 ### 解決方案細節 每一個表單的實例都有一個叫做字段的`fields`,它是一個擁有全部字段的字典。可以在運行時對它作出修改。添加或者改變字段可以在表單初始化時完成。 例如,如果我們添加一個到用戶詳情表單的復選框,只要在表單初始化時命名為“upgrade”的關鍵字參數為真,那么我們就可以以如下代碼來實現它: ```python class PersonDetailsForm(forms.Form): name = forms.CharField(max_length=100) age = forms.IntegerField() def __init__(self, *args, **kwargs): upgrade = kwargs.pop("upgrade", False) super().__init__(*args, **kwargs) # Show first class option? 顯示頭等艙選項? if upgrade: self.fields["first_class"] = forms.BooleanField(label="Fly First Class?") ``` 現在,我們只需要傳遞關鍵字參數`PersonDetailsForm(upgrade=True)`以產生一個額外的布爾輸入字段(復選框)。 >##### 注釋 注意,最新引入的關鍵字參數在我們調用`super`以避免`unexpected keyword`錯誤已經被移除,或者去掉。 如果我們對這個例子使用`FormView`類,那么我們需要通過重寫視圖類的`get_form_kwargs`方法來傳遞關鍵字參數,一如下面代碼所示: ```python class PersonDetailsEdit(generic.FormView): ... def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs["upgrade"] = True return kwargs ``` 這個模式可以用來在運行時改變任意一個字段的屬性,比如它的部件或者輔助文本。它也能夠用于模型表單。 在很多情況下,外觀所需的動態表單可以通過使用Django表單集合來解決。在頁面中當表單需要重復時就要用到它們了。一個典型的表單集合用法是在設計數據的網格視圖時一行接一行的添加元素。因此,你不需要使用任意數量的行來創建一個動態表單。你只需依照行來創建表單,使用`formset_factory`函數來創建多個行。 ##?? 模式-用戶表單 問題:表單需要根據已經登錄的用戶來進行定制。 解決方案:傳遞已登錄用戶作為關鍵字參數到表單的構造器。 ### 問題細節 表單可以按用戶以不同的形式來表現。某些用戶或許不需要填充全部字段,而其他人就需要添加額外的信息。某些情況下,你或許需要對用戶資格做些檢查,比如,驗證他們是否為一個組的成員,以便搞清楚如何構建表單。 ### 解決方案細節 你一定注意到了,你可以使用動態表單生成模式中給出的解決方案來解決這個問題。你只需要將`request.user`作為一個關鍵字參數傳遞到表單。然而,為了簡潔和更富于重復使用的解決方案,我們也可以使用`django-braces`包中的mixin。 如前面的例子所示,我們需要對用戶展示一個額外的復選框。可是,這僅在用戶是VIP組成員時才會被顯示。讓我們來看看`PersonDetailsForm`是如何使用`django-braces`的表單mixin`UserKwargModelFormMixin`來簡化的: ```python from braces.forms import UserKwargModelFormMixin class PersonDetailsForm(UserKwargModelFormMixin, forms.Form): ... def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Are you a member of the VIP group? # 你是VIP組的成員嗎? if self.user.groups.filter(name="VIP").exists(): self.fields["first_class"] = forms.BooleanField(label="Fly First Class?") ``` 注意`self.user`如何通過mixin去掉用戶關鍵字參數被自動地設為可用。 對應到表單mixin,有一個成為`UserFormKwargsMixin`的視圖mixin,它需要添加到視圖,使用`LoginRequiredMixin`以確保只有已經登錄的用戶可以訪問該視圖: ```python class VIPCheckFormView(LoginRequiredMixin, UserFormKwargsMixin, generic.FormView): form_class = PersonDetailsForm ... ``` 現在用戶參數自動地傳遞到表單`PersonDetailsForm`。 在`django-braces`中檢查其他的表單mixin,比如拿`FormValidMessageMixin`來說,它就是常見的表單使用模式的一個現成的方案。 ## 模式-一個視圖的多個表單行為 問題:在一個獨立的視圖或者頁面中處理多個表單行為。 解決方案:表單可以使用獨立的視圖來處理表單提交,或者識別基于提交按鈕名稱的表單。 ### 問題細節 Django使合并多個擁有相同行為的表單相當地簡單,例如,單獨的一個提交按鈕。不過,大多數的頁面需要在同樣的頁面上顯示多個行為。例如,你或許想要用戶在同一頁面的兩個不同表單中訂閱或取消訂閱一個新聞簡報。 不過,Django的`FormView`被設計成只處理一個表單對應一個視圖的場景。很多的其他通用類視圖也共享該假定。 ### 解決方案細節 處理多個表單有兩個辦法:分離視圖和獨立視圖。首先我們來看下第一個方法。 ### 獨立的行為分離視圖 依據視圖的行為為每個表單指定不同的視圖,是一個相當簡單的辦法。例如,訂閱和取消訂閱表單。剛好有兩個獨立的視圖類來處理他們各自表單的`POST`方法。 ### 獨立的相同視圖 可能你發現了分割視圖來處理表單是沒有必要的,抑或發現了使用一個公共視圖來處理邏輯上相關連的表單為更加簡潔明了。 當對多個表單使用相同的視圖類時,其挑戰在于標識哪個表單來處理POST行為。這里,我們利用了事實上的優勢,提交按鈕的名稱和值同時被提交了。假如提交按鈕在表單中命名唯一,那么處理時表單就可以被標識出來。 這里,我們定義一個使用crispy表單的訂閱器,這樣我們就可以的命名提交按鈕了: ```python class SubscribeForm(forms.Form): email = forms.EmailField() def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper(self) self.helper.layout.append(Submit('subscribe_butn', 'Subscribe')) ``` 取消訂閱表單類`UnSubscribeForm`以完全相同的方式來定義(因此,這里我們就省略不寫出來了),除了`Submit`按鈕被命名為`unscribe_butn`之外。 因為`FormView`被設計成單視圖,我們會使用一個簡單的類視圖,即,`TemplageView`,來做為視圖的基類。我們來看一看視圖定義以及`get`方法: ```python from .forms import SubscribeForm, UnSubscribeForm class NewsletterView(generic.TemplateView): subcribe_form_class = SubscribeForm unsubcribe_form_class = UnSubscribeForm template_name = "newsletter.html" def get(self, request, *args, **kwargs): kwargs.setdefault("subscribe_form", self.subcribe_form_class()) kwargs.setdefault("unsubscribe_form", self.unsubcribe_form_class()) return super().get(request, *args, **kwargs) ``` `TemplateView`類的關鍵字參數可以方便地插入進模板上下文。我們僅在表單實例不存在時,利用`setdefault`字典方法的幫助來創建其中一個表單的實例。我們很快就可以看到為什么要這樣做。 接下來,我們來看看POST方法,它處理了表單的提交: ```python def post(self, request, *args, **kwargs): form_args = { 'data': self.request.POST, 'files': self.request.FILES, } if "subscribe_butn" in request.POST: form = self.subcribe_form_class(**form_args) if not form.is_valid(): return self.get(request, subscribe_form=form) return redirect("success_form1") elif "unsubscribe_butn" in request.POST: form = self.unsubcribe_form_class(**form_args) if not form.is_valid(): return self.get(request, unsubscribe_form=form) return redirect("success_form2") return super().get(request) ``` 首先,表單的關鍵字參數,比如數據和文件,就是在`form_args`字典中產生的。接下來,第一個表單的`Submit`按鈕的存在是用來檢查`request.POST`。假如發現了按鈕的名稱,那么第一個表單就被初始化。 如果表單驗證失敗,那么響應通過第一個表單實例的`GET`方法被創建的方法就會返回。同樣地,我們查找第二個表單的提交按鈕以檢查第二個表單是否被提交。 相同視圖中的相同表單的實例可以通過表單前綴以相同方式來實現。你可以使用`SubscribeForm(prefix="offers")`這樣的前綴參數來實例化一個表單。比如實例利用給出的參數作為前綴加入到所有的表單字段,實際上當作一個表單的命名空間來使用。 ## 模式-CRUD視圖 問題:公式化的對模型編寫CRUD接口是在重復相同的事。 解決方案:使用類的通用視圖來編輯視圖。 ### 問題細節 在大多數的web應用中,大約百分之80的時間被用來寫,創建,讀取,更新以及刪除(CRUD)數據庫的接口。例如,基本上,Twitter就涉及到了創建和讀取其他用戶的推文。這里,推文是可以被維護和存儲的數據庫對象。 要是從零開始寫這樣的接口實在是乏味至極。如果CRUD接口可以自動地從模型類創建,那么這個模式可以輕松地管理。 ### 解決方案細節 Django利用了一個四個通用類視圖的組簡化了創建CRUD視圖的過程。如下,它們可以被映射到其自身像對應的操作: - CreateView: 該視圖顯示一個空白表單以創建一個新的對象。 - DetailView: 該視圖通過讀取數據庫來展示一個對象的細節。 - UpdateView: 該視圖被允許通過一個預先生成的表單來更新一個對象的細節。 - DeleteView?: 該視圖像是一個確認頁面,并準許刪除對象。 讓我們來看一個簡單的例子。我們擁有一個包含重要日期的模型,它關系到使用網站的每一個用戶的利益。我們需要構建簡單的CRUD接口,這樣任何人都可以查看,修改這些日期。我們來看下`Importantdate`模型的內容: ```python # models.py class ImportantDate(models.Model): date = models.DateField() desc = models.CharField(max_length=100) def get_absolute_url(self): return reverse('impdate_detail', args=[str(self.pk)]) ``` `get_absolute_url()`方法被`CreateView`和`UpdateView`類在對象成功創建或者更新之后使用。它已經被路由到了對象的`DetailView`。 這些CRUD視圖足夠的簡單,因此它們不解自明的,一如以下代碼所示: ```python # views.py from django.core.urlresolvers import reverse_lazy from . import forms class ImpDateDetail(generic.DetailView): model = models.ImportantDate class ImpDateCreate(generic.CreateView): model = models.ImportantDate form_class = forms.ImportantDateForm class ImpDateUpdate(generic.UpdateView): model = models.ImportantDate form_class = forms.ImportantDateForm class ImpDateDelete(generic.DeleteView): model = models.ImportantDate success_url = reverse_lazy("impdate_list") ``` 這些通用視圖中,模型類只是強制成員被引用。不過,在`DeleteView`的情形下,`success_url`函數需要很好地的應用。這是因為`get_absolute_url`刪除之后再也不能夠用來找到在什么地方重定向用戶。 定義`form_class`屬性并非是強制的。如果它被省略,與`ModelForm`方法相當的模型會被創建。不過,我們想要創建自己的模型表單以利用crispy表單,一如下面代碼所示: ```python # forms.py from django import forms from . import models from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit class ImportantDateForm(forms.ModelForm): class Meta: model = models.ImportantDate fields = ["date", "desc"] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper(self) self.helper.layout.append(Submit('save', 'Save')) ``` 要感謝crispy表單,我們在模板中需要非常少的HTML裝飾以構建這些CRUD表單。 >?##### 注釋 注意明確地引用`ModelForm`方法的字段就是最佳實踐,在未來的發行版中這也將成為強制規定。 默認,模板的路徑基于視圖類和模型的名稱。為了簡潔起見,此處我們省略了模板的源碼。注意我們可以對`CreateView`和`UpdateView`使用相同的表單。 最后,我們來看看`urls.py`,這里所有東西都連接起來了: ```python url(r'^impdates/create/$', pviews.ImpDateCreate.as_view(), name="impdate_create"), url(r'^impdates/(?P<pk>\d+)/$', pviews.ImpDateDetail.as_view(), name="impdate_detail"), url(r'^impdates/(?P<pk>\d+)/update/$', pviews.ImpDateUpdate.as_view(), name="impdate_update"), url(r'^impdates/(?P<pk>\d+)/delete/$', pviews.ImpDateDelete.as_view(), name="impdate_delete"), ``` Django的通用是我們開始為模型創建CRUD視圖的一種了不起的方式。僅需少少幾行代碼,你就可以獲得經過良好測試的模型表單和視圖,而不用自己來做重復乏味的工作。 ## 總結 在這一章,我們見過了web表單如何被創建,以及在Django中如何利用表單類將它們抽象。我們也即拿過了在使用表單時,利用多種技術和模式去節省時間。 在下一章,我們來看一看在使用舊版本的Django代碼庫時用到的系統化的方法,以及當我們碰到用戶需要時如何的加強它。 {% endraw %}
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看