| ID | English原文 | 中文翻译 | 最近翻译记录 | 状态 | 操作 |
|---|---|---|---|---|---|
| 0#翻译 | Chapter 7: Forms | 第7章 表单 | 翻译 | ||
| 3#翻译 | HTML forms are the backbone of interactive Web sites, from the simplicity of Google's single search box to ubiquitous blog comment-submission forms to complex custom data-entry interfaces. | 从Google的简朴的单个搜索框,到常见的Blog评论提交表单,再到复杂的自定义数据输入接口,HTML表单一直是交互性网站的支柱。 | 翻译 | ||
| 4#翻译 | This chapter covers how you can use Django to access user-submitted form data, validate it and do something with it. | 本章介绍如何用Django对用户通过表单提交的数据进行访问、有效性检查以及其它处理。 | 翻译 | ||
| 5#翻译 | Along the way, we'll cover <literal>HttpRequest</literal> and <literal>Form</literal> objects. | 与此同时,我们将介绍HttpRequest对象和Form对象。 | 翻译 | ||
| 7#翻译 | Getting Data From the Request Object | 从Request对象中获取数据 | 翻译 | ||
| 9#翻译 | We introduced <literal>HttpRequest</literal> objects in Chapter 3 when we first covered view functions, but we didn't have much to say about them at the time. | 我们在第三章讲述View的函数时已经介绍过HttpRequest对象了,但当时并没有讲太多。 | 翻译 | ||
| 10#翻译 | Recall that each view function takes an <literal>HttpRequest</literal> object as its first parameter, as in our <literal>hello()</literal> view: | 让我们回忆下:每个view函数的第一个参数是一个HttpRequest对象,就像下面这个hello()函数: | 翻译 | ||
| 13#翻译 | <literal>HttpRequest</literal> objects, such as the variable <literal>request</literal> here, have a number of interesting attributes and methods that you should familiarize yourself with, so that you know what's possible. | HttpRequest对象,比如上面代码里的request变量,拥有大量有趣的属性和方法,你应该熟悉它们,以便知道能拿它们来做些什么。 | 翻译 | ||
| 14#翻译 | You can use these attributes to get information about the current request (i.e., the user/Web browser that's loading the current page on your Django-powered site), at the time the view function is executed. | 在view函数的执行过程中,你可以用这些属性来获取当前request的一些信息(比如,你正在加载这个页面的用户是谁,或者用的是什么浏览器)。 | 翻译 | ||
| 16#翻译 | Information About the URL | URL相关信息 | 翻译 | ||
| 18#翻译 | <literal>HttpRequest</literal> objects contain several pieces of information about the currently requested URL: | HttpRequest对象包含当前请求的URL的一些信息: | 翻译 | ||
| 20#翻译 | <table><tgroup cols="3"><colspec colwidth="27"/><colspec colwidth="56"/><colspec colwidth="31"/><thead><row><entry> | <table><tgroup cols="3"><colspec colwidth="27"/><colspec colwidth="56"/><colspec colwidth="31"/><thead><row><entry> | 翻译 | ||
| 21#翻译 | Attribute/method | 属性/方法 | 翻译 | ||
| 23#翻译 | Description | 说明 | 翻译 | ||
| 25#翻译 | Example | 举例 | 翻译 | ||
| 27#翻译 | <literal>request.path</literal> | <literal>request.path</literal> | 翻译 | ||
| 29#翻译 | The full path, not including the domain but including the leading slash. | 除域名以外的请求路径,以正斜杠开头 | 翻译 | ||
| 31#翻译 | <literal>"/hello/"</literal> | <literal>"/hello/"</literal> | 翻译 | ||
| 33#翻译 | <literal>request.get_host()</literal> | <literal>request.get_host()</literal> | 翻译 | ||
| 35#翻译 | The host (i.e., the domain, in common parlance). | 主机名(比如,通常所说的域名) | 翻译 | ||
| 37#翻译 | <literal>"127.0.0.1:8000"</literal> or <literal>"www.example.com"</literal> | <literal>"127.0.0.1:8000"</literal> or <literal>"www.example.com"</literal> | 翻译 | ||
| 39#翻译 | <literal>request.get_full_path()</literal> | <literal>request.get_full_path()</literal> | 翻译 | ||
| 41#翻译 | The <literal>path</literal> , plus a query string (if available). | 请求路径,可能包含查询字符串 | 翻译 | ||
| 43#翻译 | <literal>"/hello/?print=true"</literal> | <literal>"/hello/?print=true"</literal> | 翻译 | ||
| 45#翻译 | <literal>request.is_secure()</literal> | <literal>request.is_secure()</literal> | 翻译 | ||
| 47#翻译 | <literal>True</literal> if the request was made via HTTPS. | 如果通过HTTPS访问,则此方法返回True, | 翻译 | ||
| 48#翻译 | Otherwise, <literal>False</literal> . | 否则返回False | 翻译 | ||
| 50#翻译 | <literal>True</literal> or <literal>False</literal> | <literal>True</literal> 或者 <literal>False</literal> | 翻译 | ||
| 52#翻译 | Always use these attributes/methods instead of hard-coding URLs in your views. | 在view函数里,要始终用这个属性或方法来得到URL,而不要手动输入。 | 翻译 | ||
| 53#翻译 | This makes for more flexible code that can be reused in other places. | 这会使得代码更加灵活,以便在其它地方重用。 | 翻译 | ||
| 54#翻译 | A simplistic example: | 下面是一个简单的例子: | 翻译 | ||
| 57#翻译 | Other Information About the Request | 有关request的其它信息 | 翻译 | ||
| 59#翻译 | <literal>request.META</literal> is a Python dictionary containing all available HTTP headers for the given request including the user's IP address and user agent (generally the name and version of the Web browser). | request.META 是一个Python字典,包含了所有本次HTTP请求的Header信息,比如用户IP地址和用户Agent(通常是浏览器的名称和版本号)。 | 翻译 | ||
| 60#翻译 | Note that the full list of available headers depends on which headers the user sent and which headers your Web server sets. | 注意,Header信息的完整列表取决于用户所发送的Header信息和服务器端设置的Header信息。 | 翻译 | ||
| 61#翻译 | Some commonly available keys in this dictionary are: | 这个字典中几个常见的键值有: | 翻译 | ||
| 63#翻译 | <literal>HTTP_REFERER</literal> The referring URL, if any. | <literal>HTTP_REFERER</literal>,请求的链接地址,如果有的话。 | 翻译 | ||
| 64#翻译 | (Note the misspelling of <literal>REFERER</literal> .) | (请注意,它是<literal>REFERRER</literal>的笔误。) | 翻译 | ||
| 66#翻译 | <literal>HTTP_USER_AGENT</literal> The user's browser's user-agent string, if any. | <literal>HTTP_USER_AGENT</literal>,用户浏览器的user-agent字符串,如果有的话。 | 翻译 | ||
| 67#翻译 | This looks something like: | 例如: | 翻译 | ||
| 68#翻译 | <literal>"Mozilla/5.0 (X11; U; Linux i686; fr-FR; rv:1.8.1.17) Gecko/20080829 Firefox/2.0.0.17"</literal> . | <literal>"Mozilla/5.0 (X11; U; Linux i686; fr-FR; rv:1.8.1.17) Gecko/20080829 Firefox/2.0.0.17"</literal> . | 翻译 | ||
| 70#翻译 | <literal>REMOTE_ADDR</literal> The IP address of the client, e.g., <literal>"12.345.67.89"</literal> . (If the request has passed through any proxies, then this might be a comma-separated list of IP addresses, e.g., <literal>"12.345.67.89,23.456.78.90"</literal> .) | <literal>REMOTE_ADDR</literal> 客户端IP,如:<literal>"12.345.67.89"</literal> 。(如果申请是经过代理服务器的话,那么它可能是以逗号分割的多个IP地址,如:<literal>"12.345.67.89,23.456.78.90"</literal> 。) | 翻译 | ||
| 72#翻译 | Note that because <literal>request.META</literal> is just a basic Python dictionary, you'll get a <literal>KeyError</literal> exception if you try to access a key that doesn't exist. | 注意,因为 request.META 是一个普通的Python字典,因此当你试图访问一个不存在的键时,会触发一个KeyError异常。 | 翻译 | ||
| 73#翻译 | (Because HTTP headers are <emphasis>external</emphasis> data that is, they're submitted by your users' browsers they shouldn't be trusted, and you should always design your application to fail gracefully if a particular header is empty or doesn't exist.) You should either use a <literal>try</literal> /<literal>except</literal> clause or the <literal>get()</literal> method to handle the case of undefined keys: | (HTTP header信息是由用户的浏览器所提交的、不应该给予信任的<emphasis>外部</emphasis>数据,因此你应该好好设计你的程序以便当一个特定的Header数据不存在时,给出一个优雅的回应。)你应该用 try/except 语句,或者用Python字典的 get() 方法来处理这些操作未定义键的情况: | 翻译 | ||
| 76#翻译 | We encourage you to write a small view that displays all of the <literal>request.META</literal> data so you can get to know what's in there. | 我们鼓励你动手写一个简单的view函数来显示 request.META 的所有数据,这样你就知道里面有什么了。 | 翻译 | ||
| 77#翻译 | Heres what that view might look like: | 这个view函数可能是这样的: | 翻译 | ||
| 80#翻译 | As an exercise, see whether you can convert this view to use Django's template system instead of hard-coding the HTML. | 做为一个练习,看你自己能不能把上面这个view函数改用Django模板系统来实现,而不是上面这样来手动输入HTML代码。 | 翻译 | ||
| 81#翻译 | Also try adding <literal>request.path</literal> and the other <literal>HttpRequest</literal> methods from the previous section. | 也可以试着把前面提到的 request.path 方法或 HttpRequest 对象的其它方法加进去。 | 翻译 | ||
| 83#翻译 | Information About Submitted Data | 提交的数据信息 | 翻译 | ||
| 85#翻译 | Beyond basic metadata about the request, <literal>HttpRequest</literal> objects have two attributes that contain information submitted by the user: | 除了基本的元数据,HttpRequest对象还有两个属性包含了用户所提交的信息: | 翻译 | ||
| 86#翻译 | <literal>request.GET</literal> and <literal>request.POST</literal> . Both of these are dictionary-like objects that give you access to <literal>GET</literal> and <literal>POST</literal> data. | request.GET 和 request.POST。二者都是类字典对象,你可以通过它们来访问GET和POST数据。 | 翻译 | ||
| 88#翻译 | Dictionary-like objects | 类字典对象 | 翻译 | ||
| 90#翻译 | When we say <literal>request.GET</literal> and <literal>request.POST</literal> are dictionary-like objects, we mean that they behave like standard Python dictionaries but aren't technically dictionaries under the hood. | 我们说“request.GET和request.POST是类字典对象”,意思是他们的行为像Python里标准的字典对象,但在技术底层上他们不是标准字典对象。 | 翻译 | ||
| 91#翻译 | For example, <literal>request.GET</literal> and <literal>request.POST</literal> both have <literal>get()</literal> , <literal>keys()</literal> and <literal>values()</literal> methods, and you can iterate over the keys by doing <literal>for key in request.GET</literal> . | 比如说,request.GET和request.POST都有get()、keys()和values()方法,你可以用用 for key in request.GET 获取所有的键。 | 翻译 | ||
| 93#翻译 | So why the distinction? | 那到底有什么区别呢? | 翻译 | ||
| 94#翻译 | Because both <literal>request.GET</literal> and <literal>request.POST</literal> have additional methods that normal dictionaries don't have. | 因为request.GET和request.POST拥有一些普通的字典对象所没有的方法。 | 翻译 | ||
| 95#翻译 | Well get into these in a short while. | 我们会稍后讲到。 | 翻译 | ||
| 97#翻译 | You might have encountered the similar term file-like objects Python objects that have a few basic methods, like <literal>read()</literal> , that let them act as stand-ins for real file objects. | 你可能以前遇到过相似的名字:类文件对象,这些Python对象有一些基本的方法,如read(),用来做真正的Python文件对象的代用品。 | 翻译 | ||
| 99#翻译 | <literal>POST</literal> data generally is submitted from an HTML <literal><form></literal> , while <literal>GET</literal> data can come from a <literal><form></literal> or the query string in the page's URL. | POST数据是来自HTML中的〈form〉标签提交的,而GET数据可能来自〈form〉提交也可能是URL中的查询字符串(the query string)。 | 翻译 | ||
| 101#翻译 | A Simple Form-Handling Example | 一个简单的表单处理示例 | 翻译 | ||
| 103#翻译 | Continuing this book's ongoing example of books, authors and publishers, let's create a simple view that lets users search our book database by title. | 继续本书一直进行的关于书籍、作者、出版社的例子,我们现在来创建一个简单的view函数以便让用户可以通过书名从数据库中查找书籍。 | 翻译 | ||
| 105#翻译 | Generally, there are two parts to developing a form: | 通常,表单开发分为两个部分: | 翻译 | ||
| 106#翻译 | the HTML user interface and the backend view code that processes the submitted data. | 前端HTML页面用户接口和后台对所提交数据进行处理的view函数。 | 翻译 | ||
| 107#翻译 | The first part is easy; let's just set up a view that displays a search form: | 第一部分很简单;现在我们来建立个view来显示一个搜索表单: | 翻译 | ||
| 110#翻译 | As we learned in Chapter 3, this view can live anywhere on your Python path. | 在第三章已经学过,这个view函数可以放到Python的搜索路径的任何位置。 | 翻译 | ||
| 111#翻译 | For sake of argument, put it in <literal>books/views.py</literal> . | 为了便于讨论,咱们将它放在 books/views.py 里。 | 翻译 | ||
| 113#翻译 | The accompanying template, <literal>search_form.html</literal> , could look like this: | 这个 search_form.html 模板,可能看起来是这样的: | 翻译 | ||
| 116#翻译 | The URLpattern in <literal>urls.py</literal> could look like this: | 而 urls.py 中的 URLpattern 可能是这样的: | 翻译 | ||
| 119#翻译 | (Note that we're importing the <literal>views</literal> module directly, instead of something like <literal>from mysite.views import search_form</literal> , because the former is less verbose. | (注意,我们直接将views模块import进来了,而不是用类似 from mysite.views import search_form 这样的语句,因为前者看起来更简洁。 | 翻译 | ||
| 120#翻译 | We'll cover this importing approach in more detail in Chapter 8.) | 我们将在第8章讲述更多的关于import的用法。) | 翻译 | ||
| 122#翻译 | Now, if you run the <literal>runserver</literal> and visit <literal>http://127.0.0.1:8000/search-form/</literal> , you'll see the search interface. | 现在,如果你运行 <literal>runserver</literal> 命令,然后访问<literal>http://127.0.0.1:8000/search-form/</literal>,你会看到搜索界面。 | 翻译 | ||
| 123#翻译 | Simple enough. | 非常简单。 | 翻译 | ||
| 125#翻译 | Try submitting the form, though, and you'll get a Django 404 error. | 不过,当你通过这个form提交数据时,你会得到一个Django 404错误。 | 翻译 | ||
| 126#翻译 | The form points to the URL <literal>/search/</literal> , which hasn't yet been implemented. | 这个Form指向的URL <literal>/search/</literal> 还没有被实现。 | 翻译 | ||
| 127#翻译 | Let's fix that with a second view function: | 让我们添加第二个视图函数并设置URL: | 翻译 | ||
| 130#翻译 | For the moment, this merely displays the user's search term, so we can make sure the data is being submitted to Django properly, and so you can get a feel for how the search term flows through the system. | 暂时先只显示用户搜索的字词,以确定搜索数据被正确地提交给了Django,这样你就会知道搜索数据是如何在这个系统中传递的。 | 翻译 | ||
| 131#翻译 | In short: | 简而言之: | 翻译 | ||
| 133#翻译 | The HTML <literal><form></literal> defines a variable <literal>q</literal> . When it's submitted, the value of <literal>q</literal> is sent via <literal>GET</literal> (<literal>method="get"</literal> ) to the URL <literal>/search/</literal> . | 在HTML里我们定义了一个变量q。当提交表单时,变量q的值通过GET(method="get")附加在URL /search/上。 | 翻译 | ||
| 135#翻译 | The Django view that handles the URL <literal>/search/</literal> (<literal>search()</literal> ) has access to the <literal>q</literal> value in <literal>request.GET</literal> . | 处理/search/(search())的视图通过request.GET来获取q的值。 | 翻译 | ||
| 137#翻译 | An important thing to point out here is that we explicitly check that <literal>'q'</literal> exists in <literal>request.GET</literal> . As we pointed out in the <literal>request.META</literal> section above, you shouldn't trust anything submitted by users or even assume that they've submitted anything in the first place. | 需要注意的是我们在这里明确地判断q是否包含在request.GET中。就像上面request.META小节里面提到的,不应该相信任何用户提交过来的数据,甚至不应该假定用户提交过数据。 | 翻译 | ||
| 138#翻译 | If we didn't add this check, any submission of an empty form would raise <literal>KeyError</literal> in the view: | 在这里若没有进行检测,那么用户提交一个空的表单将引发KeyError异常: | 翻译 | ||
| 141#翻译 | Query string parameters | 查询字符串参数 | 翻译 | ||
| 143#翻译 | Because <literal>GET</literal> data is passed in the query string (e.g., <literal>/search/?q=django</literal> ), you can use <literal>request.GET</literal> to access query string variables. | 因为使用GET方法的数据是通过查询字符串的方式传递的(例如/search/?q=django),所以我们可以使用requet.GET来获取这些数据。 | 翻译 | ||
| 144#翻译 | In Chapter 3's introduction of Django's URLconf system, we compared Djangos pretty URLs to more traditional PHP/Java URLs such as <literal>/time/plus?hours=3</literal> and said we'd show you how to do the latter in Chapter 7. Now you know how to access query string parameters in your views (like <literal>hours=3</literal> in this example) use <literal>request.GET</literal> . | 第三章介绍Django的URLconf系统时我们比较了Django的简洁的URL与PHP/Java传统的URL,我们提到将在第七章讲述如何使用传统的URL。通过刚才的介绍,我们知道在视图里可以使用request.GET来获取传统URL里的查询字符串(例如hours=3)。 | 翻译 | ||
| 146#翻译 | <literal>POST</literal> data works the same way as <literal>GET</literal> data just use <literal>request.POST</literal> instead of <literal>request.GET</literal> . What's the difference between <literal>GET</literal> and <literal>POST</literal> ? Use <literal>GET</literal> when the act of submitting the form is just a request to get data. | 获取使用POST方法的数据与GET的相似,只是使用request.POST代替了request.GET。那么,POST与GET之间有什么不同?当我们提交表单仅仅是为了获取数据时就可以用GET; | 翻译 | ||
| 147#翻译 | Use <literal>POST</literal> whenever the act of submitting the form will have some side effect <emphasis>changing</emphasis> data, or sending an e-mail, or something else that's beyond simple <emphasis>display</emphasis> of data. | 而当我们提交表单时需要更改服务器数据的状态,或者说发送e-mail,或者其他不仅仅是获取并显示数据的时候就使用POST。 | 翻译 | ||
| 148#翻译 | In our book-search example, we're using <literal>GET</literal> because the query doesn't change any data on our server. | 在这个搜索书籍的例子里,我们使用GET,因为这个查询不会更改服务器数据的状态。 | 翻译 | ||
| 149#翻译 | (See <reference name="http://www.w3.org/2001/tag/doc/whenToUseGet.html" refuri="http://www.w3.org/2001/tag/doc/whenToUseGet.html">http://www.w3.org/2001/tag/doc/whenToUseGet.html</reference> if you want to learn more about <literal>GET</literal> and <literal>POST</literal> .) | (如果你有兴趣了解更多关于<literal>GET</literal>和<literal>POST</literal>的知识,可以参见<reference name="http://www.w3.org/2001/tag/doc/whenToUseGet.html" refuri="http://www.w3.org/2001/tag/doc/whenToUseGet.html">http://www.w3.org/2001/tag/doc/whenToUseGet.html</reference>。) | 翻译 | ||
| 151#翻译 | Now that we've verified <literal>request.GET</literal> is being passed in properly, let's hook the users search query into our book database (again, in <literal>views.py</literal> ): | 既然已经确认用户所提交的数据是有效的,那么接下来就可以从数据库中查询这个有效的数据(同样,在views.py里操作): | 翻译 | ||
| 154#翻译 | A couple of notes on what we did here: | 让我们来分析一下上面的代码: | 翻译 | ||
| 156#翻译 | Aside from checking that <literal>'q'</literal> exists in <literal>request.GET</literal> , we also make sure that <literal>request.GET['q']</literal> is a non-empty value before passing it to the database query. | 除了检查q是否存在于request.GET之外,我们还检查来reuqest.GET['q']的值是否为空。 | 翻译 | ||
| 158#翻译 | We're using <literal>Book.objects.filter(title__icontains=q)</literal> to query our book table for all books whose title includes the given submission. | 我们使用Book.objects.filter(title__icontains=q)获取数据库中标题包含q的书籍。 | 翻译 | ||
| 159#翻译 | The <literal>icontains</literal> is a lookup type (as explained in Chapter 5 and Appendix B), and the statement can be roughly translated as Get the books whose title contains <literal>q</literal> , without being case-sensitive. | icontains是一个查询关键字(参看第五章和附录B)。这个语句可以理解为获取标题里包含q的书籍,不区分大小写。 | 翻译 | ||
| 161#翻译 | This is a very simple way to do a book search. | 这是实现书籍查询的一个很简单的方法。 | 翻译 | ||
| 162#翻译 | We wouldn't recommend using a simple <literal>icontains</literal> query on a large production database, as it can be slow. | 我们不推荐在一个包含大量产品的数据库中使用icontains查询,因为那会很慢。 | 翻译 | ||
| 163#翻译 | (In the real world, you'd want to use a custom search system of some sort. | (在真实的案例中,我们可以使用以某种分类的自定义查询系统。 | 翻译 | ||
| 164#翻译 | Search the Web for <emphasis>open-source full-text search</emphasis> to get an idea of the possibilities.) | 在网上搜索“开源 全文搜索”看看是否有好的方法。) | 翻译 | ||
| 166#翻译 | We pass <literal>books</literal> , a list of <literal>Book</literal> objects, to the template. | 最后,我们把books传给模板,它是一个包含Book对象的列表。 | 翻译 | ||
| 167#翻译 | The template code for <literal>search_results.html</literal> might include something like this: | 查询结果的显示模板search_results.html如下所示: | 翻译 | ||
| 170#翻译 | Note usage of the <literal>pluralize</literal> template filter, which outputs an s if appropriate, based on the number of books found. | 注意这里pluralize的使用,这个过滤器在适当的时候会输出s(例如找到多本书籍)。 | 翻译 | ||
| 172#翻译 | Improving Our Simple Form-Handling Example | 改进表单 | 翻译 | ||
| 174#翻译 | As in previous chapters, we've shown you the simplest thing that could possibly work. | 同上一章一样,我们先从最为简单、有效的例子开始。 | 翻译 | ||
| 175#翻译 | Now we'll point out some problems and show you how to improve it. | 现在我们再来找出这个简单的例子中的不足,然后改进他们。 | 翻译 | ||
| 177#翻译 | First, our <literal>search()</literal> view's handling of an empty query is poor were just displaying a <literal>"Please submit a search term."</literal> | 首先,search()视图对于空字符串的处理相当薄弱——仅显示一条"Please submit a search term."的提示信息。 | 翻译 | ||
| 178#翻译 | message, requiring the user to hit the browser's back button. | 若用户要重新填写表单必须自行点击“后退”按钮, | 翻译 | ||
| 179#翻译 | This is horrid and unprofessional, and if you ever actually implement something like this in the wild, your Django privileges will be revoked. | 这种做法既糟糕又不专业。如果在现实的案例中,我们这样子编写,那么Django的优势将荡然无存。 | 翻译 | ||
| 181#翻译 | It would be much better to redisplay the form, with an error above it, so that the user can try again immediately. | 在检测到空字符串时更好的解决方法是重新显示表单,并在表单上面给出错误提示以便用户立刻重新填写。 | 翻译 | ||
| 182#翻译 | The easiest way to do that would be to render the template again, like this: | 最简单的实现方法既是添加else分句重新渲染表单,代码如下: | 翻译 | ||
| 185#翻译 | (Note that we've included <literal>search_form()</literal> here so you can see both views in one place.) | (注意,将search_form()视图也包含进来以便查看。) | 翻译 | ||
| 187#翻译 | Here, we've improved <literal>search()</literal> to render the <literal>search_form.html</literal> template again, if the query is empty. | 这段代码里,我们改进来search()视图:在字符串为空时重新显示search_form.html。 | 翻译 | ||
| 188#翻译 | And because we need to display an error message in that template, we pass a template variable. | 并且给这个模板传递了一个变量error,记录着错误提示信息。 | 翻译 | ||
| 189#翻译 | Now we can edit <literal>search_form.html</literal> to check for the <literal>error</literal> variable: | 现在我们编辑一下search_form.html,检测变量error: | 翻译 | ||
| 192#翻译 | We can still use this template from our original view, <literal>search_form()</literal> , because <literal>search_form()</literal> doesn't pass <literal>error</literal> to the template so the error message won't show up in that case. | 我们修改了search_form()视图所使用的模板,因为search_form()视图没有传递error变量,所以在条用search_form视图时不会显示错误信息。 | 翻译 | ||
| 194#翻译 | With this change in place, it's a better application, but it now begs the question: | 通过上面的一些修改,现在程序变的好多了,但是现在出现一个问题: | 翻译 | ||
| 195#翻译 | is a dedicated <literal>search_form()</literal> view really necessary? | 是否有必要专门编写search_form()来显示表单? | 翻译 | ||
| 196#翻译 | As it stands, a request to the URL <literal>/search/</literal> (without any <literal>GET</literal> parameters) will display the empty form (but with an error). | 按实际情况来说,当一个请求发送至/search/(未包含GET的数据)后将会显示一个空的表单(带有错误信息)。 | 翻译 | ||
| 197#翻译 | We can remove the <literal>search_form()</literal> view, along with its associated URLpattern, as long as we change <literal>search()</literal> to hide the error message when somebody visits <literal>/search/</literal> with no <literal>GET</literal> parameters: | 所以,只要我们改变search()视图:当用户访问/search/并未提交任何GET参数时就隐藏错误信息,这样就可以去掉search_form()视图以及对应的URLpattern。 | 翻译 | ||
| 200#翻译 | In this updated view, if a user visits <literal>/search/</literal> with no <literal>GET</literal> parameters, he'll see the search form with no error message. | 在改进后的视图中,若用户访问/search/并且没有提交GET参数,那么他将看到一个没有错误信息的表单; | 翻译 | ||
| 201#翻译 | If a user submits the form with an empty value for <literal>'q'</literal> , he'll see the search form <emphasis>with</emphasis> an error message. | 如果用户提交了一个空表单,那么它将看到错误提示信息,还有表单; | 翻译 | ||
| 202#翻译 | And, finally, if a user submits the form with a non-empty value for <literal>'q'</literal> , he'll see the search results. | 最后,若用户提交了一个非空的值,那么他将看到搜索结果。 | 翻译 | ||
| 204#翻译 | We can make one final improvement to this application, to remove a bit of redundancy. | 最后,我们再稍微改进一下这个表单,去掉冗余的部分。 | 翻译 | ||
| 205#翻译 | Now that we've rolled the two views and URLs into one and <literal>/search/</literal> handles both search-form display and result display, the HTML <literal><form></literal> in <literal>search_form.html</literal> doesn't have to hard-code a URL. | 既然已经将两个视图与URLs合并起来,/search/视图管理着表单的显示以及结果的显示,那么在search_form.html里表单的action值就没有必要硬编码的指定URL。 | 翻译 | ||
| 206#翻译 | Instead of this: | 原先的代码是这样: | 翻译 | ||
| 209#翻译 | It can be changed to this: | 现在改成这样: | 翻译 | ||
| 212#翻译 | The <literal>action=""</literal> means Submit the form to the same URL as the current page. | action=""意味着表单将提交给与当前页面相同的URL。 | 翻译 | ||
| 213#翻译 | With this change in place, you won't have to remember to change the <literal>action</literal> if you ever hook the <literal>search()</literal> view to another URL. | 这样,如果你将<literal>search()</literal>视图指向其它页面的话,你也用不着再修改<literal>action</literal>。 | 翻译 | ||
| 215#翻译 | Simple validation | 简单的验证 | 翻译 | ||
| 217#翻译 | Our search example is still reasonably simple, particularly in terms of its data validation; we're merely checking to make sure the search query isn't empty. | 我们的搜索示例仍然相当地简单,特别从数据验证方面来讲;我们仅仅只验证搜索关键值是否为空。 | 翻译 | ||
| 218#翻译 | Many HTML forms include a level of validation that's more complex than making sure the value is non-empty. | 然后许多HTML表单包含着比检测值是否为空更为复杂的验证。 | 翻译 | ||
| 219#翻译 | We've all seen the error messages on Web sites: | 我们都有在网站上见过类似以下的错误提示信息: | 翻译 | ||
| 221#翻译 | Please enter a valid e-mail address. | 请输入一个有效的email地址, | 翻译 | ||
| 222#翻译 | foo' is not an e-mail address. | foo' 并不是一个有效的e-mail地址。 | 翻译 | ||
| 224#翻译 | Please enter a valid five-digit U.S. | 请输入5位数的U.S | 翻译 | ||
| 225#翻译 | ZIP code. | 邮政编码, | 翻译 | ||
| 226#翻译 | 123' is not a ZIP code. | 123并非是一个有效的邮政编码。 | 翻译 | ||
| 228#翻译 | Please enter a valid date in the format YYYY-MM-DD. | 请输入YYYY-MM-DD格式的日期。 | 翻译 | ||
| 230#翻译 | Please enter a password that is at least 8 characters long and contains at least one number. | 请输入8位数以上并至少包含一个数字的密码。 | 翻译 | ||
| 232#翻译 | A note on JavaScript validation | 关于JavaScript验证 | 翻译 | ||
| 234#翻译 | This is beyond the scope of this book, but you can use JavaScript to validate data on the client side, directly in the browser. | 可以使用Javascript在客户端浏览器里对数据进行验证,这些知识已超出本书范围。 | 翻译 | ||
| 235#翻译 | But be warned: | 要注意: | 翻译 | ||
| 236#翻译 | even if you do this, you <emphasis>must</emphasis> validate data on the server side, too. | 即使在客户端已经做了验证,但是服务器端仍必须再验证一次。 | 翻译 | ||
| 237#翻译 | Some people have JavaScript turned off, and some malicious users might submit raw, unvalidated data directly to your form handler to see whether they can cause mischief. | 因为有些用户会将JavaScript关闭掉,并且还有一些怀有恶意的用户会尝试提交非法的数据来探测是否有可以攻击的机会。 | 翻译 | ||
| 239#翻译 | There's nothing you can do about this, other than <emphasis>always</emphasis> validate user-submitted data server-side (i.e., in your Django views). | 除了在服务器端对用户提交的数据进行验证(例如在视图里验证),我们没有其他办法。 | 翻译 | ||
| 240#翻译 | You should think of JavaScript validation as a bonus usability feature, not as your only means of validating. | JavaScript验证可以看作是额外的功能,但不能作为唯一的验证功能。 | 翻译 | ||
| 242#翻译 | Let's tweak our <literal>search()</literal> view so that it validates that the search term is less than or equal to 20 characters long. | 我们来调整一下search()视图,让她能够验证搜索关键词是否小于或等于20个字符。 | 翻译 | ||
| 243#翻译 | (For sake of example, let's say anything longer than that might make the query too slow.) How might we do that? | (为来让例子更为显著,我们假设如果关键词超过20个字符将导致查询十分缓慢)。那么该如何实现呢? | 翻译 | ||
| 244#翻译 | The simplest possible thing would be to embed the logic directly in the view, like this: | 最简单的方式就是将逻辑处理直接嵌入到视图里,就像这样: | 翻译 | ||
| 247#翻译 | Now, if you try submitting a search query greater than 20 characters long, it won't let you search; you'll get an error message. | 现在,如果尝试着提交一个超过20个字符的搜索关键词,系统不会执行搜索操作,而是显示一条错误提示信息。 | 翻译 | ||
| 248#翻译 | But that error message in <literal>search_form.html</literal> currently says <literal>"Please submit a search term."</literal> | 但是,search_form.html里的这条提示信息是:"Please submit a search term.",这显然是错误的, | 翻译 | ||
| 249#翻译 | so we'll have to change it to be accurate for both cases: | 所以我们需要更精确的提示信息: | 翻译 | ||
| 252#翻译 | There's something ugly about this. | 但像这样修改之后仍有一些问题。 | 翻译 | ||
| 253#翻译 | Our one-size-fits-all error message is potentially confusing. | 我们一成不变的提示信息很容易使人产生困惑: | 翻译 | ||
| 254#翻译 | Why should the error message for an empty form submission mention anything about a 20-character limit? | 提交一个空表单怎么会出现一个关于20个字符限制的提示? | 翻译 | ||
| 255#翻译 | Error messages should be specific, unambiguous and not confusing. | 所以,提示信息必须是详细的,明确的,不会产生疑议。 | 翻译 | ||
| 257#翻译 | The problem is in the fact that we're using a simple boolean value for <literal>error</literal> , whereas we should be using a <emphasis>list</emphasis> of error message strings. | 问题的实质在于我们只使用来一个布尔类型的变量来检测是否出错,而不是使用一个列表来记录相应的错误信息。 | 翻译 | ||
| 258#翻译 | Here's how we might fix that: | 我们需要做如下的调整: | 翻译 | ||
| 261#翻译 | Then, we need make a small tweak to the <literal>search_form.html</literal> template to reflect that it's now passed an <literal>errors</literal> list instead of an <literal>error</literal> boolean value: | 接着,我们要修改一下search_form.html模板,现在需要显示一个errors列表而不是一个布尔判断。 | 翻译 | ||
| 264#翻译 | Making a Contact Form | 编写Contact表单 | 翻译 | ||
| 266#翻译 | Although we iterated over the book search form example several times and improved it nicely, it's still fundamentally simple: | 虽然我们一直使用书籍搜索的示例表单,并将其改进得很完美,但它还是相当简陋: | 翻译 | ||
| 267#翻译 | just a single field, <literal>'q'</literal> . Because it's so simple, we didnt even use Djangos form library to deal with it. | 只包含一个字段,q。这简单的例子,我们不需要使用Django表单库来处理。 | 翻译 | ||
| 268#翻译 | But more complex forms call for more complex treatment and now we'll develop something more complex: | 但是复杂一点的表单就需要多方面的处理,我们现在来一下一个较为复杂的例子: | 翻译 | ||
| 269#翻译 | a site contact form. | 站点联系表单。 | 翻译 | ||
| 271#翻译 | This will be a form that lets site users submit a bit of feedback, along with an optional e-mail return address. | 这个表单包括用户提交的反馈信息,一个可选的e-mail回信地址。 | 翻译 | ||
| 272#翻译 | After the form is submitted and the data is validated, we'll automatically send the message via e-mail to the site staff. | 当这个表单提交并且数据通过验证后,系统将自动发送一封包含用户提交信息的e-mail给站点工作人员。 | 翻译 | ||
| 274#翻译 | We'll start with our template, <literal>contact_form.html</literal> . | 我们从contact_form.html模板入手: | 翻译 | ||
| 277#翻译 | We've defined three fields: | 我们定义了三个字段: | 翻译 | ||
| 278#翻译 | the subject, e-mail address and message. | 主题,e-mail和反馈信息。 | 翻译 | ||
| 279#翻译 | The second is optional, but the other two fields are required. | 除了e-mail字段为可选,其他两个字段都是必填项。 | 翻译 | ||
| 280#翻译 | Note we're using <literal>method="post"</literal> here instead of <literal>method="get"</literal> because this form submission has a side effect it sends an e-mail. | 注意,这里我们使用method="post"而非method="get",因为这个表单会有一个服务器端的操作:发送一封e-mail。 | 翻译 | ||
| 281#翻译 | Also, we copied the error-displaying code from our previous template <literal>search_form.html</literal> . | 并且,我们复制了前一个模板search_form.html中错误信息显示的代码。 | 翻译 | ||
| 283#翻译 | If we continue down the road established by our <literal>search()</literal> view from the previous section, a naive version of our <literal>contact()</literal> view might look like this: | 如果我们顺着上一节编写search()视图的思路,那么一个contact()视图代码应该像这样: | 翻译 | ||
| 286#翻译 | (If you're following along, you may be wondering whether to put this view in the <literal>books/views.py</literal> file. | (如果按照书中的示例做下来,这这里可能乎产生一个疑问:contact()视图是否要放在books/views.py这个文件里。 | 翻译 | ||
| 287#翻译 | It doesn't have anything to do with the books application, so should it live elsewhere? | 但是contact()视图与books应用没有任何关联,那么这个视图应该可以放在别的地方? | 翻译 | ||
| 288#翻译 | It's totally up to you; Django doesn't care, as long as youre able to point to the view from your URLconf. | 这无关紧要,只要在URLconf里正确设置URL与视图之间的映射,Django会正确处理的。 | 翻译 | ||
| 289#翻译 | Our personal preference would be to create a separate directory, <literal>contact</literal> , at the same level in the directory tree as <literal>books</literal> . This would contain an empty <literal>__init__.py</literal> and <literal>views.py</literal> .) | 笔者个人喜欢创建一个contact的文件夹,与books文件夹同级。这个文件夹中包括空的__init__.py和views.py两个文件。 | 翻译 | ||
| 291#翻译 | A couple of new things are happening here: | 现在来分析一下以上的代码: | 翻译 | ||
| 293#翻译 | We're checking that <literal>request.method</literal> is <literal>'POST'</literal> . This will only be true in the case of a form submission; it won't be true if somebody is merely viewing the contact form. | 确认request.method的值是'POST'。用户浏览表单时这个值并不存在,当且仅当表单被提交时这个值才出现。 | 翻译 | ||
| 294#翻译 | (In the latter case, <literal>request.method will be set to 'GET'</literal> , because in normal Web browsing, browsers use <literal>GET</literal> , not <literal>POST</literal> .) This makes it a nice way to isolate the form display case from the form processing case. | (在后面的例子中,request.method将会设置为'GET',因为在普通的网页浏览中,浏览器都使用GET,而非POST)。判断request.method的值很好地帮助我们将表单显示与表单处理隔离开来。 | 翻译 | ||
| 296#翻译 | Instead of <literal>request.GET</literal> , we're using <literal>request.POST</literal> to access the submitted form data. | 我们使用request.POST代替request.GET来获取提交过来的数据。 | 翻译 | ||
| 297#翻译 | This is necessary because the HTML <literal><form></literal> in <literal>contact_form.html</literal> uses <literal>method="post"</literal> . If this view is accessed via <literal>POST</literal> , then <literal>request.GET</literal> will be empty. | 这是必须的,因为contact_form.html里表单使用的是method="post"。如果在视图里通过POST获取数据,那么request.GET将为空。 | 翻译 | ||
| 299#翻译 | This time, we have <emphasis>two</emphasis> required fields, <literal>subject</literal> and <literal>message</literal> , so we have to validate both. | 这里,有两个必填项,subject 和 message,所以需要对这两个进行验证。 | 翻译 | ||
| 300#翻译 | Note that we're using <literal>request.POST.get()</literal> and providing a blank string as the default value; this is a nice, short way of handling both the cases of missing keys and missing data. | 注意,我们使用request.POST.get()方法,并提供一个空的字符串作为默认值;这个方法很好的解决了键丢失与空数据问题。 | 翻译 | ||
| 302#翻译 | Although the <literal>email</literal> field is not required, we still validate it if it is indeed submitted. | 虽然email非必填项,但如果它被提交了,我们也需进行验证。 | 翻译 | ||
| 303#翻译 | Our validation algorithm here is fragile we're just checking that the string contains an <literal>@</literal> character. | 我们的验证算法相当的薄弱,仅验证值是否包含@字符。 | 翻译 | ||
| 304#翻译 | In the real world, you'd want more robust validation (and Django provides it, which we'll show you very shortly). | 在实际应用中,需要更为健壮的验证机制(Django提供这些验证机制,稍候我们就会看到)。 | 翻译 | ||
| 306#翻译 | We're using the function <literal>django.core.mail.send_mail</literal> to send an e-mail. | 我们使用了django.core.mail.send_mail函数来发送e-mail。 | 翻译 | ||
| 307#翻译 | This function has four required arguments: | 这个函数有四个必选参数: | 翻译 | ||
| 308#翻译 | the e-mail subject, the e-mail body, the from address, and a list of recipient addresses. | 主题,正文,寄信人和收件人列表。 | 翻译 | ||
| 309#翻译 | <literal>send_mail</literal> is a convenient wrapper around Django's <literal>EmailMessage</literal> class, which provides advanced features such as attachments, multipart e-mails, and full control over e-mail headers. | send_mail是Django的EmailMessage类的一个方便的包装,EmailMessage类提供了更高级的方法,比如附件,多部分邮件,以及对于邮件头部的完整控制。 | 翻译 | ||
| 311#翻译 | Note that in order to send e-mail using <literal>send_mail()</literal> , your server must be configured to send mail, and Django must be told about your outbound e-mail server. | 注意,若要使用send_mail()函数来发送邮件,那么服务器需要配置成能够对外发送邮件,并且在Django中设置出站服务器地址。 | 翻译 | ||
| 312#翻译 | See <reference name="http://docs.djangoproject.com/en/dev/topics/email/" refuri="http://docs.djangoproject.com/en/dev/topics/email/">http://docs.djangoproject.com/en/dev/topics/email/</reference> for the specifics. | 参见规范:<reference name="http://docs.djangoproject.com/en/dev/topics/email/" refuri="http://docs.djangoproject.com/en/dev/topics/email/">http://docs.djangoproject.com/en/dev/topics/email/</reference> | 翻译 | ||
| 314#翻译 | After the e-mail is sent, we redirect to a success page by returning an <literal>HttpResponseRedirect</literal> object. | 当邮件发送成功之后,我们使用HttpResponseRedirect对象将网页重定向至一个包含成功信息的页面。 | 翻译 | ||
| 315#翻译 | We'll leave the implementation of that success page up to you (it's a simple view/URLconf/template), but we should explain why we initiate a redirect instead of, for example, simply calling <literal>render_to_response()</literal> with a template right there. | 包含成功信息的页面这里留给读者去编写(很简单 一个视图/URL映射/一份模板即可),但是我们要解释一下为何重定向至新的页面,而不是在模板中直接调用render_to_response()来输出。 | 翻译 | ||
| 317#翻译 | The reason: | 原因就是: | 翻译 | ||
| 318#翻译 | if a user hits Refresh on a page that was loaded via <literal>POST</literal> , that request will be repeated. | 若用户刷新一个包含POST表单的页面,那么请求将会重复发送。 | 翻译 | ||
| 319#翻译 | This can often lead to undesired behavior, such as a duplicate record being added to the database or, in our example, the e-mail being sent twice. | 这通常会造成非期望的结果,比如说重复的数据库记录;在我们的例子中,将导致发送两封同样的邮件。 | 翻译 | ||
| 320#翻译 | If the user is redirected to another page after the <literal>POST</literal> , then there's no chance of repeating the request. | 如果用户在POST表单之后被重定向至另外的页面,就不会造成重复的请求了。 | 翻译 | ||
| 322#翻译 | You should <emphasis>always</emphasis> issue a redirect for successful <literal>POST</literal> requests. | 我们应每次都给成功的POST请求做重定向。 | 翻译 | ||
| 323#翻译 | It's a Web development best practice. | 这就是Web开发的最佳实践。 | 翻译 | ||
| 325#翻译 | This view works, but those validation functions are kind of crufty. | contact()视图可以正常工作,但是它的验证功能有些复杂。 | 翻译 | ||
| 326#翻译 | Imagine processing a form with a dozen fields; would you really want to have to write all of those <literal>if</literal> statements? | 想象一下假如一个表单包含一打字段,我们真的将必须去编写每个域对应的if判断语句? | 翻译 | ||
| 328#翻译 | Another problem is <emphasis>form redisplay</emphasis> . In the case of validation errors, it's best practice to redisplay the form <emphasis>with</emphasis> the previously submitted data already filled in, so the user can see what he did wrong (and also so the user doesn't have to reenter data in fields that were submitted correctly). | 另外一个问题是表单的重新显示。若数据验证失败后,返回客户端的表单中各字段最好是填有原来提交的数据,以便用户查看哪里出现错误(用户也不需再次填写正确的字段值)。 | 翻译 | ||
| 329#翻译 | We <emphasis>could</emphasis> manually pass the <literal>POST</literal> data back to the template, but we'd have to edit each HTML field to insert the proper value in the proper place: | 我们可以手动地将原来提交的数据返回给模板,并且必须编辑HTML里的各字段来填充原来的值。 | 翻译 | ||
| 332#翻译 | This is a lot of cruft, and it introduces a lot of opportunities for human error. | 这看起来杂乱,且写的时候容易出错。 | 翻译 | ||
| 333#翻译 | We hope you're starting to see the opportunity for some higher-level library that handles form- and validation-related tasks. | 希望你开始明白使用高级库的用意——负责处理表单及相关校验任务。 | 翻译 | ||
| 335#翻译 | Your First Form Class | 第一个Form类 | 翻译 | ||
| 337#翻译 | Django comes with a form library, called <literal>django.forms</literal> , that handles many of the issues we've been exploring this chapter from HTML form display to validation. | Django带有一个form库,称为django.forms,这个库可以处理我们本章所提到的包括HTML表单显示以及验证的问题。 | 翻译 | ||
| 338#翻译 | Let's dive in and rework our contact form application using the Django forms framework. | 接下来我们来深入了解一下form库,并使用她来重写contact表单应用。 | 翻译 | ||
| 340#翻译 | Django's newforms library | Django的newforms库 | 翻译 | ||
| 342#翻译 | Throughout the Django community, you might see chatter about something called <literal>django.newforms</literal> . When people speak of <literal>django.newforms</literal> , they're talking about what is now <literal>django.forms</literal> the library covered by this chapter. | 在Django社区上会经常看到django.newforms这个词语。当人们讨论django.newforms,其实就是我们本章里面介绍的django.forms。 | 翻译 | ||
| 344#翻译 | The reason for this name change is historic. | 改名其实有历史原因的。 | 翻译 | ||
| 345#翻译 | When Django was first released to the public, it had a complicated, confusing forms system, <literal>django.forms</literal> . It was completely rewritten, and the new version was called <literal>django.newforms</literal> so that people could still use the old system. | 当Django一次向公众发行时,它有一个复杂难懂的表单系统:<literal>django.forms</literal>。后来它被完全重写了,新的版本改叫作:<literal>django.newforms</literal>,这样人们还可以使用旧版本。 | 翻译 | ||
| 346#翻译 | When Django 1.0 was released, the old <literal>django.forms</literal> went away, and <literal>django.newforms</literal> became <literal>django.forms</literal> . | 当Django 1.0发布时,旧版本<literal>django.forms</literal>就不再使用了,而<literal>django.newforms</literal>也终于可以名正言顺的叫做:<literal>django.forms</literal>。 | 翻译 | ||
| 348#翻译 | The primary way to use the forms framework is to define a <literal>Form</literal> class for each HTML <literal><form></literal> you're dealing with. | 表单框架最主要的用法是,为每一个将要处理的HTML的<literal> <Form></literal> 定义一个<literal>Form</literal>类。 | 翻译 | ||
| 349#翻译 | In our case, we only have one <literal><form></literal> , so we'll have one <literal>Form</literal> class. | 在这个例子中,我们只有一个<literal> <Form></literal> ,因此我们只需定义一个<literal>Form</literal>类。 | 翻译 | ||
| 350#翻译 | This class can live anywhere you want including directly in your <literal>views.py</literal> file but community convention is to keep <literal>Form</literal> classes in a separate file called <literal>forms.py</literal> . Create this file in the same directory as your <literal>views.py</literal> , and enter the following: | 这个类可以存在于任何地方,甚至直接写在<literal> views.py</literal> 文件里也行,但是社区的惯例是把<literal>Form</literal>类都放到一个文件中:<literal>forms.py</literal>。在存放<literal> views.py</literal> 的目录中,创建这个文件,然后输入: | 翻译 | ||
| 353#翻译 | This is pretty intuitive, and it's similar to Django's model syntax. | 这看上去简单易懂,并且很像在模型中使用的语法。 | 翻译 | ||
| 354#翻译 | Each field in the form is represented by a type of <literal>Field</literal> class <literal>CharField</literal> and <literal>EmailField</literal> are the only types of fields used here as attributes of a <literal>Form</literal> class. | 表单中的每一个字段(域)作为<literal>Form</literal>类的属性,被展现成<literal>Field</literal>类。这里只用到<literal>CharField</literal>和<literal>EmailField</literal>类型。 | 翻译 | ||
| 355#翻译 | Each field is required by default, so to make <literal>email</literal> optional, we specify <literal>required=False</literal> . | 每一个字段都默认是必填。要使<literal>email</literal>成为可选项,我们需要指定<literal>required=False</literal>。 | 翻译 | ||
| 357#翻译 | Let's hop into the Python interactive interpreter and see what this class can do. | 让我们深入Python解释器里面看看这个类做了些什么。 | 翻译 | ||
| 358#翻译 | The first thing it can do is display itself as HTML: | 它做的第一件事是将自己展示成HTML: | 翻译 | ||
| 361#翻译 | Django adds a label to each field, along with <literal><label></literal> tags for accessibility. | 为了便于访问,Django用<literal> <label></literal> 标志,为每一个字段添加了标签。 | 翻译 | ||
| 362#翻译 | The idea is to make the default behavior as optimal as possible. | 这个做法使默认行为尽可能合适。 | 翻译 | ||
| 364#翻译 | This default output is in the format of an HTML <literal><table></literal> , but there are a few other built-in outputs: | 默认输出按照HTML的<<literal> table</literal> >格式,另外有一些其它格式的输出: | 翻译 | ||
| 367#翻译 | Note that the opening and closing <literal><table></literal> , <literal><ul></literal> and <literal><form></literal> tags aren't included in the output, so that you can add any additional rows and customization if necessary. | 请注意,标签<table>、<ul>、<form>的开闭合标记没有包含于输出当中,这样你就可以添加额外的行或者自定义格式。 | 翻译 | ||
| 369#翻译 | These methods are just shortcuts for the common case of display the entire form. | 这些类方法只是一般情况下用于快捷显示完整表单的方法。 | 翻译 | ||
| 370#翻译 | You can also display the HTML for a particular field: | 你同样可以用HTML显示个别字段: | 翻译 | ||
| 373#翻译 | The second thing <literal>Form</literal> objects can do is validate data. | <literal>Form</literal>对象做的第二件事是校验数据。 | 翻译 | ||
| 374#翻译 | To validate data, create a new <literal>Form</literal> object and pass it a dictionary of data that maps field names to data: | 为了校验数据,我们创建一个新的<literal>Form</literal>对象,并且传入一个与定义匹配的字典类型数据: | 翻译 | ||
| 377#翻译 | Once you've associated data with a <literal>Form</literal> instance, you've created a bound form: | 一旦你对一个<literal>Form</literal>实体赋值,你就得到了一个绑定form: | 翻译 | ||
| 380#翻译 | Call the <literal>is_valid()</literal> method on any bound <literal>Form</literal> to find out whether its data is valid. | 调用任何绑定form的<literal>is_valid()</literal>方法,就可以知道它的数据是否合法。 | 翻译 | ||
| 381#翻译 | We've passed a valid value for each field, so the <literal>Form</literal> in its entirety is valid: | 我们已经为每个字段传入了值,因此整个<literal>Form</literal>是合法的: | 翻译 | ||
| 384#翻译 | If we don't pass the <literal>email</literal> field, it's still valid, because weve specified <literal>required=False</literal> for that field: | 如果我们不传入<literal>email</literal>值,它依然是合法的。因为我们指定这个字段的属性为<literal>required=False</literal>: | 翻译 | ||
| 387#翻译 | But, if we leave off either <literal>subject</literal> or <literal>message</literal> , the <literal>Form</literal> is no longer valid: | 但是,如果留空<literal>subject</literal>或<literal>message</literal>,整个<literal>Form</literal>就不再合法了: | 翻译 | ||
| 390#翻译 | You can drill down to get field-specific error messages: | 你可以逐一查看每个字段的出错消息: | 翻译 | ||
| 393#翻译 | Each bound <literal>Form</literal> instance has an <literal>errors</literal> attribute that gives you a dictionary mapping field names to error-message lists: | 每一个绑定<literal>Form</literal>实体都有一个<literal>errors</literal>属性,它为你提供了一个字段与错误消息相映射的字典表。 | 翻译 | ||
| 396#翻译 | Finally, for <literal>Form</literal> instances whose data has been found to be valid, a <literal>cleaned_data</literal> attribute is available. | 最终,如果一个<literal>Form</literal>实体的数据是合法的,它就会有一个可用的<literal>cleaned_data</literal>属性。 | 翻译 | ||
| 397#翻译 | This is a dictionary of the submitted data, cleaned up. | 这是一个包含干净的提交数据的字典。 | 翻译 | ||
| 398#翻译 | Django's forms framework not only validates data, it cleans it up by converting values to the appropriate Python types. | Django的form框架不但校验数据,它还会把它们转换成相应的Python类型数据,这叫做清理数据。 | 翻译 | ||
| 401#翻译 | Our contact form only deals with strings, which are cleaned into Unicode objects but if we were to use an <literal>IntegerField</literal> or <literal>DateField</literal> , the forms framework would ensure that <literal>cleaned_data</literal> used proper Python integers or <literal>datetime.date</literal> objects for the given fields. | 我们的contact form只涉及字符串类型,它们会被清理成Unicode对象。如果我们使用整数型或日期型,form框架会确保从cleaned_data使用合适的Python整数型或<literal>datetime.date</literal>型对象。 | 翻译 | ||
| 403#翻译 | Tying Form Objects Into Views | 在视图中使用Form对象 | 翻译 | ||
| 405#翻译 | With some basic knowledge about <literal>Form</literal> classes, you might see how we can use this infrastructure to replace some of the cruft in our <literal>contact()</literal> view. | 在学习了关于<literal>Form</literal>类的基本知识后,你会看到我们如何把它用到视图中,取代<literal>contact()</literal>代码中不整齐的部分。 | 翻译 | ||
| 406#翻译 | Here's how we can rewrite <literal>contact()</literal> to use the forms framework: | 以下示例说明了我们如何用forms框架重写<literal>contact()</literal>: | 翻译 | ||
| 409#翻译 | Look at how much cruft we've been able to remove! | 看看,我们能移除这么多不整齐的代码! | 翻译 | ||
| 410#翻译 | Django's forms framework handles the HTML display, the validation, data cleanup and form redisplay-with-errors. | Django的forms框架处理HTML显示、数据校验、数据清理和表单错误重现。 | 翻译 | ||
| 412#翻译 | Try running this locally. | 尝试在本地运行。 | 翻译 | ||
| 413#翻译 | Load the form, submit it with none of the fields filled out, submit it with an invalid e-mail address, then finally submit it with valid data. | 装载表单,先留空所有字段提交空表单;继而填写一个错误的邮箱地址再尝试提交表单;最后再用正确数据提交表单。 | 翻译 | ||
| 414#翻译 | (Of course, depending on your mail-server configuration, you might get an error when <literal>send_mail()</literal> is called, but that's another issue.) | (当然,根据你邮件服务器的设置,当<literal>send_mail()</literal>被调用时,你可能会得到一个错误提示,这是另外一个问题了。) | 翻译 | ||
| 416#翻译 | Changing How Fields Are Rendered | 改变字段显示 | 翻译 | ||
| 418#翻译 | Probably the first thing you'll notice when you render this form locally is that the <literal>message</literal> field is displayed as an <literal><input type="text"></literal> , and it ought to be a <literal><textarea></literal> . We can fix that by setting the field's <emphasis>widget</emphasis> : | 你可能首先注意到:当你在本地显示这个表单的时,<literal>message</literal>字段被显示成<literal> input type="text"</literal> ,而它应该被显示成<<literal> textarea</literal> >。我们可以通过设置<emphasis> widget</emphasis> 来修改它: | 翻译 | ||
| 421#翻译 | The forms framework separates out the presentation logic for each field into a set of widgets. | forms框架把每一个字段的显示逻辑分离到一组部件(widget)中。 | 翻译 | ||
| 422#翻译 | Each field type has a default widget, but you can easily override the default, or provide a custom widget of your own. | 每一个字段类型都拥有一个默认的部件,我们也可以容易地替换掉默认的部件,或者提供一个自定义的部件。 | 翻译 | ||
| 424#翻译 | Think of the <literal>Field</literal> classes as representing <emphasis>validation logic</emphasis> , while widgets represent <emphasis>presentation logic</emphasis> . | 考虑一下<literal>Field</literal>类表现<emphasis> 校验逻辑</emphasis> ,而部件表现<emphasis> 显示逻辑</emphasis> 。 | 翻译 | ||
| 426#翻译 | Setting a Maximum Length | 设置最大长度 | 翻译 | ||
| 428#翻译 | One of the most common validation needs is to check that a field is of a certain size. | 一个最经常使用的校验要求是检查字段长度。 | 翻译 | ||
| 429#翻译 | For good measure, we should improve our <literal>ContactForm</literal> to limit the <literal>subject</literal> to 100 characters. | 另外,我们应该改进<literal>ContactForm</literal>,使<literal>subject</literal>限制在100个字符以内。 | 翻译 | ||
| 430#翻译 | To do that, just supply a <literal>max_length</literal> to the <literal>CharField</literal> , like this: | 为此,仅需为<literal>CharField</literal>提供<literal>max_length</literal>参数,像这样: | 翻译 | ||
| 433#翻译 | An optional <literal>min_length</literal> argument is also available. | 选项<literal>min_length</literal>参数同样可用。 | 翻译 | ||
| 435#翻译 | Setting Initial Values | 设置初始值 | 翻译 | ||
| 437#翻译 | As an improvement to this form, let's add an <emphasis>initial value</emphasis> for the <literal>subject</literal> field: | 让我们再改进一下这个表单:为字<literal>subject</literal>段添加<emphasis> 初始值</emphasis> : | 翻译 | ||
| 438#翻译 | <literal>"I love your site!"</literal> | <literal>"I love your site!"</literal> | 翻译 | ||
| 439#翻译 | (A little power of suggestion can't hurt.) To do this, we can use the <literal>initial</literal> argument when we create a <literal>Form</literal> instance: | (一点建议,但没坏处。)为此,我们可以在创建<literal>Form</literal>实体时,使用<literal>initial</literal>参数: | 翻译 | ||
| 442#翻译 | Now, the <literal>subject</literal> field will be displayed prepopulated with that kind statement. | 现在,<literal>subject</literal>字段将被那个句子填充。 | 翻译 | ||
| 444#翻译 | Note that there is a difference between passing <emphasis>initial</emphasis> data and passing data that <emphasis>binds</emphasis> the form. | 请注意,传入<emphasis> 初始值</emphasis> 数据和传入数据以<emphasis> 绑定</emphasis> 表单是有区别的。 | 翻译 | ||
| 445#翻译 | The biggest difference is that if you're just passing <emphasis>initial</emphasis> data, then the form will be <emphasis>unbound</emphasis> , which means it won't have any error messages. | 最大的区别是,如果仅传入<emphasis> 初始值</emphasis> 数据,表单是<emphasis>unbound</emphasis>的,那意味着它没有错误消息。 | 翻译 | ||
| 447#翻译 | Custom Validation Rules | 自定义校验规则 | 翻译 | ||
| 449#翻译 | Imagine we've launched our feedback form, and the e-mails have started tumbling in. | 假设我们已经发布了反馈页面了,email已经开始源源不断地涌入了。 | 翻译 | ||
| 450#翻译 | There's just one problem: | 这里有一个问题: | 翻译 | ||
| 451#翻译 | some of the submitted messages are just one or two words, which isn't long enough for us to make sense of. | 一些提交的消息只有一两个单词,我们无法得知详细的信息。 | 翻译 | ||
| 452#翻译 | We decide to adopt a new validation policy: | 所以我们决定增加一条新的校验: | 翻译 | ||
| 453#翻译 | four words or more, please. | 来点专业精神,最起码写四个字,拜托。 | 翻译 | ||
| 455#翻译 | There are a number of ways to hook custom validation into a Django form. | 我们有很多的方法把我们的自定义校验挂在Django的form上。 | 翻译 | ||
| 456#翻译 | If our rule is something we will reuse again and again, we can create a custom field type. | 如果我们的规则会被一次又一次的使用,我们可以创建一个自定义的字段类型。 | 翻译 | ||
| 457#翻译 | Most custom validations are one-off affairs, though, and can be tied directly to the <literal>Form</literal> class. | 大多数的自定义校验都是一次性的,可以直接绑定到form类. | 翻译 | ||
| 459#翻译 | We want additional validation on the <literal>message</literal> field, so we add a <literal>clean_message()</literal> method to our <literal>Form</literal> class: | 我们希望<literal> message</literal> 字段有一个额外的校验,我们增加一个<literal> clean_message()</literal> 方法到<literal> Form</literal> 类: | 翻译 | ||
| 462#翻译 | Django's form system automatically looks for any method whose name starts with <literal>clean_</literal> and ends with the name of a field. | Django的form系统自动寻找匹配的函数方法,该方法名称以<literal>clean_</literal>开头,并以字段名称结束。 | 翻译 | ||
| 463#翻译 | If any such method exists, it's called during validation. | 如果有这样的方法,它将在校验时被调用。 | 翻译 | ||
| 465#翻译 | Specifically, the <literal>clean_message()</literal> method will be called <emphasis>after</emphasis> the default validation logic for a given field (in this case, the validation logic for a required <literal>CharField</literal> ). Because the field data has already been partially processed, we pull it out of <literal>self.cleaned_data</literal> . Also, we don't have to worry about checking that the value exists and is non-empty; that's done by the default validator. | 有一点很明确,<literal>clean_message()</literal>方法将在指定字段的默认校验逻辑执行<emphasis> 之后</emphasis> 被调用。(本例中,在必填<literal>CharField</literal>这个校验逻辑之后。)因为字段数据已经被部分处理,所以它被从<literal>self.cleaned_data</literal>中提取出来了。同样,我们不必担心数据是否为空,因为它已经被校验过了。 | 翻译 | ||
| 467#翻译 | We naively use a combination of <literal>len()</literal> and <literal>split()</literal> to count the number of words. | 我们简单地使用了len()和split()的组合来计算单词的数量。 | 翻译 | ||
| 468#翻译 | If the user has entered too few words, we raise a <literal>forms.ValidationError</literal> . The string attached to this exception will be displayed to the user as an item in the error list. | 如果用户输入字数不足,我们抛出一个<literal>forms.ValidationError</literal>型异常。这个异常的描述会被作为错误列表中的一项显示给用户。 | 翻译 | ||
| 470#翻译 | It's important that we explicitly return the cleaned value for the field at the end of the method. | 在函数的末尾显式地返回字段的值非常重要。 | 翻译 | ||
| 471#翻译 | This allows us to modify the value (or convert it to a different Python type) within our custom validation method. | 我们可以在我们自定义的校验方法中修改它的值(或者把它转换成另一种Python类型)。 | 翻译 | ||
| 472#翻译 | If we forget the return statement, then <literal>None</literal> will be returned, and the original value will be lost. | 如果我们忘记了这一步,None值就会返回,原始的数据就丢失掉了。 | 翻译 | ||
| 474#翻译 | Specifying labels | 指定标签 | 翻译 | ||
| 476#翻译 | By default, the labels on Django's auto-generated form HTML are created by replacing underscores with spaces and capitalizing the first letter so the label for the <literal>email</literal> field is <literal>"Email"</literal> . (Sound familiar? | HTML表单中自动生成的标签默认是按照规则生成的:用空格代替下划线,首字母大写。如<literal>email</literal>的标签是<literal>"Email"</literal> 。(好像在哪听到过? | 翻译 | ||
| 477#翻译 | It's the same simple algorithm that Djangos models use to calculate default <literal>verbose_name</literal> values for fields. | 是的,同样的逻辑被用于模块(model)中字段的<literal>verbose_name</literal>值。 | 翻译 | ||
| 478#翻译 | We covered this in Chapter 5.) | 我们在第五章谈到过。) | 翻译 | ||
| 480#翻译 | But, as with Django's models, we can customize the label for a given field. | 像在模块中做过的那样,我们同样可以自定义字段的标签。 | 翻译 | ||
| 481#翻译 | Just use <literal>label</literal> , like so: | 仅需使用<literal>label</literal>,像这样: | 翻译 | ||
| 484#翻译 | Customizing Form Design | 定制Form设计 | 翻译 | ||
| 486#翻译 | Our <literal>contact_form.html</literal> template uses <literal>{{ form.as_table }}</literal> to display the form, but we can display the form in other ways to get more granular control over display. | 在上面的<literal> contact_form.html</literal> 模板中我们使用<literal> {{form.as_table}}</literal> 显示表单,不过我们可以使用其他更精确控制表单显示的方法。 | 翻译 | ||
| 488#翻译 | The quickest way to customize forms' presentation is with CSS. | 修改form的显示的最快捷的方式是使用CSS。 | 翻译 | ||
| 489#翻译 | Error lists, in particular, could do with some visual enhancement, and the auto-generated error lists use <literal><ul class="errorlist"></literal> precisely so that you can target them with CSS. | 尤其是错误列表,可以增强视觉效果。自动生成的错误列表精确的使用<literal> <ul class="errorlist"></literal>,这样,我们就可以针对它们使用CSS。 | 翻译 | ||
| 490#翻译 | The following CSS really makes our errors stand out: | 下面的CSS让错误更加醒目了: | 翻译 | ||
| 493#翻译 | While it's convenient to have our form's HTML generated for us, in many cases youll want to override the default rendering. | 虽然,自动生成HTML是很方便的,但是在某些时候,你会想覆盖默认的显示。 | 翻译 | ||
| 494#翻译 | <literal>{{ form.as_table }}</literal> and friends are useful shortcuts while you develop your application, but everything about the way a form is displayed can be overridden, mostly within the template itself, and you'll probably find yourself doing this. | {{form.as_table}}和其它的方法在开发的时候是一个快捷的方式,form的显示方式也可以在form中被方便地重写。 | 翻译 | ||
| 496#翻译 | Each field's widget (<literal><input type="text"></literal> , <literal><select></literal> , <literal><textarea></literal> , etc.) can be rendered individually by accessing <literal>{{ form.fieldname }}</literal> in the template, and any errors associated with a field are available as <literal>{{ form.fieldname.errors }}</literal> . With this in mind, we can construct a custom template for our contact form with the following template code: | 每一个字段部件(<input type="text">, <select>, <textarea>, 或者类似)都可以通过访问{{form.字段名}}进行单独的渲染。 | 翻译 | ||
| 499#翻译 | <literal>{{ form.message.errors }}</literal> displays a <literal><ul class="errorlist"></literal> if errors are present and a blank string if the field is valid (or the form is unbound). | <literal>{{ form.message.errors }}</literal> 会在 <literal><ul class="errorlist"></literal> 里面显示,如果字段是合法的,或者form没有被绑定,就显示一个空字符串。 | 翻译 | ||
| 500#翻译 | We can also treat <literal>form.message.errors</literal> as a Boolean or even iterate over it as a list. | 我们还可以把 <literal>form.message.errors</literal> 当作一个布尔值或者当它是list在上面做迭代, | 翻译 | ||
| 501#翻译 | For example: | 例如: | 翻译 | ||
| 504#翻译 | In the case of validation errors, this will add an errors class to the containing <literal><div></literal> and display the list of errors in an unordered list. | 在校验失败的情况下, 这段代码会在包含错误字段的div的class属性中增加一个"errors",在一个无序列表中显示错误信息。 | 翻译 | ||
| 506#翻译 | What's Next? | 下一章 | 翻译 | ||
| 508#翻译 | This chapter concludes the introductory material in this book the so-called core curriculum. | 这一章总结了本书的介绍材料,即所谓“核心教程”。 | 翻译 | ||
| 509#翻译 | The next section of the book, Chapters 8 to 12, goes into more detail about advanced Django usage, including how to deploy a Django application (Chapter 12). | 后面部分,从第八章到第十二章,将详细讲述高级(进阶)使用,包括如何部署一个Django应用程序(第十二章)。 | 翻译 | ||
| 511#翻译 | After these first seven chapters, you should know enough to start writing your own Django projects. | 在学习本书的前面七章后,我们终于对于使用Django构建自己的网站足够了解了。 | 翻译 | ||
| 512#翻译 | The rest of the material in this book will help fill in the missing pieces as you need them. | 本书中剩余的材料将在你需要的时候帮助你补遗。 | 翻译 | ||
| 514#翻译 | We'll start in <reference name="Chapter 8" refuri="../chapter08/">Chapter 8</reference> by doubling back and taking a closer look at views and URLconfs (introduced first in <reference name="Chapter 3" refuri="../chapter03/">Chapter 3</reference>). | 第八章我们将回头并深入地讲解 视图和URLconfs(第三章已简单介绍)。 | 翻译 | ||
| 516#翻译 | Document License`_. | Document License`_. | 翻译 | ||
| 517#翻译 | Hosting graciously provided by | Hosting graciously provided by | 翻译 |