在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表单就比较麻烦,因为用户必须按照特定的格式来输入数据。











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