首頁  >  文章  >  後端開發  >  python模擬Django框架

python模擬Django框架

高洛峰
高洛峰原創
2017-03-02 16:27:451252瀏覽

一、python實作web伺服器

web開發首先要有web伺服器才行。例如apache,但是在開發階段最好有一個簡單方便的開發伺服器,
容易重啟進行調試,等開發調試完畢後,再將程式碼部署到成熟穩定高效的web伺服器。

# -*- coding: utf-8 -*-
from wsgiref import simple_server

# 定义一个输出 hello world 和环境变量的简单web应用程序
def hello_app(environ, start_response):
 # 输出 http 头,text/plain 表示是纯文本
 start_response('200 OK', [('Content-type','text/plain')])
 # 准备输出的内容
 content = []
 content.append('Hello world')
 for key, value in environ.items():
  content.append('%s : %s' % (key, value))
 # 输出,根据 wsgi 协议,返回的需要是一个迭代器,返回一个 list 就可以
 return ['\n'.join(content)]

# 构造开发服务器对象,设置绑定的地址和端口,并把 hello world 应用程序传给他
server = simple_server.make_server('localhost', 8080, hello_app)
# 启动开发服务器
server.serve_forever()

執行上面這個程式後,開啟瀏覽器,造訪一個以http://www.php.cn/:8080 開頭的網址即可看到environ 所包含的內容。

python模擬Django框架

 (截取一小部分)

#二、基礎知識

瀏覽器和web應用程式之間使用的是http協議,它規定了請求和回應的格式。
1、請求包(Http Request)
請求主要包括請求的方法,請求的URL,請求頭,請求體。
請求的方法http規定有GET, POST, PUT, DELETE,只不過透過瀏覽器發起的web請求一般只涉及GET和POST請求。
GET一般用來取得伺服器內容,POST類似修改內容,PUT添加,DELETE刪除。
一般透過提交html的form表單發起POST請求。成功後需要進行重定向。
從協定上看GET,HTTP請求最大的差別就是GET請求沒有請求體,而POST請求有。這意味著可以透過POST請求
向伺服器發送大量數據,如上傳檔案等,當然GET請求也可以透過URL本身以及其參數向伺服器傳遞參數,例如
url?arg1=value&arg2=value
 請求頭就是包含了請求包的描述資訊。 如編碼,包長度等。
 2、回應套件(Http Response)
#http的回應套件的格式更簡單一些,包括狀態碼,回應頭和回應體,狀態碼表示該請求的結果,例如
200表示成功
404表示資源沒有找到
500表示伺服器錯誤
301表示資源換了位址,客戶端需要跳轉。
回應頭和請求頭類似,包括一些描述訊息,響應體一般就是輸出內容了,大部分是頁面html代碼。
 3、請求的生命週期
1. web伺服器接收到原始的http請求後進行一定程度的包裝再交給web應用程式
2 . web應用程式處理後,再以一定的格式返回資料給web伺服器
3. web伺服器再將資料包裝成http回應包回傳給瀏覽器。
4、關於cgi
cgi(common gateway interface)就是web伺服器與web應用程式之間的一個古老的協議,在cgi協定中,
web伺服器將http要求的各種資訊放到cgi應用程式的環境變數中,cgi應用程式再透過標準輸出,輸出它的回應頭
和對應內容給web伺服器。
 上面用到的開發伺服器與應用程式之間所使用的協定叫做wsgi,它和cgi類似,同樣將請求包裝成一種key-value對,
只不過cgi透過環境變數傳給cgi應用程序,而wsgi直接使用python的字典物件來傳遞。 
hello_app的第一個參數environ就是包含請求訊息的字典對象,第二個參數是個函數,web應用程式在輸出回應內容
前需要先呼叫它來輸出狀態碼和回應頭。
處理web請求和回應這裡使用webob模組來處理請求和回應,需要安裝,這裡首先要安裝setuptools模組,一個包管理的工具,可以透過這個工具自動下載需要的軟體包,類似ubuntu的app- get。以下是網址:http://www.php.cn/安裝結束,可以直接在命令列輸入:easy_install webob這樣就會自動下載安裝。

python模擬Django框架 

 簡單使用

>>> # 匯入Request 物件

>>> from webob import Request

>>> environ = {}

>>> # 使用Request 包裝environ 字典

##>>> # 使用Request 來包裝environ 字典

##>>> # 使用Request 來包裝environ 字典

>>> req = Request(environ)

###使用一個Request類別來包裝environ,然後透過Request物件的屬性和方法對environ進行存取。由於只有在一個web環境才能得到一個真實的environ字典,為了方便大家在shell中進行測試,webob提供了一個模擬簡單web請求的方法:###

python模擬Django框架 

也可以通过req查找其它有用的信息

python模擬Django框架  

同时也可以通过webob模块中的Response对象来包装响应信息。

python模擬Django框架

下面使用webob模块重写之前的hello_app

# -*- coding: utf-8 -*-
from wsgiref import simple_server
from webob import Request, Response


# 我们顺便增加了一个功能,就是根据用户在 URL 后面传递的参数
# 显示相应的内容
def hello_app(request):
 content = []
 # 获取 get 请求的参数
 content.append('Hello %s'%request.GET['name'])
 # 输出所有 environ 变量
 for key, value in request.environ.items():
  content.append('%s : %s' % (key, value))

 response = Response(body='\n'.join(content))
 response.headers['content-type'] = 'text/plain'
 return response

# 对请求和响应进行包装
def wsgi_wrapper(environ, start_response):
 request = Request(environ)
 response = hello_app(request)
 # response 对象本身也实现了与 wsgi 服务器之间通讯的协议,
 # 所以可以帮我们处理与web服务器之间的交互。
 # 这一句比较奇怪,对象使用括号是什么意思。。。。
 return response(environ, start_response)

server = simple_server.make_server('localhost', 8080, wsgi_wrapper)
server.serve_forever()

为了让 wsgi_wrapper 更加通用一点,可以把它设计成装饰器的形式:

# -*- coding: utf-8 -*-
from wsgiref import simple_server
from webob import Request, Response

# 写成装饰器的 wsgi_wrapper
def wsgi_wrapper(func):
 def new_func(environ, start_response):
  request = Request(environ)
  response = func(request)
  return response(environ, start_response)
 new_func.__name__ = func.__name__
 new_func.__doc__ = func.__doc__
 return new_func

# 应用程序
@wsgi_wrapper
def hello_app(request):
 content = []
 content.append('Hello %s'%request.GET['name'])
 for key, value in request.environ.items():
  content.append('%s : %s' % (key, value))

 response = Response(body='\n'.join(content))
 response.headers['content-type'] = 'text/plain'
 return response

server = simple_server.make_server('localhost', 8080, hello_app)
server.serve_forever()

三、模板
果然,还是需要用到模板,不能总是直接在Response中写上长串的html代码。
python中的模板引擎主要有mako, genshi, jinjia等。
mako 主要特点在于模板里面 可以比较方便的嵌入Python代码,而且执行效率一流;
genshi 的特点在于基于 xml, 非常简单易懂的模板语法,对于热爱xhtml的朋友来说是很好的选择,
同时也可以嵌入Python 代码,实现一些复杂的展现逻辑;
jinja genshi 一样拥有很简单的模板语法,只是不 依赖于 xml 的格式,同样很适合设计人员直接进行模板的制作,
同时也可以嵌入Python 代码实现一些复杂的展现逻辑。 

这里使用Mako,地址ttp://pypi.python.org/pypi/Mako,下载python setup.py install进行安装
简单的模块例子:

## -*- coding: utf-8 -*-
<html>
 <head>
 <title>简单mako模板</title>
 </head>
 <body>
 <h5>Hello ${name}!</h5>
 <ul>
  % for key, value in data.items():
  <li>
  ${key} - ${value}
  <li>
  % endfor
 </ul>
 </body>
</html>

保存为simple.html文件,然后需要给模板对象传递data和name两个参数,然后进行渲染,就可以输入html内容

# -*- coding: utf-8 -*-
# 导入模板对象
from mako.template import Template
# 使用模板文件名构造模板对象
tmpl = Template(filename=&#39;./simple.html&#39;, output_encoding=&#39;utf-8&#39;)
# 构造一个简单的字典填充模板,并print出来
print tmpl.render(name=&#39;python&#39;, data = {&#39;a&#39;:1, &#39;b&#39;:2})

保存为test_template.py文件,运行就可以输入内容:
$ python test_template.py

<html>
 <head>
 <title>简单mako模板</title>
 </head>
 <body>
 <h5>Hello python!</h5>
 <ul>
  <li>
  a - 1
  <li>
  <li>
  b - 2
  <li>
 </ul>
 </body>
</html>

下面对hello_app程序进行重构:
1. 把 wsgi_wrapper 单独放到通用模块 utils.py:

# -*- coding: utf-8 -*-
from webob import Request

def wsgi_wrapper(func):
 def new_func(environ, start_response):
  request = Request(environ)
  response = func(request)
  return response(environ, start_response)
 new_func.__name__ = func.__name__
 new_func.__doc__ = func.__doc__
 return new_func

2. 把 hello_app 给彻底独立出来,形成单独的模块 controller.py :

# -*- coding: utf-8 -*-
from utils import wsgi_wrapper
from webob import Response
from mako import Template

# 整合了模板功能的 hello_app
@wsgi_wrapper
def hello_app(request):
 tmpl = Template(filename=&#39;./simple.html&#39;, output_encoding=&#39;utf-8&#39;)
 content = tmpl.render(name=request.GET[&#39;name&#39;], data=request.environ)
 return Response(body=content)

3. 这样 main.py 就变成这样了:

# -*- coding: utf-8 -*-
from wsgiref import simple_server
from controller import hello_app

server = simple_server.make_server(&#39;localhost&#39;, 8080, hello_app)
server.serve_forever()

四、ORM(Object Relation Mapping, 对象关系映射)
终于也要这一步了,作为web应用,还是需要与数据库进行合作。
这里使用sqlalchemy,是一个 ORM (对象-关系映射)库,提供Python对象与关系数据库之间的映射。和Django的models
用法很像,也是可以通过python代码来创建数据库表,并进行操作。
sqlalchemy 还可以自动映射 Python 对象的继承,可以实现eager loading、lazy loading, 可以直接将 Model 映射到自定
义的 SQL 语句,支持n多的数据库等等等等。 可以说 sqlalchemy 既有不输于 Hibernate 的强大功能,同时不失 Python
的简洁优雅。
使用方法:

# -*- coding: utf-8 -*-
from sqlalchemy import *
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base

# 创建数据库引擎,这里我们直接使用 Python2.5 自带的数据库引擎:sqlite,
# 直接在当前目录下建立名为 data.db 的数据库
engine = create_engine(&#39;sqlite:///data.db&#39;)
# sqlalchemy 中所有数据库操作都要由某个session来进行管理
# 关于 session 的详细信息请参考:http://www.sqlalchemy.org/docs/05/session.html
Session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
Base = declarative_base()

class Dictionary(Base):
 # Python 对象对应关系数据库的表名
 __tablename__ = &#39;t_dictionary&#39;
 # 定义自动,参数含义分别为:数据库字段名,字段类型,其他选项
 key = Column(&#39;key&#39;, String(255), primary_key=True)
 value = Column(&#39;value&#39;, String(255))

# 创建数据库
Base.metadata.create_all(engine)

session = Session()
for item in [&#39;python&#39;,&#39;ruby&#39;,&#39;java&#39;]:
 # 构造一个对象
 dictionary = Dictionary(key=item, value=item.upper())
 # 告诉 sqlalchemy ,将该对象加到数据库
 session.add(dictionary)

# 提交session,在这里才真正执行数据库的操作,添加三条记录到数据库
session.commit()

# 查询数据库中Dictionary对象对应的数据
for dictionary in session.query(Dictionary):
 print dictionary.key, dictionary.value

上面的代码你执行两遍就会报错,为什么。。。因为插入数据库的主键重复了。。。。
 这样就可以整合到之前的controller.py文件中

# -*- coding: utf-8 -*-
from utils import wsgi_wrapper
from webob import Response
from mako.template import Template
# 导入公用的 model 模块
from model import Session, Dictionary

@wsgi_wrapper
def hello_app(request):
 session = Session()
 # 查询到所有 Dictionary 对象
 dictionaries = session.query(Dictionary)
 # 然后根据 Dictionary 对象的 key、value 属性把列表转换成一个字典
 data = dict([(dictionary.key, dictionary.value) for dictionary in dictionaries])

 tmpl = Template(filename=&#39;./simple.html&#39;, output_encoding=&#39;utf-8&#39;)
 content = tmpl.render(name=request.GET[&#39;name&#39;], data=data)
 return Response(body=content)

五、URL分发控制
给不同的资源设计不同的 URL, 客户端请求这个 URL,web应用程序再根据用户请求的 URL 定位到具体功能并执行之。
提供一个干净的 URL 有很多好处:
1. 可读性,通过 URL 就可以大概了解其提供什么功能
2. 用户容易记住也方便直接输入
3.设计良好的 URL 一般都更短小精悍,对搜索引擎也 更友好
使用selector模块来处理url映射
下载地址http://pypi.python.org/pypi/selector, 下载那个source文件进行python setup.py install
 首先把urls的配置单独放到urls.py中

# -*- coding: utf-8 -*-
from controller import hello_app
mappings = [(&#39;/hello/{name}&#39;, {&#39;GET&#39;:hello_app})]

修改main.py

# -*- coding: utf-8 -*-
from wsgiref import simple_server
from urls import mappings
from selector import Selector

# 构建 url 分发器
app = Selector(mappings)
server = simple_server.make_server(&#39;localhost&#39;, 8080, app)
server.serve_forever()

然后,在 hello_app 中就可以通过 environ['wsgiorg.routing_args'] 获取到 name 参数了,
不过在 wsgi_wrapper 其实还可以进一步简化 hello_app 的工作: 直接把解析得到的参数
当作函数参数传过去!修改 utils.py:

from webob import Request

def wsgi_wrapper(func):
 def new_func(environ, start_response):
  request = Request(environ)
  position_args, keyword_args = environ.get(&#39;wsgiorg.routing_args&#39;, ((), {}))
  response = func(request, *position_args, **keyword_args)
  return response(environ, start_response)
 new_func.__name__ = func.__name__
 new_func.__doc__ = func.__doc__
 return new_func

那 hello_app 就可以改成这样了:

...
@wsgi_wrapper
def hello_app(request, name=&#39;&#39;):
 ...
 content = tmpl.render(name=name, data=data)
 return Response(body=content)
执行main.py,访问http://localhost:8080/hello/Python

总结
以上部分的实现,就是类似Django框架中的几个主要的功能模块,希望对大家的学习有所帮助。

更多python模擬Django框架相关文章请关注PHP中文网!


陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn