返回正常中文阅读

想对这篇译文“指手画脚”吗?

您的参与将有助于译者提高译文的质量;同时,大家一起对问题的讨论也是最佳的学习方式。还等什么?请现在就注册登录译言,开始眉批!
大错 小错 不顺 建议

The Django Form Validation Framework on Google App Engine

The Django Form Validation Framework on Google App Engine

Alexander Power
April 2008

Introduction

In this article we show how to use Django's form validation framework with the Google App Engine. This framework allows you to construct HTML forms from your data models, and handle the inputted information from the forms seamlessly when interacting with the datastore.

What is the Django Form Validation Framework?

Django's form validation framework is included with the Django project. It uses a database model to construct well formed HTML forms for your application. It also handles the server-side functionality to validate the entry and put the data entered in to your datastore. With the Django forms framework, you can easily turn your data model into a set of pages that can be used to insert and update data in a datastore. Once the data is in the datastore, you can use GQL queries to access this data.

How do Django Forms Interact with the Datastore?

The Google App Engine model class, db.Model, is not the same as the model class used by Django. As a result, you cannot directly use the Django forms framework with Google App Engine. However, Google App Engine includes a module, db.djangoforms, which casts between the datastore models used with Google App Engine and the Django models specification. In most cases, you can use db.djangoforms.ModelForm in the same manner as the Django framework.

Shopping List - An Example

We are going to develop a simple application uses Django forms which creates pages that allow you to add and edit items on a shopping list, then stores the information in our datastore. Finally we will use GQL to query the data and display it in our application.

First, we specify the Python module imports and Google App Engine imports for our application. Please note that you must import google.appengine.webapp.template before importing any Django modules:

import cgi
import wsgiref.handlers

from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp importtemplate

from google.appengine.ext.db import djangoforms

Define Model and Form Classes for our Example

Next, we define our model for a shopping list entry as a subclass of the Google App Engine db.Model class. For each object, we note the name of the item, how many we want, what price we want to pay, the date we made the entry, and by whom it was made:

classItem(db.Model):
  name
= db.StringProperty()
  quantity
= db.IntegerProperty(default=1)
  target_price
= db.FloatProperty()
  priority
= db.StringProperty(default='Medium',choices=[
   
'High','Medium','Low'])
  entry_time
= db.DateTimeProperty(auto_now_add=True)
  added_by
= db.UserProperty()

Next, we create the form object based on the model. To do this, we create a class that inherits from djangofroms.ModelForm, and create a subclass of that class called Meta in which we specify the model and any excluded fields that we do not need in our form. In this case, since we will utilize the Users API to get the person who added the item to the shopping list, we will not need to include the added_by field on our form:

classItemForm(djangoforms.ModelForm):
 
classMeta:
    model
=Item
    exclude
=['added_by']

We also don't need to have the user specify the entrytime. However, DateTime fields with auto_now or auto_now_add set to true are automatically not displayed when a form is generated. Therefore, we do not need to include the entry_time field in the list for exclusions.

Defining Request Handlers

Adding an Item

Now, let us create the request handlers for our shopping list. The form will be accessed at the root URL via HTTP GET, and the user will submit it to our application via an HTTP POST to the same URL.

One of the advantages of Django forms is the automatic form validation. If the user submitted data is invalid, it will produce an error and automatically display form messaging that will ask the user to fix their input errors.

To define the HTTP GET request method:

classMainPage(webapp.RequestHandler):
 
def get(self):
    self
.response.out.write('<html><body>'
                           
'<form method="POST" '
                           
'action="/">'
                           
'<table>')
   
# This generates our shopping list form and writes it in the response
    self
.response.out.write(ItemForm())
    self
.response.out.write('</table>'
                           
'<input type="submit">'
                           
'</form></body></html>')

The form will pre-populate the entries with default values where they are provided, and will otherwise leave blanks. Additionally, if a fixed list of choices are provided, a drop-down menu will be created.

Next, we write a method to handle the HTTP POST request:

 def post(self):
    data
=ItemForm(data=self.request.POST)
   
if data.is_valid():
     
# Save the data, and redirect to the view page
      entity
= data.save(commit=False)
      entity
.added_by = users.get_current_user()
      entity
.put()
      self
.redirect('/items.html')
   
else:
     
# Reprint the form
      self
.response.out.write('<html><body>'
                             
'<form method="POST" '
                             
'action="/">'
                             
'<table>')
      self
.response.out.write(data)
      self
.response.out.write('</table>'
                             
'<input type="submit">'
                             
'</form></body></html>')

We use data.is_valid() to check that there are no errors in the user input. If errors are found, the form is reprinted with the user entered information and appropriate error messages.

If the form input is valid, data.save will generate the datastore entity. Since we wish to add the user information to the entity, we specify commit=False. Without this flag, calling save() would save the entity directly to the datastore. We include the current user information, and then call the put() method to save the data to our datastore.

Default values are very effective ways to initialize entities in your datastore. However, we cannot use this to initialize the current user with users.get_current_user(), because the value is cached between requests. The Property page has more information.

Displaying our List

After we have added items to our shopping list, we want to display them to the user when he requests the URL /items.html. We create another request handler for this page that queries the datastore using the GQL query language.

classItemPage(webapp.RequestHandler):
 
def get(self):
    query
= db.GqlQuery("SELECT * FROM Item ORDER BY name")
   
for item in query:
      self
.response.out.write("%s - Need to buy %d, cost $%0.2f each<br>"%
                             
(item.name, item.quantity, item.target_price))

Writing our main() Function

After writing all our handler classes, we need to define the main() function for this program to handle the CGI requests:

def main():
  application
= webapp.WSGIApplication(
                                       
[('/',MainPage),
                                       
('/items.html',ItemPage),
                                       
],
                                       debug
=True)
  wsgiref
.handlers.CGIHandler().run(application)

if __name__=="__main__":
  main
()

This main function directs each request to the appropriate handler class based on the URL request.

Writing our app.yaml File

With Google App Engine, the app.yaml file contains the specification as to which script will handle an incoming request. Our application only has one script, form.py, so all requests will be handled by that script:

application: shoppinglist
version
:1
runtime
: python
api_version
:1

handlers
:
- url:.*
  script
: form.py

Editing Existing Entities

We can extend the above example by allowing the users of our shopping list to edit an existing shopping list entry.

Modifying our ItemPage Class

First, when handling the item page request, we'll add a link next to each item that they can click on to edit the entry. The link will request the /edit URL, and include the item id as a query argument.

classItemPage(webapp.RequestHandler):
 
def get(self):
    query
= db.GqlQuery("SELECT * FROM Item ORDER BY name")
   
for item in query:
      self
.response.out.write('<a href="/edit?id=%d">Edit</a> - '%
                              item
.key().id())
      self
.response.out.write("%s - Need to buy %d, cost $%0.2f each<br>"%
                             
(item.name, item.quantity, item.target_price))

Adding an EditPage Class

Now we must add the EditPage class to handle the edit request. This handler is similar to handlers used to add an item.

In the get() method, for our EditPage class, we first retrieve the instance we are editing from the datastore, and then pass that instance to form renderer so that the item information can be populated. Also, we add the item id as a hidden input to the form so that we can access the id information when the user submits the edited item:

classEditPage(webapp.RequestHandler):
 
def get(self):
   id =int(self.request.get('id'))
    item
=Item.get(db.Key.from_path('Item', id))

    self
.response.out.write('<html><body>'
                           
'<form method="POST" '
                           'action="/edit">'
                           
'<table>')
    self
.response.out.write(ItemForm(instance=item))
    self
.response.out.write('</table>'
                           '<input type="hidden" name="_id" value="%s">'
                           
'<input type="submit">'
                           
'</form></body></html>'% id)

Next we write the method to handle the HTTP POST. We query our datastore for the item we are editing, and then validate the edited information using the validation framework available with Django forms. As before, if the information is valid, we put the change in the datastore, or else we allow the user to modify their input:

 def post(self):
   id =int(self.request.get('_id'))
    item
=Item.get(db.Key.from_path('Item', id))

    data
=ItemForm(data=self.request.POST, instance=item)
   
if data.is_valid():
     
# Save the data, and redirect to the view page
      entity
= data.save(commit=False)
      entity
.added_by = users.get_current_user()
      entity
.put()
      self
.redirect('/items.html')
   
else:
     
# Reprint the form
      self
.response.out.write('<html><body>'
                             
'<form method="POST" '
                             'action="/edit">'
                             
'<table>')
      self
.response.out.write(data)
      self
.response.out.write('</table>'
                             '<input type="hidden" name="_id" value="%s">'
                             
'<input type="submit">'
                             
'</form></body></html>'% id)

Modifying your main() Function

Lastly, we must add the new URL and request handler to our main function:

def main():
  application
= webapp.WSGIApplication(
                                       
[('/',MainPage),
                                       ('/edit',EditPage),
                                       
('/items.html',ItemPage),
                                       
],
                                       debug
=True)

  wsgiref
.handlers.CGIHandler().run(application)

Additional Notes on Setting Up Django Forms

Django's form validating framework does strict validation against data types. However, it does not provide Javascript validation on the client side of input. As a result, some data types, such as dates and lists, may be more difficult to use with Django forms, because users must enter the data in the specific format required.

在Google App Engine上使用Django表单验证框架

在Google App Engine上使用Django表单验证框架

原文作者:Alexander Power
翻译:Wang Shaobo

April 2008

导言

在这篇文章告诉我们如何在Google APP Engine 使用Django 表格验证框架。这个框架可以让您由数据模型生成HTML表单,并且在表单和数据库进行交互时,它能准确地处理表单里的信息。

什么是Django表单验证框架?

Django表单验证框架是Django项目的一部分。它通过数据库模型为你的应用程序构建高质量的HTML表单。它也拥有服务端功能,验证条目并将数据存入数据库中。有了Django表单框架,你能够轻松地将你地数据模型生成一套页面,这些页面可以插入、更新数据到数据库中。一旦数据存入了数据库中,你就可以使用GQL查询来访问这些数据了。

Django表单是如何与数据库进行交互的呢?

Google APP Engine 的模型(db.Model)和Django使用的模型是不一样的。所以你不能直接地在Google App Engine上使用Django表单验证框架。然而,Google App Engine有一个db.djangoforms单元,它映射着Google App Engine使用的datastore 模型和Django模型。在大多数情况下,你可以使用db.djangoforms.ModelForm的方式一样地使用Django框架。

购物清单(Shopping List) - 一个示例

我们准备使用Django表单来开发一个简单的应用。它能让你添加或者更改一个购物清单的条目,然后把信息存储到我们的数据库。左后我们可以使用GQL来查询数据,并显示到我们的应用上。

首先,我们要为我们的应用导入Python和Google App Engine所需的模块。请注意,你必须在导入任何Django模块前导入google.appengine.webapp.template:

import cgi
import wsgiref.handlers

from google.appengine.api import users
from google.appengine.ext import db
from google.appengine.ext import webapp
from google.appengine.ext.webapp importtemplate

from google.appengine.ext.db import djangoforms

为示例定义模型和表单类

接下来,我们定义购物清单条目模型,它是Google App Engine的db.Model的子类。对于每个对象,我们记录条目的名称,数量,价格,日期和创作的用户:

class Item(db.Model):
  name
= db.StringProperty()
  quantity
= db.IntegerProperty(default=1)
  target_price
= db.FloatProperty()
  priority
= db.StringProperty(default='Medium',choices=[
   
'High','Medium','Low'])
  entry_time
= db.DateTimeProperty(auto_now_add=True)
  added_by
= db.UserProperty()

下一步,我们创建基于刚才的模型的表单对象。我们创建一个继承djangoforms.ModelFrom的类,并创建一个Meta的子类,我们在这个子类里指定模型并排除我们不想放到表单的单元。在这种情况下,因为我们可以利用Users API来获取将条目添加到购物清单的用户,我们不必要在表单里包含added_by项:

class ItemForm(djangoforms.ModelForm):
 
class Meta:
    model
=Item
    exclude
=['added_by']

我们也不必要让每个用户指定条目的添加时间。不过,当生成一个表单时,auto_now或者auto_now_add设为true的DateTime项是不会自动显示的。因此我们不必要将entry_time列入例外的列表中。

定义请求处理器(Request Handlers)

添加一个条目(Item)

现在,让我们为购物清单创建请求处理器。表单可以通过HTTP的GET请求在根URL上访问,用户通过一个HTTP的POST请求至同样的URL提交表单到我们的应用。

自动验证表单是Django先进功能之一。如果用户提交的数据不合法,Django会残生一错误信息并自动显示在消息框中,提示用户改正输入的错误。

这样定义处理HTTP GET请求的方法:

class MainPage(webapp.RequestHandler):
 
def get(self):
    self
.response.out.write('<html><body>'
                           
'<form method="POST" '
                           
'action="/">'
                           
'<table>')
   
# This generates our shopping list form and writes it in the response
    self
.response.out.write(ItemForm())
    self
.response.out.write('</table>'
                           
'<input type="submit">'
                           
'</form></body></html>')

这个表单将用我们指定的默认值预设条目,没有指定默认值的将为空。另外,如果我们设定了一个固定的选择列表,它将创建一个下拉菜单。

接下来,我们写一个用来处理HTTP POST请求的方法:

 def post(self):
    data
=ItemForm(data=self.request.POST)
   
if data.is_valid():
     
# Save the data, and redirect to the view page
      entity
= data.save(commit=False)
      entity
.added_by = users.get_current_user()
      entity
.put()
      self
.redirect('/items.html')
   
else:
     
# Reprint the form
      self
.response.out.write('<html><body>'
                             
'<form method="POST" '
                             
'action="/">'
                             
'<table>')
      self
.response.out.write(data)
      self
.response.out.write('</table>'
                             
'<input type="submit">'
                             
'</form></body></html>')

我们利用data.is_valid()来检查用户的输入是否有错误。如果有,根据用户输入的信息重新显示表单,并提示出错误信息。

如果表单的输入是合法的,data.save将会生成一个datastore实体。由于我们希望加入用户的信息到实体中,我们指定了 commit = False. 没有它,调用save()将会把实体直接存入datastore. 我们把当前用户的信息包含进去,然后调用put()函数将数据存入datastore.

注意:默认值对初始化datastore的实体是很有用的。但是,我们不能使用users.get_current_user()初始化当前用户,因为他和个值是隐含在请求之中的。Property 页面有更多的信息。

显示清单

当我们添加条目到我们的购物清单后,我们想让用户访问URL /items.html时显示出它们来。我们为这个页面创建另外一个请求处理器,它使通过使用GQL这个查询语言来查询datastore.

class ItemPage(webapp.RequestHandler):
 
def get(self):
    query
= db.GqlQuery("SELECT * FROM Item ORDER BY name")
   
for item in query:
      self
.response.out.write("%s - Need to buy %d, cost $%0.2f each<br>"%
                             
(item.name, item.quantity, item.target_price))

写我们的主函数main()

当我们写完所有的处理请求的类后,我们需要定义main()函数来处理CGI请求:

def main():
  application
= webapp.WSGIApplication(
                                       
[('/',MainPage),
                                       
('/items.html',ItemPage),
                                       
],
                                       debug
=True)
  wsgiref
.handlers.CGIHandler().run(application)

if __name__=="__main__":
  main
()

这个主函数直接将每个请求根据URL来映射到适当的请求处理函数。

写应用配置文件app.yaml

在Google App Engine上,app.yaml文件包含指定关于哪一个脚本来处理收到的请求的配置信息。我们的应用只有一个脚本,form.py,所以所有的请求都将交给那个脚本来处理:

application: shoppinglist
version
:1
runtime
: python
api_version
:1

handlers
:
- url:.*
  script
: form.py

编辑存在的实体

我们可以改进上面的示例,允许购物清单的用户编辑自己创建的条目。

修改ItemPage类

首先,当我们处理一个条目列表页面请求时,我们为每条添加一个链接,这样就可以点击然后编辑它们了。链接将请求至/edit这个地址,它包含条目的id。

class ItemPage(webapp.RequestHandler):
 
def get(self):
    query
= db.GqlQuery("SELECT * FROM Item ORDER BY name")
   
for item in query:
      self
.response.out.write('<a href="/edit?id=%d">Edit</a> - '%
                              item
.key().id())
      self
.response.out.write("%s - Need to buy %d, cost $%0.2f each<br>"%
                             
(item.name, item.quantity, item.target_price))

添加一个EditPage类

现在,我们添加一个EditPage类来处理编辑(edit)的请求。这个处理器和添加的那个类似。

在EditPage类里的get()方法中,我们首先从datastore里获得要编辑的实例,然后把它传给渲染页面,这样就装入了条目的信息了。:

class EditPage(webapp.RequestHandler):
 
def get(self):
   id =int(self.request.get('id'))
    item
=Item.get(db.Key.from_path('Item', id))

    self
.response.out.write('<html><body>'
                           
'<form method="POST" '
                           'action="/edit">'
                           
'<table>')
    self
.response.out.write(ItemForm(instance=item))
    self
.response.out.write('</table>'
                           '<input type="hidden" name="_id" value="%s">'
                           
'<input type="submit">'
                           
'</form></body></html>'% id)

接下来,沃尔玛你写一个用来处理HTTP POST请求的方法。我们从datastore中查询我们要编辑的条目,然后使用验证框架来验证Django表单编辑的信息。和之前一样,如果信息是合法的,我们将更改的信息变更到datastore中,否这我们让用户修改它们输入的信息:

 def post(self):
   id =int(self.request.get('_id'))
    item
=Item.get(db.Key.from_path('Item', id))

    data
=ItemForm(data=self.request.POST, instance=item)
   
if data.is_valid():
     
# Save the data, and redirect to the view page
      entity
= data.save(commit=False)
      entity
.added_by = users.get_current_user()
      entity
.put()
      self
.redirect('/items.html')
   
else:
     
# Reprint the form
      self
.response.out.write('<html><body>'
                             
'<form method="POST" '
                             'action="/edit">'
                             
'<table>')
      self
.response.out.write(data)
      self
.response.out.write('</table>'
                             '<input type="hidden" name="_id" value="%s">'
                             
'<input type="submit">'
                             
'</form></body></html>'% id)

修改 main() 函数

最后,我们必须将新的URL和请求处理器添加到main函数:

def main():
  application
= webapp.WSGIApplication(
                                       
[('/',MainPage),
                                       ('/edit',EditPage),
                                       
('/items.html',ItemPage),
                                       
],
                                       debug
=True)

  wsgiref
.handlers.CGIHandler().run(application)

建立Django表单的更多信息

Django表单验证框架对数据类型的验证很严格。由于它不提供客户端输入的Javascript的验证方式,所以一些数据类型,如日期、列表要使用Django表单就比较麻烦,因为用户必须按照特定的格式来输入数据。


阅读
发现
翻译