The Django Book

Chapter 7: Form Processing

绗竷绔 琛ㄥ崟澶勭悊

Guest author: Simon Willison

鏈珷浣滆呮槸Simon Willison

After following along with the last chapter, you should now have a fully functioning if somewhat simple site. In this chapter, well deal with the next piece of the puzzle: building views that take input from readers.

缁忚繃涓婁竴绔狅紝浣犲簲璇ュ绠鍗曠綉绔欐湁涓叏闈㈢殑璁よ瘑銆傝繖涓绔狅紝鏉ュ鐞唚eb寮鍙戠殑涓嬩竴涓毦棰橈細寤虹珛鐢ㄦ埛杈撳叆鐨勮鍥俱

Well start by making a simple search form by hand and looking at how to handle data submitted from the browser. From there, well move on to using Djangos forms framework.

鎴戜滑浼氫粠鎵嬪伐鎵撻犱竴涓畝鍗曠殑鎼滅储椤甸潰寮濮嬶紝鐪嬬湅鎬庢牱澶勭悊娴忚鍣ㄦ彁浜よ屾潵鐨勬暟鎹傜劧鍚庢垜浠紑濮嬩娇鐢―jango鐨刦orms妗嗘灦銆

鎼滅储

The Web is all about search. Two of the Nets biggest success stories, Google and Yahoo, built their multi-billion-dollar businesses around search. Nearly every site sees a large percentage of traffic coming to and from its search pages. Often the difference between the success or failure of a site is the quality of its search. So it looks like wed better add some searching to our fledgling books site, no?

鍦╳eb搴旂敤涓婏紝鏈変袱涓叧浜庢悳绱㈣幏寰楀法澶ф垚鍔熺殑鏁呬簨锛欸oogle鍜孻ahoo锛岄氳繃鎼滅储锛屼粬浠缓绔嬩簡鍑犲崄浜跨編鍏冪殑涓氬姟銆傚嚑涔庢瘡涓綉绔欓兘鏈夊緢澶х殑姣斾緥璁块棶閲忔潵鑷繖涓や釜鎼滅储寮曟搸銆傜敋鑷筹紝涓涓綉绔欐槸鍚︽垚鍔熷彇鍐充簬鍏剁珯鍐呮悳绱㈢殑璐ㄩ噺銆傚洜姝わ紝鍦ㄦ垜浠繖涓綉绔欐坊鍔犳悳绱㈠姛鑳界湅璧锋潵濂戒竴浜涖

Well start by adding the search view to our URLconf (mysite.urls ). Recall that this means adding something like (r'^search/$', 'mysite.books.views.search') to the set of URL patterns.

寮濮嬶紝鍦║RLconf (mysite.urls )娣诲姞鎼滅储瑙嗗浘銆傛坊鍔犵被浼 (r'^search/$','mysite.books.views.search') 璁剧疆URL妯″紡銆

Next, well write this search view into our view module (mysite.books.views ):

涓嬩竴姝ワ紝鍦ㄨ鍥炬ā鍧(mysite.books.views )涓啓杩欎釜 search 瑙嗗浘:

from django.db.models import Q
from django.shortcuts import render_to_response
from models import Book

def search(request):
    query = request.GET.get('q', '')
    if query:
        qset = (
            Q(title__icontains=query) |
            Q(authors__first_name__icontains=query) |
            Q(authors__last_name__icontains=query)
        )
        results = Book.objects.filter(qset).distinct()
    else:
        results = []
    return render_to_response("books/search.html", {
        "results": results,
        "query": query
    })

There are a couple of things going on here that you havent yet seen. First, theres request.GET . This is how you access GET data from Django; POST data is accessed through a similar request.POST object. These objects behave exactly like standard Python dictionaries with some extra features covered in Appendix H.

杩欓噷鏈変竴浜涢渶瑕佹敞鎰忕殑锛岄鍏 request.GET 锛岃繖浠嶥jango涓庢牱璁块棶GET鏁版嵁锛汸OST鏁版嵁閫氳繃绫讳技鐨 request.POST 瀵硅薄璁块棶銆傝繖浜涘璞¤涓轰笌鏍囧噯Python瀛楀吀寰堝儚锛屽湪闄勫綍H涓垪鍑烘潵鍏跺彟澶栫殑鐗规с

Whats GET and POST Data?

浠涔堟槸 GET and POST 鏁版嵁?

GET and POST are the two methods that browsers use to send data to a server. Most of the time, youll see them in HTML form tags:

GET 鍜孭OST 鏄祻瑙堝櫒浣跨敤鐨勪袱涓柟娉曪紝鐢ㄤ簬鍙戦佹暟鎹埌鏈嶅姟鍣ㄧ銆 涓鑸潵璇达紝浼氬湪html琛ㄥ崟閲岄潰鐪嬪埌:

<form action="/books/search/" method="get">

This instructs the browser to submit the form data to the URL /books/search/ using the GET method.

瀹冩寚绀烘祻瑙堝櫒鍚/books/search/浠ET鐨勬柟娉曟彁浜ゆ暟鎹

There are important differences between the semantics of GET and POST that we wont get into right now, but see http://www.w3.org/2001/tag/doc/whenToUseGet.html if you want to learn more.

鍏充簬GET鍜孭OST杩欎袱涓柟娉曚箣闂存湁寰堝ぇ鐨勪笉鍚岋紝涓嶈繃鎴戜滑鏆傛椂涓嶆繁鍏ュ畠锛屽鏋滀綘鎯充簡瑙f洿澶氾紝鍙互璁块棶锛 http://www.w3.org/2001/tag/doc/whenToUseGet.html

So the line:

鎵浠ヤ笅闈㈣繖琛岋細

query = request.GET.get('q', '')

looks for a GET parameter named q and returns an empty string if that parameter wasnt submitted.

瀵绘壘鍚嶄负 q 鐨凣ET鍙傛暟锛岃屼笖濡傛灉鍙傛暟娌℃湁鎻愪氦锛岃繑鍥炰竴涓┖鐨勫瓧绗︿覆銆

Note that were using the get() method on request.GET , which is potentially confusing. The get() method here is the one that every Python dictionary has. Were using it here to be careful: it is not safe to assume that request.GET contains a 'q' key, so we use get('q', '') to provide a default fallback value of '' (the empty string). If we merely accessed the variable using request.GET['q'] , that code would raise a KeyError if q wasnt available in the GET data.

娉ㄦ剰鍦 request.GET 涓娇鐢ㄤ簡 get() 鏂规硶锛岃繖鍙兘璁╁ぇ瀹朵笉濂界悊瑙c傝繖閲岀殑 get() 鏄瘡涓猵ython鐨勭殑瀛楀吀鏁版嵁绫诲瀷閮芥湁鐨勬柟娉曘備娇鐢ㄧ殑鏃跺欒灏忓績锛氬亣璁 request.GET 鍖呭惈涓涓 'q' 鐨刱ey鏄笉瀹夊叏鐨勶紝鎵浠ユ垜浠娇鐢 get('q', '') 鎻愪緵涓涓己鐪佺殑杩斿洖鍊 '' (涓涓┖瀛楃涓)銆傚鏋滃彧鏄娇鐢 request.GET['q'] 璁块棶鍙橀噺锛屽湪Get鏁版嵁鏃 q 涓嶅彲寰,鍙兘寮曞彂 KeyError .

Second, what about this Q business? Q objects are used to build up complex queries in this case, were searching for any books where either the title or the name of one of the authors matches the search query. Technically, these Q objects comprise a QuerySet, and you can read more about them in Appendix C.

鍏舵,鍏充簬 Q , Q 瀵硅薄鍦ㄨ繖涓緥瀛愰噷鐢ㄤ簬寤虹珛澶嶆潅鐨勬煡璇,鎼滅储鍖归厤鏌ヨ鐨勪换浣曚功绫.鎶鏈笂 Q 瀵硅薄鍖呭惈QuerySet,鍙互鍦ㄩ檮褰旵涓繘涓姝ラ槄璇.

In these queries, icontains is a case-insensitive search that uses the SQL LIKE operator in the underlying database.

鍦ㄨ繖涓煡璇腑锛 icontains 浣跨敤SQL鐨 LIKE 鎿嶄綔绗︼紝鏄ぇ灏忓啓涓嶆晱鎰熺殑銆

Since were searching against a many-to-many field, its possible for the same book to be returned more than once by the query (e.g., a book with two authors who both match the search query). Adding .distinct() to the filter lookup eliminates any duplicate results.

鏃㈢劧鎼滅储渚濋潬澶氬澶氬煙鏉ュ疄鐜帮紝灏辨湁鍙兘瀵瑰悓涓鏈功杩斿洖澶氭鏌ヨ缁撴灉锛堜緥濡傦細涓鏈功鏈変袱涓綔鑰呴兘绗﹀悎鏌ヨ鏉′欢锛夈傚洜姝ゆ坊鍔 .distinct() 杩囨护鏌ヨ缁撴灉锛屾秷闄ら噸澶嶉儴鍒嗐

Theres still no template for this search view, however. This should do the trick:

鐜板湪浠嶇劧娌℃湁杩欎釜鎼滅储瑙嗗浘鐨勬ā鏉匡紝鍙互濡備笅瀹炵幇:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
    <title>Search{% if query %} Results{% endif %}</title>
</head>
<body>
  <h1>Search</h1>
  <form action="." method="GET">
    <label for="q">Search: </label>
    <input type="text" name="q" value="{{ query|escape }}">
    <input type="submit" value="Search">
  </form>

  {% if query %}
    <h2>Results for "{{ query|escape }}":</h2>

    {% if results %}
      <ul>
      {% for book in results %}
        <li>{{ book|escape }}</l1>
      {% endfor %}
      </ul>
    {% else %}
      <p>No books found</p>
    {% endif %}
  {% endif %}
</body>
</html>

Hopefully by now what this does is fairly obvious. However, there are a few subtleties worth pointing out:

甯屾湜浣犲凡缁忓緢娓呮鍦版槑鐧借繖涓疄鐜般備笉杩囷紝鏈夊嚑涓粏鑺傞渶瑕佹寚鍑猴細

The forms action is . , which means the current URL. This is a standard best practice: dont use separate views for the form page and the results page; use a single one that serves the form and search results.

琛ㄥ崟鐨刟ction鏄 . 锛 琛ㄧず褰撳墠鐨刄RL銆傝繖鏄竴涓爣鍑嗙殑鏈浣虫儻甯稿鐞嗘柟寮忥細涓嶄娇鐢ㄧ嫭绔 鐨勮鍥惧垎鍒潵鏄剧ず琛ㄥ崟椤甸潰鍜岀粨鏋滈〉闈紱鑰屾槸浣跨敤鍗曚釜瑙嗗浘椤甸潰鏉ュ鐞嗚〃鍗曞苟鏄剧ず鎼滅储缁撴灉銆

We reinsert the value of the query back into the <input> . This lets readers easily refine their searches without having to retype what they searched for.

鎴戜滑鎶婅繑鍥炵殑鏌ヨ鍊奸噸鏂版彃鍏ュ埌 <input> 涓幓锛屼互渚夸簬璇昏呭彲浠ュ畬鍠勪粬浠殑鎼滅储鍐呭锛 鑰屼笉蹇呴噸鏂拌緭鍏ユ悳绱㈠唴瀹广

Everywhere query and book is used, we pass it through the escape filter to make sure that any potentially malicious search text is filtered out before being inserted into the page.

鍦ㄦ墍鏈変娇鐢 querybook 鐨勫湴鏂癸紝鎴戜滑閫氳繃 escape 杩囨护鍣ㄦ潵纭繚浠讳綍 鍙兘鐨勬伓鎰忕殑鎼滅储鏂囧瓧琚繃婊ゅ嚭鍘伙紝浠ヤ繚璇佷笉琚彃鍏ュ埌椤甸潰閲屻

Its vital that you do this with any user-submitted content! Otherwise you open your site up to cross-site scripting (XSS) attacks. Chapter 19 discusses XSS and security in more detail.

杩欏澶勭悊浠讳綍鐢ㄦ埛鎻愪氦鏁版嵁鏉ヨ鏄 蹇呴』 鐨勶紒鍚﹀垯鐨勮瘽浣犲氨寮鏀句綘鐨勭綉绔欏厑璁歌法绔欑偣鑴氭湰 锛圶SS锛夋敾鍑汇傚湪绗崄涔濈珷涓皢璇︾粏璁ㄨ浜哫SS鍜屽畨鍏ㄣ

However, we dont need to worry about harmful content in your database lookups we can simply pass the query into the lookup as is. This is because Djangos database layer handles this aspect of security for you.

涓嶈繃锛屾垜浠笉蹇呮媴蹇冩暟鎹簱瀵瑰彲鑳芥湁鍗卞鍐呭鐨勬煡璇㈢殑澶勭悊銆 Django鐨勬暟鎹簱灞傚湪杩欐柟闈㈠凡缁忓仛杩囧畨鍏ㄥ鐞嗐 銆愯瘧娉細鏁版嵁搴撳眰瀵规煡璇㈡暟鎹嚜鍔‥scape锛屾墍浠ヤ笉鐢ㄦ媴蹇冦

Now we have a working search. A further improvement would be putting a search form on every page (i.e., in the base template); well let you handle that one yourself.

鐜板湪鎴戜滑宸茬粡浣滀簡鎼滅储銆傝繘涓姝ヨ鎶婃悳绱㈣〃鍗曞姞鍒版墍鏈夌殑椤甸潰锛堜緥濡傦紝鍦╞ase妯℃澘锛夛紱杩欎釜鍙互鐢变綘鑷繁瀹屾垚銆

Next, well look at a more complex example. But before we do, lets discuss a more abstract topic: the perfect form.

涓嬮潰锛屾垜浠湅涓涓嬫洿澶嶆潅鐨勪緥瀛愩備簨鍏堟垜浠璁轰竴涓娊璞$殑璇濋锛氬畬缇庤〃鍗曘

The Perfect Form

瀹岀編琛ㄥ崟

Forms can often be a major cause of frustration for the users of your site. Lets consider the behavior of a hypothetical perfect form:

琛ㄥ崟缁忓父寮曡捣绔欑偣鐢ㄦ埛鐨勫弽鎰熴傛垜浠冭檻涓涓嬩竴涓亣璁剧殑瀹岀編鐨勮〃鍗曠殑琛屼负锛

  • It should ask the user for some information, obviously. Accessibility and usability matter here, so smart use of the HTML <label> element and useful contextual help are important.

  • 瀹冨簲璇ラ棶鐢ㄦ埛涓浜涗俊鎭紝鏄剧劧锛岀敱浜庡彲鐢ㄦх殑闂锛 浣跨敤HTML <label> 鍏冪礌鍜屾湁鐢ㄧ殑 涓婁笅鏂囧府鍔╂槸寰堥噸瑕佺殑銆

  • The submitted data should be subjected to extensive validation. The golden rule of Web application security is never trust incoming data, so validation is essential.

  • 鎵鎻愪氦鐨勬暟鎹簲璇ュ鏂归潰鐨勯獙璇併俉eb搴旂敤瀹夊叏鐨勯噾绉戠帀寰嬫槸浠庝笉瑕佺浉淇¤繘鏉ョ殑鏁版嵁锛屾墍浠ラ獙璇佹槸蹇呴渶鐨勩

  • If the user has made any mistakes, the form should be redisplayed with detailed, informative error messages. The original data should be prefilled, to save the user from having to reenter everything.

  • 濡傛灉鐢ㄦ埛鏈変竴浜涢敊璇紝琛ㄥ崟搴旇閲嶆柊鏄剧ず璇︽儏锛岄敊璇俊鎭傚師鏉ョ殑鏁版嵁搴旇宸茬粡濉ソ锛岄伩鍏嶇敤鎴烽噸鏂板綍鍏ワ紝

  • The form should continue to redisplay until all of the fields have been correctly filled.

  • 琛ㄥ崟搴旇鍦ㄦ墍鏈夊煙楠岃瘉姝g‘鍓嶄竴鐩撮噸鏂版樉绀恒

Constructing the perfect form seems like a lot of work! Thankfully, Djangos forms framework is designed to do most of the work for you. You provide a description of the forms fields, validation rules, and a simple template, and Django does the rest. The result is a perfect form with very little effort.

寤虹珛杩欐牱鐨勮〃鍗曞ソ鍍忛渶瑕佸仛寰堝宸ヤ綔锛佸垢濂斤紝Django鐨勮〃鍗曟鏋跺凡缁忚璁$殑鍙互涓轰綘鍋氱粷澶ч儴鍒嗙殑宸ヤ綔銆備綘鍙渶瑕佹彁渚涜〃鍗曞煙鐨勬弿杩帮紝楠岃瘉瑙勫垯鍜岀畝鍗曠殑妯℃澘鍗冲彲銆傝繖鏍峰氨鍙渶瑕佷竴鐐圭殑宸ヤ綔灏卞彲浠ュ仛鎴愪竴涓畬缇庣殑琛ㄥ崟銆

Creating a Feedback Form

鍒涘缓涓涓洖棣堣〃鍗

The best way to build a site that people love is to listen to their feedback. Many sites appear to have forgotten this; they hide their contact details behind layers of FAQs, and they seem to make it as difficult as possible to get in touch with an actual human being.

鍋氬ソ涓涓綉绔欓渶瑕佹敞鎰忕敤鎴风殑鍙嶉锛屽緢澶氱珯鐐瑰ソ鍍忓繕璁拌繖涓備粬浠妸鑱旂郴淇℃伅鏀惧湪FAQ鍚庨潰锛岃屼笖濂藉儚寰堥毦鑱旂郴鍒板疄闄呯殑浜恒

When your site has millions of users, this may be a reasonable strategy. When youre trying to build up an audience, though, you should actively encourage feedback at every opportunity. Lets build a simple feedback form and use it to illustrate Djangos forms framework in action.

涓涓櫨涓囩敤鎴风骇鐨勭綉绔欙紝鍙兘鏈変簺鍚堢悊鐨勭瓥鐣ャ傚鏋滃缓绔嬩竴涓潰鍚戠敤鎴风殑绔欑偣锛岄渶瑕侀紦鍔卞洖棣堛傛垜浠缓绔嬩竴涓畝鍗曠殑鍥為琛ㄥ崟锛岀敤鏉ュ睍绀篋jango鐨勮〃鍗曟鏋躲

Well start by adding adding (r'^contact/$', 'mysite.books.views.contact') to the URLconf, then defining our form. Forms in Django are created in a similar way to models: declaratively, using a Python class. Heres the class for our simple form. By convention, well insert it into a new forms.py file within our application directory:

寮濮嬶紝鍦║RLconf閲屾坊鍔 (r'^contact/$', 'mysite.books.views.contact') 锛岀劧鍚庡畾涔夎〃鍗曘 鍦―jango涓〃鍗曠殑鍒涘缓绫讳技MODEL:浣跨敤Python绫绘潵澹版槑銆傝繖閲屾槸鎴戜滑绠鍗曡〃鍗曠殑绫汇備负浜嗘柟渚匡紝鎶婂畠鍐欏埌鏂扮殑 forms.py 鏂囦欢涓紝杩欎釜鏂囦欢鍦╝pp鐩綍涓嬨

from django import newforms as forms

TOPIC_CHOICES = (
    ('general', 'General enquiry'),
    ('bug', 'Bug report'),
    ('suggestion', 'Suggestion'),
)

class ContactForm(forms.Form):
    topic = forms.ChoiceField(choices=TOPIC_CHOICES)
    message = forms.CharField()
    sender = forms.EmailField(required=False)

New Forms? What?

New Forms鏄粈涔堬紵

When Django was first released to the public, it had a complicated, confusing forms system. It made producing forms far too difficult, so it was completely rewritten and is now called newforms. However, theres still a fair amount of code that depends on the old form system, so for the time being Django ships with two form packages.

褰揇jango鏈鍒濇帹鍑虹殑鏃跺欙紝鏈変竴涓鏉傝岄毦鐢ㄧ殑form绯荤粺銆傜敤瀹冩潵鏋勫缓琛ㄥ崟绠鐩村氨鏄櫓姊︼紝鎵浠ュ畠鍦ㄦ柊鐗堟湰閲岄潰琚竴涓彨鍋歯ewforms鐨勭郴缁熷彇浠d簡銆備絾鏄壌浜庤繕鏈夊緢澶氫唬鐮佷緷璧栦簬鑰佺殑閭d釜form绯荤粺锛屾殏鏃禗jango杩樻槸鍚屾椂淇濇湁涓や釜forms鍖呫

As we write this book, Djangos old form system is still available as django.forms and the new form package as django.newforms . At some point that will change and django.forms will point to the new form package. However, to make sure the examples in this book work as widely as possible, all the examples will refer to django.newforms .

鍦ㄦ湰涔﹀啓浣滄湡闂达紝Django鐨勮乫orm绯荤粺杩樻槸鍦 django.forms 涓紝鏂扮殑form绯荤粺浣嶄簬 django.newforms 涓傝繖绉嶇姸鍐佃繜鏃╀細鏀瑰彉锛 django.forms 浼氭寚鍚戞柊鐨刦orm鍖呫 浣嗘槸涓轰簡璁╂湰涔︿腑鐨勪緥瀛愬敖鍙兘骞挎硾鍦板伐浣滐紝鎵鏈夌殑浠g爜涓粛鐒朵細浣跨敤 django.newforms

A Django form is a subclass of django.newforms.Form , just as a Django model is a subclass of django.db.models.Model . The django.newforms module also contains a number of Field classes; a full list is available in Djangos documentation at http://www.djangoproject.com/documentation/0.96/newforms/.

涓涓狣jango琛ㄥ崟鏄 django.newforms.Form 鐨勫瓙绫伙紝灏卞儚Django妯″瀷鏄 django.db.models.Model 鐨勫瓙绫讳竴鏍枫傚湪django.newforms妯″潡涓繕鍖呭惈寰堝Field绫伙紱Django鐨勬枃妗o紙 http://www.djangoproject.com/documentation/0.96/newforms/ 锛変腑鍖呭惈浜嗕竴涓彲鐢ㄧ殑Field鍒楄〃銆

Our ContactForm consists of three fields: a topic, which is a choice among three options; a message, which is a character field; and a sender, which is an email field and is optional (because even anonymous feedback can be useful). There are a number of other field types available, and you can write your own if they dont cover your needs.

鎴戜滑鐨 ContactForm 鍖呭惈涓変釜瀛楁锛氫竴涓猼opic锛屽畠鏄竴涓笁閫変竴鐨勯夋嫨妗嗭紱涓涓猰essage锛屽畠鏄竴涓枃鏈煙锛涜繕鏈変竴涓猻ender锛屽畠鏄竴涓彲閫夌殑email鍩燂紙鍥犱负鍗充娇鏄尶鍚嶅弽棣堜篃鏄湁鐢ㄧ殑锛夈傝繕鏈夊緢澶氬瓧娈电被鍨嬪彲渚涢夋嫨锛屽鏋滃畠浠兘涓嶆弧瓒宠姹傦紝浣犲彲浠ヨ冭檻鑷繁鍐欎竴涓

The form object itself knows how to do a number of useful things. It can validate a collection of data, it can generate its own HTML widgets, it can construct a set of useful error messages and, if were feeling lazy, it can even draw the entire form for us. Lets hook it into a view and see it in action. In views.py :

form瀵硅薄鑷繁鐭ラ亾濡備綍鍋氫竴浜涙湁鐢ㄧ殑浜嬫儏銆傚畠鑳芥牎楠屾暟鎹泦鍚堬紝鐢熸垚HTML鈥滈儴浠垛濓紝鐢熸垚涓闆嗘湁鐢ㄧ殑閿欒淇℃伅锛屽綋鐒讹紝濡傛灉浣犵‘瀹炲緢鎳掞紝瀹冧篃鑳界粯鍑烘暣涓猣orm銆傜幇鍦ㄨ鎴戜滑鎶婂畠宓屽叆涓涓鍥撅紝鐪嬬湅鎬庝箞鏍蜂娇鐢ㄥ畠銆傚湪views.py閲岄潰锛

from django.db.models import Q
from django.shortcuts import render_to_response
from models import Book
**from forms import ContactForm**

def search(request):
    query = request.GET.get('q', '')
    if query:
        qset = (
            Q(title__icontains=query) |
            Q(authors__first_name__icontains=query) |
            Q(authors__last_name__icontains=query)
        )
        results = Book.objects.filter(qset).distinct()
    else:
        results = []
    return render_to_response("books/search.html", {
        "results": results,
        "query": query
    })

**def contact(request):**
    **form = ContactForm()**
    **return render_to_response('contact.html', {'form': form})**

and in contact.html :

娣诲姞contact.html鏂囦欢锛

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
    <title>Contact us</title>
</head>
<body>
    <h1>Contact us</h1>
    <form action="." method="POST">
        <table>
            {{ form.as_table }}
        </table>
        <p><input type="submit" value="Submit"></p>
    </form>
</body>
</html>

The most interesting line here is {{ form.as_table }} . form is our ContactForm instance, as passed to render_to_response . as_table is a method on that object that renders the form as a sequence of table rows (as_ul and as_p can also be used). The generated HTML looks like this:

鏈鏈夋剰鎬濈殑涓琛屾槸 {{ form.as_table }}銆俧orm鏄疌ontactForm鐨勪竴涓疄渚嬶紝鎴戜滑閫氳繃render_to_response鏂规硶鎶婂畠浼犻掔粰妯℃澘銆俛s_table鏄痜orm鐨勪竴涓柟娉曪紝瀹冩妸form娓叉煋鎴愪竴绯诲垪鐨勮〃鏍艰(as_ul鍜宎s_p涔熸槸璧风潃鐩镐技鐨勪綔鐢)銆傜敓鎴愮殑HTML鍍忚繖鏍凤細

<tr>
    <th><label for="id_topic">Topic:</label></th>
    <td>
        <select name="topic" id="id_topic">
            <option value="general">General enquiry</option>
            <option value="bug">Bug report</option>
            <option value="suggestion">Suggestion</option>
        </select>
    </td>
</tr>
<tr>
    <th><label for="id_message">Message:</label></th>
    <td><input type="text" name="message" id="id_message" /></td>
</tr>
<tr>
    <th><label for="id_sender">Sender:</label></th>
    <td><input type="text" name="sender" id="id_sender" /></td>
</tr>

Note that the <table> and <form> tags are not included; you need to define those yourself in the template, which gives you control over how the form behaves when it is submitted. Label elements are included, making forms accessible out of the box.

璇锋敞鎰忥細<table>鍜<form>鏍囩骞舵病鏈夊寘鍚湪鍐咃紱鎴戜滑闇瑕佸湪妯℃澘閲屽畾涔夊畠浠紝杩欑粰浜堟垜浠洿澶х殑鎺у埗鏉冨幓鍐冲畾form鎻愪氦鏃剁殑琛屼负銆侺abel鍏冪礌鏄寘鍚湪鍐呯殑锛屼护璁块棶鎬ф洿浣筹紙鍥犱负label鐨勫间細鏄剧ず鍦ㄩ〉闈笂锛夈

Our form is currently using a <input type="text"> widget for the message field. We dont want to restrict our users to a single line of text, so well swap in a <textarea> widget instead:

鎴戜滑鐨刦orm鐜板湪浣跨敤浜嗕竴涓<input type=”text”>閮ㄤ欢鏉ユ樉绀簃essage瀛楁銆備絾鎴戜滑涓嶆兂闄愬埗鎴戜滑鐨勭敤鎴峰彧鑳借緭鍏ヤ竴琛屾枃鏈紝鎵浠ユ垜浠敤涓涓<textarea>閮ㄤ欢鏉ユ浛浠o細

class ContactForm(forms.Form):
    topic = forms.ChoiceField(choices=TOPIC_CHOICES)
    message = forms.CharField(**widget=forms.Textarea()** )
    sender = forms.EmailField(required=False)

The forms framework separates out the presentation logic for each field into a set of widgets. Each field type has a default widget, but you can easily override the default, or provide a custom widget of your own.

forms妗嗘灦鎶婃瘡涓涓瓧娈电殑鏄剧ず閫昏緫鍒嗙鍒颁竴缁勯儴浠讹紙widget锛変腑銆傛瘡涓涓瓧娈电被鍨嬮兘鎷ユ湁涓涓粯璁ょ殑閮ㄤ欢锛屾垜浠篃鍙互瀹规槗鍦版浛鎹㈡帀榛樿鐨勯儴浠讹紝鎴栬呮彁渚涗竴涓嚜瀹氫箟鐨勯儴浠躲

At the moment, submitting the form doesnt actually do anything. Lets hook in our validation rules:

鐜板湪锛屾彁浜よ繖涓猣orm娌℃湁鍦ㄥ悗鍙板仛浠讳綍浜嬫儏銆傝鎴戜滑鎶婃垜浠殑鏍¢獙瑙勫垯鍔犺繘鍘伙細

def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
    else:
        form = ContactForm()
    return render_to_response('contact.html', {'form': form})

A form instance can be in one of two states: bound or unbound. A bound instance is constructed with a dictionary (or dictionary-like object) and knows how to validate and redisplay the data from it. An unbound form has no data associated with it and simply knows how to display itself.

涓涓猣orm瀹炰緥鍙兘澶勫湪涓ょ鐘舵侊細缁戝畾鎴栬呮湭缁戝畾銆備竴涓粦瀹氱殑瀹炰緥鏄敱瀛楀吀锛堟垨鑰呯被浼间簬瀛楀吀鐨勫璞★級鏋勯犺屾潵鐨勶紝瀹冨悓鏍蜂篃鐭ラ亾濡備綍楠岃瘉鍜岄噸鏂版樉绀哄畠鐨勬暟鎹備竴涓湭缁戝畾鐨刦orm鏄病鏈変笌涔嬭仈绯荤殑鏁版嵁锛屼粎浠呯煡閬撳浣曟樉绀哄叾鑷韩銆

Try clicking Submit on the blank form. The page should redisplay, showing a validation error that informs us that our message field is required.

鐜板湪鍙互璇曠潃鎻愪氦涓涓嬭繖涓┖鐧界殑form浜嗐傞〉闈㈠皢浼氳閲嶆柊鏄剧ず鍑烘潵锛屾樉绀轰竴涓獙璇侀敊璇紝鎻愮ず鎴戜滑message瀛楁鏄繀椤荤殑銆

Try entering an invalid email address as well. The EmailField knows how to validate email addresses, at least to a reasonable level of doubt.

鐜板湪杈撳叆涓涓笉鍚堟硶鐨別mail鍦板潃锛孍mailField鐭ラ亾濡備綍楠岃瘉email鍦板潃锛屽ぇ澶氭暟鎯呭喌涓嬭繖绉嶉獙璇佹槸鍚堢悊鐨勩

Setting Initial Data

璁剧疆鍒濆鏁版嵁

Passing data directly to the form constructor binds that data and indicates that validation should be performed. Often, though, we need to display an initial form with some of the fields prefilled for example, an edit form. We can do this with the initial keyword argument:

鍚慺orm鐨勬瀯閫犲櫒鍑芥暟鐩存帴浼犻掓暟鎹細鎶婅繖浜涙暟鎹粦瀹氬埌form锛屾寚绀篺orm杩涜楠岃瘉銆傛垜浠湁鏃朵篃闇瑕佸湪鍒濆鍖栫殑鏃跺欓鍏堝~鍏呬竴浜涘瓧娈碘斺旀瘮鏂硅涓涓紪杈慺orm銆傛垜浠彲浠ヤ紶鍏ヤ竴浜涘垵濮嬬殑鍏抽敭瀛楀弬鏁帮細

form = CommentForm(initial={'sender': 'user@example.com'})

If our form will always use the same default values, we can configure them in the form definition itself:

濡傛灉鎴戜滑鐨刦orm鎬绘槸浼氫娇鐢ㄧ浉鍚岀殑榛樿鍊硷紝鎴戜滑鍙互鍦╢orm鑷韩鐨勫畾涔変腑璁剧疆瀹冧滑

message = forms.CharField(widget=forms.Textarea(),
                          **initial="Replace with your feedback"** )

Processing the Submission

澶勭悊鎻愪氦

Once the user has filled the form to the point that it passes our validation rules, we need to do something useful with the data. In this case, we want to construct and send an email containing the users feedback. Well use Djangos email package to do this.

褰撶敤鎴峰~瀹宖orm锛屽畬鎴愪簡鏍¢獙锛屾垜浠渶瑕佸仛涓浜涙湁鐢ㄧ殑浜嬫儏浜嗐傚湪鏈緥涓紝鎴戜滑闇瑕佹瀯閫犲苟鍙戦佷竴涓寘鍚簡鐢ㄦ埛鍙嶉鐨別mail锛屾垜浠皢浼氫娇鐢―jango鐨別mail鍖呮潵瀹屾垚

First, though, we need to tell if the data is indeed valid, and if it is, we need access to the validated data. The forms framework does more than just validate the data, it also converts it into Python types. Our contact form only deals with strings, but if we were to use an IntegerField or DateTimeField , the forms framework would ensure that we got back a Python integer or datetime object, respectively.

棣栧厛锛屾垜浠渶瑕佺煡閬撶敤鎴锋暟鎹槸涓嶆槸鐪熺殑鍚堟硶锛屽鏋滄槸杩欐牱锛屾垜浠氨瑕佽闂凡缁忛獙璇佽繃鐨勬暟鎹俧orms妗嗘灦鐢氳嚦鍋氱殑鏇村锛屽畠浼氭妸瀹冧滑杞崲鎴愬搴旂殑Python绫诲瀷銆傛垜浠殑鑱旂郴鏂瑰紡form浠呬粎澶勭悊瀛楃涓诧紝浣嗘槸濡傛灉鎴戜滑浣跨敤IntegerField鎴栬匘ataTimeField锛宖orms妗嗘灦浼氫繚璇佹垜浠粠涓彇寰楃被鍨嬫纭殑鍊笺

To tell whether a form is bound to valid data, call the is_valid() method:

娴嬭瘯涓涓猣orm鏄惁宸茬粡缁戝畾鍒板悎娉曠殑鏁版嵁锛屼娇鐢╥s_valid()鏂规硶锛

form = ContactForm(request.POST)
if form.is_valid():
    # Process form data

Now we need access to the data. We could pull it straight out of request.POST , but if we did, wed miss out on the type conversions performed by the forms framework. Instead, we use form.clean_data :

鐜板湪鎴戜滑瑕佽闂暟鎹簡銆傛垜浠彲浠ヤ粠request.POST閲岄潰鐩存帴鎶婂畠浠彇鍑烘潵锛屼絾鏄繖鏍峰仛鎴戜滑灏变抚澶变簡鐢眆ramework涓烘垜浠嚜鍔ㄥ仛绫诲瀷杞崲鐨勫ソ澶勪簡銆傛墍浠ユ垜浠浣跨敤form.clean_data:

if form.is_valid():
    topic = form.clean_data['topic']
    message = form.clean_data['message']
    sender = form.clean_data.get('sender', 'noreply@example.com')
    # ...

Note that since sender is not required, we provide a default when its missing. Finally, we need to record the users feedback. The easiest way to do this is to email it to a site administrator. We can do that using the send_mail function:

璇锋敞鎰忓洜涓簊ender涓嶆槸蹇呴渶鐨勶紝鎴戜滑涓哄畠鎻愪緵浜嗕竴涓粯璁ゅ笺傜粓浜庯紝鎴戜滑瑕佽褰曚笅鐢ㄦ埛鐨勫弽棣堜簡锛屾渶绠鍗曠殑鏂规硶灏辨槸鎶婂畠鍙戦佺粰绔欑偣绠$悊鍛橈紝鎴戜滑鍙互浣跨敤send_mail鏂规硶锛

from django.core.mail import send_mail

# ...

send_mail(
    'Feedback from your site, topic: %s' % topic,
    message, sender,
    ['administrator@example.com']
)

The send_mail function has four required arguments: the email subject, the email body, the from address, and a list of recipient addresses. send_mail is a convenient wrapper around Djangos EmailMessage class, which provides advanced features such as attachments, multipart emails, and full control over email headers.

send_mail鏂规硶鏈夊洓涓繀椤荤殑鍙傛暟锛氫富棰橈紝閭欢姝f枃锛宖rom鍜屼竴涓帴鍙楄呭垪琛ㄣ俿end_mail鏄疍jango鐨凟mailMessage绫荤殑涓涓柟渚跨殑鍖呰锛孍mailMessage绫绘彁渚涗簡鏇撮珮绾х殑鏂规硶锛屾瘮濡傞檮浠讹紝澶氶儴鍒嗛偖浠讹紝浠ュ強瀵逛簬閭欢澶撮儴鐨勫畬鏁存帶鍒躲 鍙戦佸畬閭欢涔嬪悗锛屾垜浠細鎶婄敤鎴烽噸瀹氬悜鍒扮‘璁ょ殑椤甸潰銆傚畬鎴愪箣鍚庣殑瑙嗗浘鏂规硶濡備笅锛

Having sent the feedback email, well redirect our user to a static confirmation page. The finished view function looks like this:

鍙戦佸畬閭欢涔嬪悗锛屾垜浠細鎶婄敤鎴烽噸瀹氬悜鍒扮‘璁ょ殑椤甸潰銆傚畬鎴愪箣鍚庣殑瑙嗗浘鏂规硶濡備笅锛

from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.core.mail import send_mail
from forms import ContactForm

def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            topic = form.clean_data['topic']
            message = form.clean_data['message']
            sender = form.clean_data.get('sender', 'noreply@example.com')
            send_mail(
                'Feedback from your site, topic: %s' % topic,
                message, sender,
                ['administrator@example.com']
            )
            return HttpResponseRedirect('/contact/thanks/')
    else:
        form = ContactForm()
    return render_to_response('contact.html', {'form': form})

Redirect After POST

鍦≒OST涔嬪悗绔嬪嵆閲嶅畾鍚

If a user selects Refresh on a page that was displayed by a POST request, that request will be repeated. This can often lead to undesired behavior, such as a duplicate record being added to the database. Redirect after POST is a useful pattern that can help avoid this scenario: after a successful POST has been processed, redirect the user to another page rather than returning HTML directly.

鍦ㄤ竴涓狿OST璇锋眰杩囧悗锛屽鏋滅敤鎴烽夋嫨鍒锋柊椤甸潰锛岃繖涓姹傚氨閲嶅鎻愪氦浜嗐傝繖甯稿父浼氬鑷存垜浠笉甯屾湜鐨勮涓猴紝姣斿閲嶅鐨勬暟鎹簱璁板綍銆傚湪POST涔嬪悗閲嶅畾鍚戦〉闈㈡槸涓涓湁鐢ㄧ殑妯″紡锛屽彲浠ラ伩鍏嶈繖鏍风殑鎯呭喌鍑虹幇锛氬湪涓涓狿OST璇锋眰鎴愬姛鐨勫鐞嗕箣鍚庯紝鎶婄敤鎴峰寮曞埌鍙﹀涓涓〉闈笂鍘伙紝鑰屼笉鏄洿鎺ヨ繑鍥濰TML椤甸潰銆

Custom Validation Rules

1d0p2u <a href=”http://azetemwaeinp.com/“>azetemwaeinp</a>, [url=http://lcaytpsbsxkh.com/]lcaytpsbsxkh[/url], [link=http://hgjnhjgnqlch.com/]hgjnhjgnqlch[/link], http://curhltadaokb.com/

Imagine weve launched our feedback form, and the emails have started tumbling in. Theres just one problem: some of the emails are just one or two words, hardly enough for a detailed missive. We decide to adopt a new validation policy: four words or more, please.

鍋囪鎴戜滑宸茬粡鍙戝竷浜嗗弽棣堥〉闈簡锛宔mail宸茬粡寮濮嬫簮婧愪笉鏂湴娑屽叆浜嗐傚彧鏈変竴涓棶棰橈細涓浜沞mail鍙湁瀵ュ鏁拌锛屽緢闅句粠涓緱鍒颁粈涔堣缁嗘湁鐢ㄧ殑淇℃伅銆傛墍浠ユ垜浠喅瀹氬鍔犱竴鏉℃柊鐨勬牎楠岋細鏉ョ偣涓撲笟绮剧锛屾渶璧风爜鍐欏洓涓瓧锛屾嫓鎵樸

There are a number of ways to hook custom validation into a Django form. If our rule is something we will reuse again and again, we can create a custom field type. Most custom validations are one-off affairs, though, and can be tied directly to the form class.

鎴戜滑鏈夊緢澶氱殑鏂规硶鎶婃垜浠殑鑷畾涔夋牎楠屾寕鍦―jango鐨刦orm涓娿傚鏋滄垜浠殑瑙勫垯浼氳涓娆″張涓娆$殑浣跨敤锛屾垜浠彲浠ュ垱寤轰竴涓嚜瀹氫箟鐨勫瓧娈电被鍨嬨傚ぇ澶氭暟鐨勮嚜瀹氫箟鏍¢獙閮芥槸涓娆℃х殑锛屽彲浠ョ洿鎺ョ粦瀹氬埌form绫.

We want additional validation on the message field, so we need to add a clean_message method to our form:

鎴戜滑甯屾湜message瀛楁鏈変竴涓澶栫殑鏍¢獙锛屾垜浠鍔犱竴涓猚lean_message鏂规硶锛

class ContactForm(forms.Form):
    topic = forms.ChoiceField(choices=TOPIC_CHOICES)
    message = forms.CharField(widget=forms.Textarea())
    sender = forms.EmailField(required=False)

    def clean_message(self):
        message = self.clean_data.get('message', '')
        num_words = len(message.split())
        if num_words < 4:
            raise forms.ValidationError("Not enough words!")
        return message

This new method will be called after the default field validator (in this case, the validator for a required CharField ). Because the field data has already been partially processed, we need to pull it out of the forms clean_data dictionary.

杩欎釜鏂扮殑鏂规硶灏嗗湪榛樿鐨勫瓧娈垫牎楠屽櫒涔嬪悗琚皟鐢紙鍦ㄦ湰渚嬩腑锛屽氨鏄疌harField鐨勬牎楠屽櫒锛夈傚洜涓哄瓧娈垫暟鎹凡缁忚閮ㄥ垎鍦板鐞嗘帀浜嗭紝鎴戜滑闇瑕佷粠form鐨刢lean_data瀛楀吀涓妸瀹冨紕鍑烘潵銆

We naively use a combination of len() and split() to count the number of words. If the user has entered too few words, we raise a ValidationError . The string attached to this exception will be displayed to the user as an item in the error list.

鎴戜滑绠鍗曞湴浣跨敤浜唋en()鍜宻plit()鐨勭粍鍚堟潵璁$畻鍗曡瘝鐨勬暟閲忋傚鏋滅敤鎴疯緭鍏ヤ簡杩囧皯鐨勮瘝锛屾垜浠墧鍑轰竴涓猇alidationError銆傝繖涓猠xception鐨勯敊璇俊鎭細琚樉绀哄湪閿欒鍒楄〃閲屻

It is important that we explicitly return the value for the field at the end of the method. This allows us to modify the value (or convert it to a different Python type) within our custom validation method. If we forget the return statement, then None will be returned, and the original value will be lost.

鍦ㄥ嚱鏁扮殑鏈熬鏄惧紡鍦拌繑鍥炲瓧娈电殑鍊奸潪甯搁噸瑕併傛垜浠彲浠ュ湪鎴戜滑鑷畾涔夌殑鏍¢獙鏂规硶涓慨鏀瑰畠鐨勫硷紙鎴栬呮妸瀹冭浆鎹㈡垚鍙︿竴绉峆ython绫诲瀷锛夈傚鏋滄垜浠繕璁颁簡杩欎竴姝ワ紝None鍊煎氨浼氳繑鍥烇紝鍘熷鐨勬暟鎹氨涓㈠け鎺変簡銆

A Custom Look and Feel

鑷畾涔夎鎰

The quickest way to customize the forms presentation is with CSS. The list of errors in particular could do with some visual enhancement, and the <ul> has a class attribute of errorlist for that exact purpose. The following CSS really makes our errors stand out:

淇敼form鐨勬樉绀虹殑鏈蹇嵎鐨勬柟寮忔槸浣跨敤CSS銆傞敊璇殑鍒楄〃鍙互鍋氫竴浜涜瑙変笂鐨勫寮猴紝<ul>鏍囩鐨刢lass灞炴т负浜嗚繖涓洰鐨勩備笅闈㈢殑CSS璁╅敊璇洿鍔犻啋鐩簡锛

<style type="text/css">
    ul.errorlist {
        margin: 0;
        padding: 0;
    }
    .errorlist li {
        background-color: red;
        color: white;
        display: block;
        font-size: 10px;
        margin: 0 0 3px;
        padding: 4px 5px;
    }
</style>

While its convenient to have our forms HTML generated for us, in many cases the default rendering wont be right for our application. {{ form.as_table }} and friends are useful shortcuts while we develop our application, but everything about the way a form is displayed can be overridden, mostly within the template itself.

铏界劧鎴戜滑鍙互鏂逛究鍦颁娇鐢╢orm鏉ョ敓鎴怘TML锛屽彲鏄粯璁ょ殑娓叉煋鍦ㄥ鏁版儏鍐典笅婊¤冻涓嶄簡鎴戜滑鐨勫簲鐢ㄣ倇{form.as_table}}鍜屽叾瀹冪殑鏂规硶鍦ㄥ紑鍙戠殑鏃跺欐槸涓涓揩鎹风殑鏂瑰紡锛宖orm鐨勬樉绀烘柟寮忎篃鍙互鍦╢orm涓鏂逛究鍦伴噸鍐欍

Each field widget (<input type="text"> , <select> , <textarea> , or similar) can be rendered individually by accessing {{ form.fieldname }} . Any errors associated with a field are available as {{ form.fieldname.errors }} . We can use these form variables to construct a custom template for our contact form:

姣忎竴涓瓧娈甸儴浠(<input type=”text”>, <select>, <textarea>, 鎴栬呯被浼)閮藉彲浠ラ氳繃璁块棶{{form.瀛楁鍚峿}杩涜鍗曠嫭鐨勬覆鏌撱備换浣曡窡瀛楁鐩稿叧鐨勯敊璇兘鍙互閫氳繃{{form.fieldname.errors}}璁块棶銆傛垜浠彲浠ュ悓杩欎簺form鐨勫彉閲忔潵涓烘垜浠殑琛ㄥ崟鏋勯犱竴涓嚜瀹氫箟鐨勬ā鏉匡細

<form action="." method="POST">
    <div class="fieldWrapper">
        {{ form.topic.errors }}
        <label for="id_topic">Kind of feedback:</label>
        {{ form.topic }}
    </div>
    <div class="fieldWrapper">
        {{ form.message.errors }}
        <label for="id_message">Your message:</label>
        {{ form.message }}
    </div>
    <div class="fieldWrapper">
        {{ form.sender.errors }}
        <label for="id_sender">Your email (optional):</label>
        {{ form.sender }}
    </div>
    <p><input type="submit" value="Submit"></p>
</form>

{{ form.message.errors }} will display as a <ul class="errorlist"> if errors are present and a blank string if the field is valid (or the form is unbound). We can also treat form.message.errors as a Boolean or even iterate over it as a list, for example:

{{ form.message.errors }} 浼氬湪 <ul class="errorlist"> 閲岄潰鏄剧ず锛屽鏋滃瓧娈垫槸鍚堟硶鐨勶紝鎴栬協orm娌℃湁琚粦瀹氾紝灏辨樉绀轰竴涓┖瀛楃涓层傛垜浠繕鍙互鎶 form.message.errors 褰撲綔涓涓竷灏斿兼垨鑰呭綋瀹冩槸list鍦ㄤ笂闈㈠仛杩唬锛

<div class="fieldWrapper{% if form.message.errors %} errors{% endif %}">
    {% if form.message.errors %}
        <ol>
        {% for error in form.message.errors %}
            <li><strong>{{ error|escape }}</strong></li>
        {% endfor %}
        </ol>
    {% endif %}
    {{ form.message }}
</div>

In the case of validation errors, this will add an errors class to the containing <div> and display the list of errors in an ordered list.

鍦ㄦ牎楠屽け璐ョ殑鎯呭喌涓, 杩欐浠g爜浼氬湪鍖呭惈閿欒瀛楁鐨刣iv鐨刢lass灞炴т腑澧炲姞涓涓”errors”锛屽湪涓涓湁搴忓垪琛ㄤ腑鏄剧ず閿欒淇℃伅銆

Creating Forms from Models

浠庢ā鍨嬪垱寤鸿〃鍗

Lets build something a little more interesting: a form that submits a new publisher to our book application from Chapter 5.

鎴戜滑寮勪釜鏈夎叮鐨勪笢瑗垮惂锛氫竴涓柊鐨刦orm锛屾彁浜や竴涓柊鍑虹増鍟嗙殑淇℃伅鍒版垜浠浜旂珷鐨刡ook搴旂敤銆

An important rule of thumb in software development that Django tries to adhere to is Dont Repeat Yourself (DRY). Andy Hunt and Dave Thomas in The Pragmatic Programmer define this as follows:

涓涓潪甯搁噸瑕佺殑Django鐨勫紑鍙戠悊蹇靛氨鏄笉瑕侀噸澶嶄綘鑷繁(DRY)銆侫ny Hunt鍜孌ave Thomas鍦ㄣ婂疄鐢ㄤ富涔夌▼搴忓憳銆嬮噷瀹氫箟浜嗚繖涓師鍒欙細

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.

鍦ㄧ郴缁熷唴閮紝姣忎竴鏉★紙棰嗗煙鐩稿叧鐨勶級鐭ヨ瘑鐨勭墖鏂兘蹇呴』鏈変竴涓崟鐙殑锛屾棤姝т箟鐨勶紝姝e紡鐨勮〃杩般

Our Publisher model class says that a publisher has a name, address, city, state_province, country, and website. Duplicating this information in a form definition would break the DRY rule. Instead, we can use a useful shortcut: form_for_model() :

鎴戜滑鐨勫嚭鐗堝晢妯″瀷鎷ユ湁涓涓悕瀛楋紝鍦板潃锛屽煄甯傦紝宸烇紙鐪侊級锛屽浗瀹跺拰缃戠珯銆傚湪form涓噸澶嶈繖涓俊鎭棤鐤戣繚鍙嶄簡DRY鍘熷垯銆傛垜浠彲浠ヤ娇鐢ㄤ竴涓嵎寰勶細form_for_model()锛

from models import Publisher
from django.newforms import form_for_model

PublisherForm = form_for_model(Publisher)

PublisherForm is a Form subclass, just like the ContactForm class we created manually earlier on. We can use it in much the same way:

PublisherForm鏄竴涓狥orm瀛愮被锛屽儚鍒氬垰鎵嬪伐鍒涘缓鐨凜ontactForm绫讳竴鏍枫傛垜浠彲浠ュ儚鍒氭墠涓鏍蜂娇鐢ㄥ畠锛

from forms import PublisherForm

def add_publisher(request):
    if request.method == 'POST':
        form = PublisherForm(request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect('/add_publisher/thanks/')
    else:
        form = PublisherForm()
    return render_to_response('books/add_publisher.html', {'form': form})

The add_publisher.html file is almost identical to our original contact.html template, so it has been omitted. Also remember to add a new pattern to the URLconf: (r'^add_publisher/$', 'mysite.books.views.add_publisher') .

add_publisher.html 鏂囦欢鍑犱箮璺熸垜浠殑contact.html妯℃澘涓鏍凤紝鎵浠ヤ笉璧樿堪浜嗐傝寰楀湪URLConf閲岄潰鍔犱笂锛 (r'^add_publisher/$', 'mysite.books.views.add_publisher') .

Theres one more shortcut being demonstrated here. Since forms derived from models are often used to save new instances of the model to the database, the form class created by form_for_model includes a convenient save() method. This deals with the common case; youre welcome to ignore it if you want to do something a bit more involved with the submitted data.

杩樻湁涓涓揩鎹风殑鏂规硶銆傚洜涓轰粠妯″瀷鑰屾潵鐨勮〃鍗曠粡甯歌鐢ㄦ潵鎶婃柊鐨勬ā鍨嬬殑瀹炰緥淇濆瓨鍒版暟鎹簱锛屼粠 form_for_model 鑰屾潵鐨勮〃鍗曞璞″寘鍚竴涓 save() 鏂规硶銆備竴鑸儏鍐典笅澶熺敤浜嗭紱浣犳兂瀵规彁浜ょ殑鏁版嵁浣滆繘涓姝ョ殑澶勭悊鐨勮瘽锛屾棤瑙嗗畠灏卞ソ浜嗐

form_for_instance() is a related method that can create a preinitialized form from an instance of a model class. This is useful for creating edit forms.

form_for_instance() 鏄彟澶栦竴涓柟娉曪紝鐢ㄤ簬浠庝竴涓ā鍨嬪璞′腑浜х敓涓涓垵濮嬪寲杩囩殑琛ㄥ崟瀵硅薄锛岃繖涓綋鐒剁粰鈥滅紪杈戔濊〃鍗曟彁渚涗簡鏂逛究銆

Whats Next?

涓嬩竴姝ワ紵

This chapter concludes the introductory material in this book. The next 13 chapters deal with various advanced topics, including generating content other than HTML (Chapter 11), security (Chapter 19), and deployment (Chapter 20).

杩欎竴绔犲凡缁忓畬鎴愪簡杩欐湰涔︾殑浠嬬粛鎬х殑鏉愭枡銆傛帴涓嬫潵鐨勫崄涓変釜绔犺妭璁ㄨ浜嗕竴浜涢珮绾х殑璇濋锛屽寘鎷敓鎴愰潪html鍐呭锛堢11绔狅級锛屽畨鍏紙绗19绔狅級鍜岄儴缃诧紙绗20绔狅級銆

After these first seven chapters, you should know enough to start writing your own Django projects. The rest of the material in this book will help fill in the missing pieces as you need them.

鍦ㄦ湰涔︽渶鍒濈殑涓冪珷鍚庯紝鎴戜滑锛堢粓浜庯級瀵逛簬浣跨敤Django鏋勫缓鑷繁鐨勭綉绔欏凡缁忕煡閬撶殑澶熷浜嗭紝鎺ヤ笅鏉ョ殑鍐呭鍙互鍦ㄩ渶瑕佺殑鏃跺欓槄璇汇

Well start in Chapter 8 by doubling back and taking a closer look at views and URLconfs (introduced first in Chapter 3).

绗叓绔犻噷鎴戜滑浼氭洿杩涗竴姝ュ湴浠嬬粛瑙嗗浘鍜孶RLConfs锛堜粙缁嶈绗笁绔狅級銆

Copyright 2006 Adrian Holovaty and Jacob Kaplan-Moss.
This work is licensed under the GNU Free Document License.
Hosting graciously provided by media temple
Chinese translate hosting by py3k.cn.