为什么这么多Python框架
原文:http://bitworking.org/news/Why_so_many_Python_web_frameworks
This is Joe Gregorio's writings (archives), projects and status updates.
Why so many Python web frameworks?
为什么这么多Python Web框架
When asked about the plethora of web frameworks for Python the answer is often that it is way too easy to put together one in Python. That certainly seems plausible since there are so many libraries that implement the components of a web framework and if it's easy to plug those pieces together then maybe that lowers the bar of entry for new frameworks. So let's give it a shot, we'll pick some components and spend a couple hours seeing how far we can get building a web framework, which we'll call Robaccia.
当被问及为什么有这么多python web框架,一般回答都是:对于Python来说能够非常简单的把很多东西结合成一个整体。似乎看起来的确是这样,因为有很多现成的库已经实现了构造一个web 框架的所需的基本组件,我们能够很容易的把这些零碎的组件组合起来,这样大大降低了创建一个新的web 框架的难度。让我们试一试,我们将会选择一些组件,花费一些时间来展示如何构建一个web框剪,我们称它为 Robaccia。
Executive Summary: Robaccia was built in three hours and a total of 60 lines of Python code.
概要提示:Robaccia是一个只用了三个小时构建的,总共代码只有60行的简单的框架。
[Update: Add a link to the WSGI Wiki and cleaned up some typos. And yes, robaccia.py
could be even shorter if I had used the mimetypes module.]
For each type of library we are going to need I will choose just one. Because I have to. Does that mean that's the library I prefer, or that the other ones are not good? No. It means I had to choose one. Please don't feel slighted if I didn't choose your favorite templating/routing/sql library.
对于每种类型的库,我们将会选择最合适的一个.就因为我这样做,意味着除了我选择的库,其他的库都不好吗?不.它仅仅表示适合我选择.请不要感到沮丧,如果我选择你喜欢的templating/routing/sql库。
Templating(模板)
There are quite a few templating libraries available for Python, such as Myghty,Cheetah, etc. I chose Kid; "a simple template language for XML based vocabularies".
有很多Python模板库,比如 Myghty,Cheetah . 我选择的是 Kid ,一个基于XML词汇表的简单模板语言
SQL
For interfacing to the database I chose SQLAlchemy. There are others likeSQLObject.
Routing
We need some way to route incoming HTTP requests to the right handlers. For this I chose Selector. Again, there are other options in the Python universe like Routes.
WSGI
WSGI, as defined by PEP 333, is the conceptual glue that holds this all together. The best way to think of WSGI is as the Java servlet API for Python. It is a standard interface between web servers and Python web applications or frameworks, to web application portability across a variety of web servers. You can learn more about WSGI and find servers, frameworks, middleware, etc. on the WSGI Wiki
WSGI是由 PEP 333, 定义的,可以理解为把所有东西连在一起的胶水.理解WSGI的最好方式就是WSGI相当于Python中的Servlet.它是web server和web application或者framewokrs之间的标准接口,是为了使得web application能够在任何web servers之间移植.如果你想了解更多细节请参看我之前翻译的 PEP33
Now that we have all of our components let's start plugging them together
所有组件都准备之后,我们开始将它们组合在一起.
Actually, at this point you should probably go off and run through the Django tutorial if you haven't already, to give you an idea of what we are aiming for, not that we are going to get anywhere close to the fit and finish of Django.
事实上,此时如果你还头脑里对这些还没有什么概念,你应该马上去阅读一下Django 手册.
当然只是让你明白我们的目地,而不是让你精通Django.
We're going to follow the classic model/view/controller paradigm, but in the case of web frameworks it is more like model/view/template/dispatcher, so every application will have four required files: model.py, view.py, urls.py and a templates directory. Let's throw in one more file, dbconfig.py that allows you to setup access to your database.
我会依次讲解 model/view/controller 的例子,在web 框架中依次对应 model/template/dispatcher.每个web app都含有四种文件 model.py, view.py, urls.py和templates目录.或者更多的文件,dbconfig.py用于设置访问数据的配置.
What we'll do is start building a weblog application from these pieces but being very careful about what lands in the application and what becomes part of the framework. The first thing we need to create is a model, which we will do using SQLAlchemy, and capture in model.py
.
我们将会使用上述提供的组件构建一个weblog运用,对于那些将要成为web framrwork中的部件要非常的小心.我们要做的第一件事就是创建一个model,在这个model中我们将会用到SQLAlchemy.下面是 model.py的代码
from sqlalchemy import Table, Column, String
import dbconfig
entry_table = Table('entry', dbconfig.metadata,
Column('id', String(100), primary_key=True),
Column('title', String(100)),
Column('content', String(30000)),
Column('updated', String(20), index=True)
)
Now that's pure a Python description of our model, and the configuration in dbconfig.py
is equally simple.
一个简单的python model描述文件,下面是dbconfig,py的代码非常简单
fromsqlalchemy
import*
metadata = BoundMetaData(
'sqlite:///tutorial.db')
One of the first things you do in the Django tutorial is use such a model to actually create the tables in the database. We'll do the same here, with 'manage.py' which is the first thing in our Robaccia framework.
在Django手册中要做的第一件事就是使用一个model在数据库中创建一张表.我们下面要做的事是一样, manage.py 是构建Robaccia框架中的要做的第一件事
manage.py
import os, sys
defcreate
():
from
sqlalchemy
importTable
import
model
for
(name, table)
invars(model).iteritems():
if
isinstance(table, Table):
table.create()
if__name__ ==
"__main__":
if
'create'
in
sys.argv:
create()
Which we can now use to create the database. 创建数据库
$ python manage.py create
$
Now that our database table is created we can go into the Python interpreter and manipulate the data via the 'model' module. Note that we could have also gone into the interpreter to create the table, but that's not normally how you would proceed. In the interpreter session below we add two rows to the table.
数据表创建完成之后,我们进入Python解释器,引入model.py来操作数据.当然你也直接在解释器中创建表,但是那样不是我们正常的处理方式.我们向表中将插入两条记录.
$ python
Python 2.4.3 (#2, Apr 27 2006, 14:43:58)
[GCC 4.0.3 (Ubuntu 4.0.3-1ubuntu5)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import model
>>> i = model.entry_table.insert()
>>> i.execute(id='first-post', title="Some Title", content="Some pithy text...",
updated="2006-09-01T01:00:00Z")
>>> i.execute(id='second-post', title="Moving On", content="Some not so pithy words...",
updated="2006-09-01T01:01:00Z")
>>>
Now we have a model with some data in it, time to introduce the URLs and the views. The urls.py
file contains information on how the incoming requests are to be routed to views, and view.py
contains all those view targets.
现在介绍URLs和views. urls.py 描述了请求将会被路由到那views,views.py包含了所有的视图标记.
urls.py
import selector
import view
urls = selector.Selector()
urls.add(
'/blog/', GET=view.list)
urls.add(
'/blog/{id}/', GET=view.member_get)
urls.add(
'/blog/;create_form', POST=view.create, GET=view.list)
urls.add(
'/blog/{id}/;edit_form', GET=view.member_get, POST=view.member_update)
Selector maps URIs to views. If an incoming request has a URI that matches then the request gets dispatched to the associated handler. Both Selector and the handler are WSGI compliant objects, which will make plugging all this together much easier.
Selector存储了了URIs和Views的映射关系.如果某个请求的URI有匹配的,那么请求将会分发到相应的处理程序. Selector和 handler都是兼容WSGI的对象,使得我们组合起来更加容易
下面是view.py的源码
view.py
import robaccia
import model
deflist
(environ, start_response):
rows = model.entry_table.select().execute()
return
robaccia.render(start_response,
'list.html', locals())
defmember_get
(environ, start_response):
id = environ[
'selector.vars'][
'id']
row = model.entry_table.select(model.entry_table.c.id==id).execute().fetchone()
return
robaccia.render(start_response,
'entry.html', locals())
defcreate
(environ, start_response):
pass
defcreate_form
(environ, start_response):
pass
defmember_edit_form
(environ, start_response):
pass
defmember_update
(environ, start_response):
pass
Note that in the above code only list()
and member_get()
are implemented.
上面的代码我们只实现了 list()和member_get()两个方法
In my first implementation the view handlers originally did the rendering of the templates themselves and then put everything together to fit into the WSGI model, but that was just repeated code for every view, so that code got factored out into our second piece of Robaccia:
在我最初实现的视图处理程序中,我让视图自己渲染模板并且把这些都放在一起以便适应WSGI模型,但是每个试图中都有重复的代码,因此我把这些代码分离到robaccia.py文件中.
robaccia.py
import kid
import os
extensions = {
'html'
:
'text/html',
'atom'
:
'application/atom+xml'
}
defrender
(start_response, template_file, vars):
ext = template_file.rsplit(
".")
contenttype =
"text/html"
iflen(ext) > 1
and(ext[1]
inextensions):
contenttype = extensions[ext[1]]
template = kid.Template(file=os.path.join(
'templates', template_file), **vars)
body = template.serialize(encoding=
'utf-8')
start_response(
"200 OK", [(
'Content-Type', contenttype)])
return [body]
The render()
function looks at the extension of the template and uses that to determine what to use as the content-type. Then the template and variables are passed into Kid to be processed. The whole thing is processed and returned in a way that conforms to WSGI. Here is the list.html template:
render()函数是模板的扩展函数用来确定content-type.模板和变量传递给Kid进行处理.所有处理完的结果以符合WSGI规范的形式返回(要是一个迭代器)。下面是一个模板 list.html
list.html
<?xml version="1.0" encoding="utf-8"?>
<html xmlns:py="http://purl.org/kid/ns#>">
<head>
<title>A Robaccia Blog</title>
</head>
<div py:for="row in rows.fetchall()">
<h2>${row.title}</h2>
<div>${row.content}</div>
<p><a href="./${row.id}/">${row.updated}</a></p>
</div>
</html>
So let's take stock of where we are, urls.urls
is a WSGI compliant application that looks at the incoming calls and dispatches to the WSGI compliant applications listed inview.py
. Each of those is turn use the model in model.py
and pass the results through templates in the templates
directory to generate the responses.
Now all we need to do is run the code. Since we are dealing with WSGI applications we can use wsgiref
. Let's add a 'run' option to manage.py
.
现在我们总结一下我们所作的是.urls是一个WSGI组件 application,它将所有的请求转发到view.py中相应的符合WSGI规范的处理函数.处理函数中使用了model.py进行处理,将model处理的结果传递给templates目录中的模板生成响应结果.
我们现在要做的就是运行代码.我们可以使用wsgiref来处理WSGI application.我们添加一个运行文件 manage.py
manage.py
import os, sys
defcreate
():
from
sqlalchemy
importTable
import
model
for
(name, table)
invars(model).iteritems():
if
isinstance(table, Table):
table.create()
defrun
():
import
urls
if
os.environ.get(
"REQUEST_METHOD",
""):
from
wsgiref.handlers
importBaseCGIHandler
BaseCGIHandler(sys.stdin, sys.stdout, sys.stderr, os.environ).run(urls.urls)
else
:
from
wsgiref.simple_server
importWSGIServer, WSGIRequestHandler
httpd = WSGIServer((
'', 8080), WSGIRequestHandler)
httpd.set_app(urls.urls)
"Serving HTTP on %s port %s ..."
% httpd.socket.getsockname()
httpd.serve_forever()
if__name__ ==
"__main__":
if
'create'
in
sys.argv:
create()
if
'run'
in
sys.argv:
run()
The run()
function looks at the environment variables to determine if it is being run as a CGI application, otherwise it runs the application under it's own server at port 8080.
run()函数根据环境变量来决定是否以CGI方式运行程序,否则的话自己以server方式运行在8080端口
$ python manage.py run
Serving HTTP on 0.0.0.0 port 8080 ...
Point your browser at http://localhost:8080/blog/
and you should get the blog's main page, the list.html
template filled in with the two entries we put in the system earlier. That's it, our application is running and our framework is functional.
打开浏览器输入http://localhost:8080/blog/
你将会进入到主页面,list.html显示我们早先插入的两条记录.就这么简单,我们的程序正在运行并且我们的框架发挥作用了.
And what if we want to run our application via CGI? That file is just a few lines long:
如果想以CGI方式运行,只用写以下代码就可以了
main.cgi
#!/usr/bin/python2.4
import manage
manage.run()
Summary(总结)
So what do we have here? A set of conventions for how to lay out files in a directory:
各个文件的作用
model.py
- One or more models expressed in SQLAlchemy Tables.view.py
- One or more views, implemented as WSGI applications.urls.py
- A single instance of a selector object that maps URIs to the WSGI applications inview.py
.templates
- A directory of Kid templates to be used to format the responses from the view applications.dbconfig.py
- Configuration for the SQLAlchemy Tables in model.py
Beyond those files which actually implement our example web service we havemanage.py
, main.cgi
, and robaccia.py
, the sum total of our framework code, which comes to about 60 lines of code. That's not a lot of glue code to bring four powerful libraries like SQLAlchemy, Kid, Selector, and WSGIref together. And because we used WSGI throughout we can easily plug in WSGI pieces that handle authentication, caching, logging, etc.
Now let's be clear also about what we do not have when compared to Django. We don't have an instant admin interface, we don't have generic views, automatic form generation, automatic form handling, the Django community, bug tracking, IRC, etc, etc.
What I want to draw your attention to is the touch-points between the major components. How much code did we have to write to make the data model consumable by Kid templates? None. How much translation code did we have to write to hook our WSGI views into Selector? None. And how much code did we have to write to pull information out of URLs and use them in pulling information out of our model? About one line:
id = environ['selector.vars']['id'].
The nice part about the ocean of components that exists for building Python web frameworks is that the same is true for all of them: they would only require a small amount of glue code. Our little framework would be about the same size if I had instead chosen SQLObject, Cheetah and Routes.
Oh yeah, did I tell you why I chose the name Robaccia? It means trash in Italian. It's a throw away. So go on, get out of here, go work on one of the dozens of already established web frameworks for Python.
posted on 2013-12-20 20:32 Arts&Crafts 阅读(1184) 评论(0) 编辑 收藏 举报