TurboGears 快速指南



TurboGears - 概述

什么是 Web 框架?

Web 应用程序框架或简称 Web 框架表示一组库和模块,它使 Web 应用程序开发人员能够编写应用程序,而无需担心诸如协议、线程管理等底层细节。

Web Frameworks

什么是 TurboGears?

TurboGears 是一个用 Python 编写的 Web 应用程序框架。最初由 Kevin Dangoor 于 2005 年创建,其最新版本 TurboGears(版本 2.3.7)由 Mark Ramm 和 Florent Aide 领导的开发人员团队管理。

TurboGears 遵循模型-视图-控制器范式,就像大多数现代 Web 框架(如 Rails、Django、Struts 等)一样。

模型视图控制器

MVC 是一种用于开发 Web 应用程序的软件设计模式。模型视图控制器模式由三个部分组成:

  • 模型 - 模式最低层负责维护数据。

  • 视图 - 负责向用户显示所有或部分数据。

  • 控制器 - 控制模型和视图之间交互的软件代码。

MVC 很受欢迎,因为它将应用程序逻辑与用户界面层隔离开,并支持关注点分离。在这里,控制器接收应用程序的所有请求,然后与模型一起准备视图所需的所有数据。然后,视图使用控制器准备的数据生成最终的可呈现响应。MVC 抽象可以以图形方式表示如下:

MVC

模型

模型负责管理应用程序的数据。它响应来自视图的请求,并且它还响应来自控制器的更新自身指令。

视图

以特定格式呈现数据,由控制器决定呈现数据。它们是基于脚本的模板系统,非常易于与 AJAX 技术集成。

控制器

控制器负责响应用户输入并对数据模型对象执行交互。控制器接收输入,验证输入,然后执行修改数据模型状态的业务操作。

TurboGears 建立在许多库和工具之上。这些工具在 TurboGears 的不同版本之间有所变化。当前版本(版本 2.3.7)的组件列在下面。

SQLAlchemy

它是一个开源 SQL 工具包,为 Python 代码提供对象关系映射 (ORM)。

SQLAlchemy

Genshi

此模板引擎用于构建 TG 应用程序的前端。Web 模板系统将模板与某个数据源结合以呈现动态网页。

ToscaWidgets

这是一个用于生成带有服务器端控件的 HTML 表单的小部件库。Tosca 还充当与 JavaScript 小部件和工具包连接的中间件。

Gearbox

它提供了一组命令来管理项目和服务 TurboGears 应用程序。TurboGears 应用程序可以部署在任何符合 WSGI 的 Web 服务器上。

Web 服务器网关接口 (WSGI) 已被采用作为 Python Web 应用程序开发的标准。WSGI 是 Web 服务器和 Web 应用程序之间通用接口的规范。wsgiref 包是 WSGI 的参考实现。它用于为 Web TurboGears Web 框架添加 WSGI 支持。此包中的 simple_server 模块实现了一个简单的 HTTP 服务器,该服务器服务于 WSGI 应用程序。我们将使用它来测试在本教程中开发的应用程序。

TurboGears - 环境

先决条件

Python 2.6 或更高版本。早期版本的 TurboGears 与 Python 3.X 不兼容。最新版本声称可以在 Python 3.X 上正常工作。但是,TurboGears 的官方文档仍然基于 Python 2.7 环境。

以下命令安装 virtualenv

pip install virtualenv

此命令需要管理员权限。在 Linux/Mac OS 上在 pip 之前添加sudo。如果您使用的是 Windows,请以管理员身份登录。在 Ubuntu 上,可以使用其包管理器安装 virtualenv。

Sudo apt-get install virtualenv

安装完成后,新的虚拟环境将在文件夹中创建。

mkdir newproj
cd newproj
virtualenv venv

要激活相应的环境,在Linux/OS X

venv/bin/activate

Windows

venv\scripts\activate

现在我们准备好在该环境中安装 TurboGears。通过以下命令可以进行 TurboGears 的最小安装:

pip install TurboGears2

上述命令可以在没有虚拟环境的情况下直接运行以进行系统范围的安装。

要安装 TurboGears 以及开发工具,请使用以下命令:

pip install tg.devtools

TurboGears - 第一个程序

TurboGears 具有最小模式,可以快速创建单文件应用程序。可以使用最少的依赖项快速构建简单的示例和服务。

TG 应用程序中的 Application 类继承自TGController类。此类中的方法可通过tg模块中的@expose装饰器访问。在我们的第一个应用程序中,index()方法被映射为我们应用程序的根目录。TGController 类也需要从tg模块导入。

from tg import expose, TGController
class MyController(TGController):
   @expose()
   def index(self):
      return 'Hello World turbogears'

接下来,设置应用程序的配置并声明应用程序对象。AppConfig类的构造函数在这里接受两个参数 - 将最小属性设置为 true 和控制器类。

config = AppConfig(minimal = True, root_controller = RootController())
application = config.make_wsgi_app()

此处的make_wsgi_app()函数构建应用程序对象。

为了提供此应用程序,我们现在需要启动 HTTP 服务器。如前所述,我们将使用wsgiref包中的simple_server模块来设置和启动它。此模块具有make_server()方法,该方法需要端口号和应用程序对象作为参数。

from wsgiref.simple_server import make_server
server = make_server('', 8080, application)
server.serve_forever()

这意味着我们的应用程序将在 localhost 的 8080 端口上提供服务。

以下是我们的第一个 TurboGears 应用程序的完整代码:

app.py

from wsgiref.simple_server import make_server
from tg import expose, TGController, AppConfig

class MyController(TGController):

   @expose()
   def index(self):
      return 'Hello World TurboGears'
		 
config = AppConfig(minimal = True, root_controller = MyController())
application = config.make_wsgi_app()

print "Serving on port 8080..."
server = make_server('', 8080, application)
server.serve_forever()

从 Python shell 运行上述脚本。

Python app.py

在浏览器的地址栏中输入https://127.0.0.1:8080以查看“Hello World TurboGears”消息。

TurboGears 的tg.devtools包含 Gearbox。它是一组命令,可用于管理更复杂的 TG 项目。可以通过以下 Gearbox 命令快速创建完整堆栈项目:

gearbox quickstart HelloWorld

这将创建一个名为HelloWorld的项目。

TurboGears - 依赖项

TurboGears 项目包含以下目录:

  • Config - 项目设置和配置所在位置

  • Controllers - 所有项目控制器,Web 应用程序的逻辑

  • i018n - 支持的语言的翻译文件

  • Lib - 实用程序 Python 函数和类

  • Model - 数据库模型

  • Public Static Files - CSS、JavaScript 和图像

  • Templates - 我们的控制器公开的模板。

  • Tests - 执行的测试集。

  • Websetup - 在应用程序设置时执行的功能。

如何安装项目

现在需要安装此项目。项目的基本目录中已提供setup.py。执行此脚本时将安装项目依赖项。

Python setup.py develop

默认情况下,在项目设置时会安装以下依赖项:

  • Beaker
  • Genshi
  • zope.sqlalchemy
  • sqlalchemy
  • alembic
  • repoze.who
  • tw2.forms
  • tgext.admin ≥ 0.6.1
  • WebHelpers2
  • babel

安装后,通过在 shell 中发出以下命令在开发服务器上启动服务项目:

Gearbox serve –reload –debug

按照上述命令为预构建的示例项目提供服务。在浏览器中打开https://127.0.0.1:8080。这个现成的示例应用程序简要介绍了 TurboGears 框架本身。

Project Window

在此 Hello 项目中,默认控制器在 controllers 目录中创建为Hello/hello/controllers.root.py。让我们使用以下代码修改 root.py

from hello.lib.base import BaseController
from tg import expose, flash

class RootController(BaseController):
   movie = MovieController()
   @expose()
   def index(self):
      return "<h1>Hello World</h1>"
		
   @expose()
   def _default(self, *args, **kw):
      return "This page is not ready"

准备好基本的工作应用程序后,可以在控制器类中添加更多视图。在上面的Mycontroller类中,添加了一个新方法sayHello()@expose()装饰器将/sayHello URL 附加到它。此函数旨在从 URL 中接受名称作为参数。

通过“gearbox serve”命令启动服务器后,https://127.0.0.1:8080。即使输入以下 URL,也会在浏览器中显示 Hello World 消息:

https://127.0.0.1:8080/

https://127.0.0.1:8080/index

所有这些 URL 都映射到RootController.index()方法。此类还具有_default()方法,当 URL 未映射到任何特定函数时将调用该方法。对 URL 的响应通过 @expose() 装饰器映射到函数。

可以从 URL 向公开的函数发送参数。以下函数从 URL 读取名称参数。

@expose()
def sayHello(self, name):
   return '<h3>Hello %s</h3>' %name

浏览器中将显示以下输出作为对 URL 的响应:https://127.0.0.1:8080/?name=MVL

Hello MVL

TurboGears 自动将 URL 参数映射到函数参数。我们的 RootController 类继承自 BaseController。这在应用程序的lib 文件夹中的base.py中定义。

其代码如下:

from tg import TGController, tmpl_context
from tg import request

__all__ = ['BaseController']

def __call__(self, environ, context):
   tmpl_context.identity = request.identity
   return TGController.__call__(self, environ, context)

TGController.__call__将请求路由到的控制器方法分派出去。

TurboGears - 服务模板

尽管可以通过 HTML 内容返回到浏览器,但对于更高级的输出,始终建议使用模板引擎。在由 Gearbox “快速启动” 的完整堆栈项目中,Genshi 被启用为默认的模板渲染器。在最小应用程序中,Genshi(或任何其他模板引擎,例如 jinja)需要安装和启用。Genshi 模板引擎允许使用纯 xhtml 编写模板,并在编译时对其进行验证以检测问题并防止提供损坏的页面。

模板通过使用点表示法来引用。在我们的 Hello 项目中,提供了一个 templates 目录来存储模板网页。因此,**sample.html** 将被引用为 **hello.templates.sample**(未提及扩展名)。TurboGears 通过 expose 装饰器渲染此模板,以通过 **tg.render_template()** 函数将其与控制器方法链接。

公开的控制器函数返回一个 Python 字典对象。此字典对象依次传递给链接的模板。模板中的占位符将使用字典值填充。

首先,让我们显示一个带有纯 html 脚本的网页。公开的控制器返回一个 **空字典对象**,因为我们不打算发送任何数据到 HTML 脚本中进行解析。

如何创建示例 HTML

我们的 **sample.html** 如下所示。确保将其存储在项目的 templates 目录中。

<html>
   <head>
      <title>TurboGears Templating Example</title>
   </head>
	
   <body>
      <h2>Hello, Welcome to TurboGears!.</h2>
   </body>
</html>

在 **root.py** 中添加 **sample()** 函数,并通过它公开 sample.html。

@expose("hello.templates.sample")
   def sample(self):
      return {}

在启动 Web 服务器后输入 URL **https://127.0.0.1:8080/sample**,浏览器将显示以下结果。

Display Result

如上所述,字典对象作为参数集合发送到 Genshi 模板。此模板包含“占位符”,这些占位符将使用从控制器接收的参数动态填充。

让我们更改 **sample()** 函数以将字典对象发送到 sample 模板。

@expose("hello.templates.sample")
   def sample(self,name):
      mydata = {'person':name}
      return mydata

在 templates 文件夹中创建 **sample.html**(**templates\sample.html**)

<html>
   <head>
      <title>TurboGears Templating Example</title>
   </head>
	
   <body>
      <h2>Hello, my name is ${person}!.</h2>
   </body>
</html>

在上面的 HTML 代码中,**${person}** 是占位符。在浏览器中输入 **https://127.0.0.1:8080/sample?name=MVL** 作为 URL。此 URL 映射到我们根控制器中的 **sample()** 方法。它返回一个字典对象。这由 templates 目录中链接的模板页面 sample.html 获取。然后,网页中的 ${person} 将被 MVL 替换。

也可以在控制器函数中访问 HTML 表单数据。HTML 表单用于发送表单数据。

Result

TurboGears - HTTP 方法

HTTP 协议是万维网中数据通信的基础。此协议中定义了从指定 URL 检索数据的不同方法。下表总结了不同的 HTTP 方法:

序号 HTTP 方法及描述
1

GET

以未加密的形式将数据发送到服务器。最常见的方法。

2

HEAD

与 GET 相同,但没有响应主体

3

POST

用于将 HTML 表单数据发送到服务器。POST 方法接收的数据不会被服务器缓存。

4

PUT

用上传的内容替换目标资源的所有当前表示形式。

5

DELETE

删除 URL 指定的目标资源的所有当前表示形式

创建 HTML 表单

让我们创建一个 HTML 表单并将表单数据发送到 URL。将以下脚本保存为 login.html

<html>
   <body>
      <form action = "https://127.0.0.1:8080/login" method = "get">
         <p>Enter Name:</p>
         <p><input type = "text" name = "nm" /></p>
         <p><input type = "submit" value = "submit" /></p>
      </form>
   </body>
</html>

此表单中输入的数据将提交到 **'/login' URL**。现在创建一个控制器函数 **loginpage()** 并将上述 html 页面公开给它。

@expose("hello.templates.login")
   def loginpage(self):
      return {}

为了接收表单数据,提供一个 **login()** 控制器,该控制器具有表单属性作为其参数。这里 **'nm'** 是登录表单中文本输入字段的名称,在 login() 函数中使用相同的名称作为参数。

@expose("hello.templates.sample")
   def login(self, nm):
      name = nm
      return {'person':name}

可以看出,从登录表单接收到的数据被发送到 sample.html 模板(之前使用过)。它由 **Genshi 模板引擎** 解析以生成以下输出:

Genshi Result

POST 方法

当 HTML 表单使用 POST 方法将数据分派到 action 属性中的 URL 时,表单数据不会在 URL 中公开。编码后的数据由控制器函数以 **dict** 参数接收。下面的 **kw** 参数是保存表单数据的字典对象。

HTML 表单包含两个文本输入字段。

<html>
   <body>
	
      <form action = "https://127.0.0.1:8080/marks" method = "post">
         <p>Marks in Physics:</p>
         <p><input type = "text" name = "phy" /></p>
         <p>Marks in Maths:</p>
         <p><input type = "text" name = "maths" /></p>
         <p><input type = "submit" value = "submit" /></p>
      </form>
		
   </body>	
</html>

**marks()** 控制器接收表单数据并将其发送到 **sample.html** 模板。**root.py** 的代码如下:

from hello.lib.base import BaseController
from tg import expose, request

class RootController(BaseController):
   @expose("hello.templates.marks")
   def marksform(self):
      return {}
		
   @expose("hello.templates.sample")
   def marks(self, **kw):
      phy = kw['phy']
      maths = kw['maths']
      ttl = int(phy)+int(maths)
      mydata = {'phy':phy, 'maths':maths, 'total':ttl}
      return mydata

最后,sample.html 模板如下:

<html>
   <head>
      <title>TurboGears Templating Example</title>
   </head>
	
   <body>
      <h2>Hello, Welcome to TurboGears!.</h2>
      <h3>Marks in Physics: ${phy}.</h3>
      <h3>Marks in Maths: ${maths}.</h3>
      <h3>Total Marks: ${total}</h3>
   </body>
	
</html>

启动服务器(如果尚未运行)

Gearbox server –reload –debug

在浏览器中输入 **https://127.0.0.1::8080/marksform**

Sample Template

**sample.html** 将呈现以下输出:

Sample Html Result

TurboGears - Genshi 模板语言

Genshi 是一种基于 XML 的模板语言。它类似于 **Kid**,Kid 曾是早期版本的 TurboGears 的模板引擎。Genshi 和 Kid 都受到其他知名模板语言(如 **HSLT、TAL** 和 **PHP**)的启发。

Genshi 模板由处理指令组成。这些指令是模板中的元素和属性。Genshi 指令在命名空间 http://genshi.edgewall.org/ 中定义。因此,需要在模板的根元素中声明此命名空间。

<html xmlns = "http://www.w3.org/1999/xhtml"
   xmlns:py = "http://genshi.edgewall.org/"
   lang = "en">
...
</html>

以上声明意味着默认命名空间设置为 XHTML,而 Genshi 指令具有 'py' 前缀。

Genshi 指令

Genshi 中定义了许多指令。以下列表枚举了 Genshi 指令:

  • py:if
  • py:choose
  • py:for
  • py:def
  • py:match
  • py:with
  • py:replace
  • py:content
  • py:attrs
  • py:strip

条件节

Genshi 提供了两个用于条件渲染内容的指令:py:if 和 py:choose。

py:if

仅当 **if 子句** 中的表达式计算结果为 true 时,才会渲染此指令元素的内容。假设模板上下文中的数据为 **{‘foo’:True, ‘bar’:’Hello’}**,则以下指令:

<div>
   <b py:if = "foo">${bar}</b>
</div>

将产生以下结果

Hello

但是,如果 **'foo' 设置为 False**,则不会渲染此输出。

此指令也可以用作元素。在这种情况下,**<py:if>** 必须由相应的 **</py:if>** 关闭。

<div>
   <py:if test = "foo">
      <b>${bar}</b>
   </py:if>
</div>

py:choose

可以使用 **py:choose** 结合 **py:when** 和 **py:otherwise** 指令进行高级条件处理。此功能类似于 **C/C++** 中的 **switch-case** 结构。

**py:choose** 指令中的表达式将使用 **py:when** 替代项标识的不同值进行检查,并将渲染相应的内容。可以在 **py:otherwise** 指令的形式下提供默认替代项。

<div py:choose = "foo”>
   <span py:when = "0">0</span>
   <span py:when = "1">1</span>
   <span py:otherwise = "">2</span>
</div>

以下示例说明了 **py:choose** 和 **py:when** 指令的使用。HTML 表单将数据发布到 /marks URL。**marks()** 函数重定向分数并将结果以字典对象的形式发送到 **total.html** 模板。通过使用 **py:choose** 和 **py:when** 指令实现 **结果及格/不及格** 的条件显示。

输入分数的 HTML 脚本(**marks.html**)如下:

<html>
   <body>
      <form action = "https://127.0.0.1:8080/marks" method = "post">
         <p>Marks in Physics:</p>
         <p><input type = "text" name = "phy" /></p>
         <p>Marks in Maths:</p>
         <p><input type = "text" name = "maths" /></p>
         <p><input type = "submit" value = "submit" /></p>
      </form>
   </body>
</html>

**root.py** 的完整代码如下。**marks()** 控制器将分数和结果发送到 **total.html** 模板:

from hello.lib.base import BaseController
from tg import expose, request

class RootController(BaseController):
   @expose("hello.templates.marks")
      def marksform(self):
      return {}
		
   @expose("hello.templates.total")
      def marks(self, **kw):
      phy = kw['phy']
      maths = kw['maths']
      ttl = int(phy)+int(maths)
      avg = ttl/2
		
      if avg ≥ 50:
         mydata = {'phy':phy, 'maths':maths, 'total':ttl, 'result':2}
      else:
         mydata = {'phy':phy, 'maths':maths, 'total':ttl,'result':1}
	
      return mydata

templates 文件夹中的 **total.html** 接收字典数据并在 html 输出中按如下方式有条件地解析它:

<html xmlns = "http://www.w3.org/1999/xhtml"
   xmlns:py = "http://genshi.edgewall.org/"
   lang = "en">
	
   <head>
      <title>TurboGears Templating Example</title>
   </head>
	
   <body>
      <h2>Hello, Welcome to TurboGears!.</h2>
      <h3>Marks in Physics: ${phy}.</h3>
      <h3>Marks in Maths: ${maths}.</h3>
      <h3>Total Marks: ${total}</h3>
		
      <div py:choose = "result">
         <span py:when = "1"><h2>Result: Fail</h2></span>
         <span py:when = "2"><h2>Result: Pass</h2></span>
      </div>
		
   </body>
</html>

启动服务器(如果尚未运行)

Gearbox server –reload –debug

在浏览器中输入 **https://127.0.0.1::8080/marksform**

Result Window

**total.html** 将呈现以下输出:

Total Html

py:for

py:for 指令中的元素将为可迭代对象(通常是 Python 列表对象)中的每个项目重复。如果 **items = [1,2,3]** 存在于模板上下文中,则可以通过以下 py:for 指令对其进行迭代:

<ul>
   <li py:for = "item in items">${item}</li>
</ul>

将呈现以下输出:

1
2
3

以下示例显示了使用 py:for 指令在 total.html 模板中渲染的 HTML 表单数据,也可以按如下方式使用:

<py:for each = "item in items">
   <li>${item}</li>
</py:for>

HTML 表单脚本

<html>
   <body>
	
      <form action = "https://127.0.0.1:8080/loop" method="post">
         <p>Marks in Physics:</p>
         <p><input type = "text" name = "phy" /></p>
         <p>Marks in Chemistry:</p>
         <p><input type = "text" name = "che" /></p>
         <p>Marks in Maths:</p>
         <p><input type = "text" name = "maths" /></p>
         <p><input type = "submit" value = "submit" /></p>
      </form>
		
   </body>
</html>

**loop()** 控制器读取表单数据并将其以列表对象的形式发送到 total.template。

from hello.lib.base import BaseController
from tg import expose, request

class RootController(BaseController):
   @expose("hello.templates.marks")
   def marksform(self):
   return {}
	
   @expose("hello.templates.temp")
   def loop(self, **kw):
      phy = kw['phy']
      maths = kw['maths']
      che = kw['che']
      l1 = []
      l1.append(phy)
      l1.append(che)
      l1.append(maths)
		
   return ({'subjects':['physics', 'Chemistry', 'Mathematics'], 'marks':l1})

temp.html 模板使用 py:for 循环以表格的形式渲染 dict 对象的内容。

<html xmlns = "http://www.w3.org/1999/xhtml" 
   xmlns:py = "http://genshi.edgewall.org/" lang = "en">
	
   <body>
      <b>Marks Statement</b>
      <table border = '1'>
         <thead>
            <py:for each = "key in subjects"><th>${key}</th></py:for>
         </thead>
         <tr>
            <py:for each = "key in marks"><td>${key}</td></py:for>
         </tr>
      </table>
   </body>
</html>

启动服务器(如果尚未运行)

gearbox server –reload –debug

在浏览器中输入 **https://127.0.0.1::8080/marksform**。

Window Result

提交上述表单后,浏览器将显示以下输出。

Form Output

py:def

此指令用于创建宏。宏是可重用的模板代码片段。与 Python 函数非常相似,它具有名称,并且可以可选地具有参数。此宏的输出可以插入模板中的任何位置。

py:def 指令遵循以下语法:

<p py:def = "greeting(name)">
   Hello, ${name}!
</p>

此宏可以使用变量值渲染到 'name' 参数。

${greeting('world')}
${greeting('everybody)}

此指令也可以使用另一种语法版本,如下所示:

<py:def function = "greeting(name)">
   <p>Hello, ${name}! </p>
</py:def>

在以下示例中,**root.py** 中的 **macro()** 控制器将一个具有两个键 name1 和 name2 的 **dict** 对象发送到 macro.html 模板。

from hello.lib.base import BaseController
from tg import expose, request

class RootController(BaseController):
   @expose('hello.templates.macro')
   def macro(self):
      return {'name1':'TutorialPoint', 'name2':'TurboGears'}

此 macro.html 模板包含一个名为 greeting 的宏的定义。它用于为从控制器接收的数据生成问候语消息。

<html xmlns = "http://www.w3.org/1999/xhtml"
   xmlns:py = "http://genshi.edgewall.org/"
   lang = "en">
	
   <body>
      <h2>py:def example</h2>
		
      <div>
         <div py:def = "greeting(name)">
            Hello, Welcome to ${name}!
         </div>
				
         <b>
            ${greeting(name1)}
            ${greeting(name2)}
         </b>
			
      </div>
   </body>
</html>

使用 gearbox 启动服务器

gearbox serve –reload –debug

通过在浏览器中输入以下 URL 调用 macro() 控制器:

https://127.0.0.1:8080/macro

浏览器将呈现以下输出:

Def Example

py:with

此指令允许您将表达式分配给局部变量。这些局部变量使表达式内部更简洁且更高效。

假设模板的上下文数据中给出了 x = 50,则以下为 py:with 指令:

<div>
   <span py:with = "y = 50; z = x+y">$x $y $z</span>
</div>

它将产生以下输出:

50 50 100

py:with 指令也提供了一个替代版本:

<div>
   <py:with = "y = 50; z = x+y">$x $y $z</py:with>
</div>

在以下示例中,macro() 控制器返回一个具有 name、phy 和 maths 键的 dict 对象。

from hello.lib.base import BaseController
from tg import expose, request

class RootController(BaseController):
   @expose('hello.templates.macro')
   def macro(self):
      return {'name':'XYZ', 'phy':60, 'maths':70}

macro.html 模板使用 py:with 指令添加 phy 和 maths 键的值。

<html xmlns = "http://www.w3.org/1999/xhtml"
   xmlns:py = "http://genshi.edgewall.org/"
   lang = "en">
	
   <body>
      <h2>py:with example</h2>
      <h3>Marks Statement for : ${name}!</h3>
		
      <b>Phy: $phy Maths: $maths
         <span py:with = "ttl = phy+maths">Total: $ttl</span>
      </b>
		
   </body>
	
</html>

浏览器将对 URL **https://127.0.0.1:8080/macro** 响应以下输出

Py:for Example

结构操作指令

**py:attrs** 指令添加、修改或删除元素的属性。

<ul>
   <li py:attrs = "foo">Bar</li>
</ul>

如果 **foo = {‘class’:’collapse’}** 存在于模板上下文中,则上述代码段将渲染以下内容。

<ul>
   <li class = "collapse">Bar</li>
</ul>

**py:content** 指令使用表达式计算结果替换任何嵌套内容:

<ul>
   <li py:content = "bar">Hello</li>
</ul>

在上下文数据中给定 bar = 'Bye',这将产生以下结果

<ul>
   <li>Bye</li>
</ul>

**py:replace** 指令使用表达式计算结果替换元素本身:

<div>
   <span py:replace = "bar">Hello</span>
</div>

如果上下文数据中给定 bar = 'Bye',则会生成

<div>
   Bye
</div>

TurboGears - 包含

另一个 XML 文档(尤其是 HTML 文档)的内容可以通过在当前文档中使用包含标签来包含。为了启用这种包含,必须在 HTML 文档的根元素中声明 XInclude 命名空间。

<html xmlns = "http://www.w3.org/1999/xhtml" xmlns:xi = "http://www.w3.org/2001/XInclude >

以上声明指定 include 指令包含'xi' 前缀。要在当前文档中添加另一个 html 页面的内容,请按如下方式使用 xi:include 指令:

<xi:include href = "somepage.html" />

在下面的示例中,root.py 包含 include() 控制器,它公开 include.html。

from hello.lib.base import BaseController
from tg import expose, request

class RootController(BaseController):
   @expose('hello.templates.include')
   def include(self):
      return {}

头部和页脚 HTML

在 include.html 中,声明了 include 命名空间,并添加了 heading.html 和 footer.html 的内容。以下是 templates\include.html 的 HTML 脚本:

<html xmlns = "http://www.w3.org/1999/xhtml" 
   xmlns:xi = "http://www.w3.org/2001/XInclude">
	
   <head>
      <title>TurboGears Templating Example</title>
   </head>
	
   <body>
      <xi:include href = "heading.html" />
      <h2>main content </h2>
      <xi:include href = "footer.html" />
   </body>
	
</html>

以下是 templates\heading.html 代码:

<html>
   <head>
      <title>TurboGears Templating Example</title>
   </head>
	
   <body>
      <h1>This is page Header</h1>
   </body>
</html>

以下是 templates\footer.html

<html>
   <head>
      <title>TurboGears Templating Example</title>
   </head>
	
   <body>
      <h3>This is page footer</h3>
   </body>
</html>

使用 gearbox 启动开发,并在浏览器中输入https://127.0.0.1:8080/include。呈现的输出将如下所示:

Template Examples

这样就可以实现视图的模块化构建。如果 xi:include 指令中提到的资源不可用,则会引发错误。在这种情况下,可以使用 xi:fallback 加载备用资源。

<xi:include href = “main.html”>
   <xi:fallback href = ”default.html”/>
</xi.include>

内容的包含可以是动态的,因为 href 属性可以包含表达式。

在 root.py 中添加以下控制器。

@expose('hello.templates.ref-include')
   def refinclude(self):
      return {'pages':['heading','main','footer']}

将以下代码保存为 templates 文件夹中的 ref-include.html。

<html xmlns = "http://www.w3.org/1999/xhtml"
   xmlns:py = "http://genshi.edgewall.org/"
   xmlns:xi = "http://www.w3.org/2001/XInclude">
	
   <head>
      <title>TurboGears Templating Example</title>
   </head>
	
   <body>
      <xi:include href = "${name}.html" py:for = "name in pages" />
   </body>
	
</html>

在启动服务器之前,请确保 templates 文件夹中包含 heading.html、main.html 和 footer.html。在浏览器中输入https://127.0.0.1:8082/refinclude以获取以下输出

Footer Template

TurboGears - JSON 渲染

@expose() 装饰器默认情况下会呈现 html 内容。但是,这可以设置为json 内容类型。TurboGears 通过tg.jsonify.JSONEncoder (**kwargs) 类支持 json 渲染。要渲染 json 数据,只需将 json 作为内容类型传递给 expose 装饰器即可。

@expose('json')
def jsondata(self, **kwargs):
   return dict(hello = 'World')

如果在浏览器中输入'/jsondata' URL,它将通过显示以下内容进行响应:

{"hello": "World"}

jsonp 渲染

jsonp 代表 json with padding。它的工作原理类似于 json 输出,只是它提供了一个 application/javascript 响应,并调用一个 javascript 函数,将控制器返回的所有值作为函数参数提供。

要启用 jsonp 渲染,您必须首先将其追加到应用程序中的必需引擎列表中 - config/app_cfg.py -

base_config.renderers.append('jsonp')

按如下方式编写您的 expose 装饰器:

@expose('json')
@expose('jsonp')
def jsonpdata (self, **kwargs): 
   return dict(hello = 'World')

访问 /jsonpdata?callback = callme 时,您应该看到:

callme({"hello": "World"});

TurboGears - URL 层次结构

有时,Web 应用程序可能需要具有多个级别的 URL 结构。TurboGears 可以遍历对象层次结构以找到可以处理您的请求的适当方法。

使用 gearbox “快速启动” 的项目在项目的 lib 文件夹中有一个 BaseController 类。它可用作 'Hello/hello/lib/base.py'。它充当所有子控制器的基类。为了在应用程序中添加 URL 的子级,设计一个名为 BlogController 的子类,该类派生自 BaseController。

此 BlogController 有两个控制器函数,index() 和 post()。两者都旨在分别公开一个模板,即 blog.html 和 post.html。

注意 - 这些模板放在一个子文件夹中 - templates/blog

class BlogController(BaseController):

   @expose('hello.templates.blog.blog')
   def index(self):
      return {}
		
   @expose('hello.templates.blog.post')
   def post(self):
      from datetime import date
      now = date.today().strftime("%d-%m-%y")
      return {'date':now}

现在在 RootController 类(在 root.py 中)中声明此类的对象,如下所示:

class RootController(BaseController):
   blog = BlogController()

其他顶级 URL 的控制器函数将像以前一样存在于此类中。

当输入 URL https://127.0.0.1:8080/blog/ 时,它将映射到 BlogController 类中的 index() 控制器函数。类似地,https://127.0.0.1:8080/blog/post 将调用 post() 函数。

blog.html 和 post.html 的代码如下所示:

Blog.html

<html>
   <body>
      <h2>My Blog</h2>
   </body>
</html>

post.html

<html>
   <body>
      <h2>My new post dated $date</h2>
   </body>
</html>

当输入 URL https://127.0.0.1:8080/blog/ 时,它将生成以下输出:

Blog

当输入 URL https://127.0.0.1:8080/blog/post 时,它将生成以下输出:

Blog Post

TurboGears – ToscaWidgets 表单

任何 Web 应用程序最基本的一个方面是为用户呈现用户界面。HTML 提供了一个<form>标签,用于设计界面。可以适当地使用表单元素,例如文本输入、单选按钮、选择等。用户输入的数据以 Http 请求消息的形式通过 GET 或 POST 方法提交到服务器端脚本。

服务器端脚本必须从 http 请求数据中重新创建表单元素。因此,为此目的,表单元素必须定义两次 - 一次在 HTML 中,另一次在服务器端脚本中。

使用 HTML 表单的另一个缺点是难以(如果不是不可能的话)动态渲染表单元素。HTML 本身不提供任何方法来验证用户的输入。

ToscaWidgets2

TurboGears 依赖于 ToscaWidgets2,这是一个灵活的表单渲染和验证库。使用 ToscaWidgets,我们可以在 Python 脚本中定义表单字段,并使用 HTML 模板渲染它们。也可以对 tw2 字段应用验证。

ToscaWidgets 库是许多模块的集合。下面列出了一些重要的模块:

  • tw2.core - 它提供了核心功能。此模块中的 Widget 不适合最终用户使用。

  • tw2.forms - 这是一个基本的表单库。它包含用于字段、字段集和表单的小部件。

  • tw2.dynforms - 这包含动态表单功能。

  • tw2.sqla - 这是 SQLAlchemy 数据库的接口。

tw2.forms

它包含一个 Form 类,作为自定义表单的基础。有一个 TableForm 类,可用于在两列表中渲染字段。ListForm 在无序列表中呈现其字段。

序号 字段和描述
1

TextField

单行文本输入字段

2

TextArea

多行文本输入字段

3

CheckBox

显示带标签的可选中矩形框

4

CheckBoxList

复选框的多选组

5

RadioButton

用于选择/取消选择的切换按钮

6

RadioButtonList

一组互斥的单选按钮

7

PasswordField

类似于 Textfield,但输入键不会显示

8

CalendarDatePicker

允许用户选择日期

9

SubmitButton

提交表单的按钮

10

ImageButton

带图像的单击按钮

11

SingleSelectField

启用从列表中选择单个项目

12

MultipleSelectField

启用从列表中选择多个项目

13

FileField

用于上传文件的字段

14

EmailField

电子邮件输入字段

15

URLField

输入 URL 的字段

16

NumberField

数字微调框

17

RangeField

数字滑块

在下面的示例中,使用其中一些小部件构建了一个表单。虽然大多数这些小部件都在 tw2.forms 中定义,但 CalendarDateField 在 tw2.Dynforms 模块中定义。因此,这两个模块以及 tw2.core 在开头都被导入了:

import tw2.core as twc
import tw2.forms as twf
import tw2.dynforms as twd

ToscaWidgets 表单是从tw2.forms.form基类派生的类。所需的 Widget 放在 Layout 对象中。在此示例中,使用TableLayout。Widget 在两列表中呈现。第一列显示标题,第二列显示输入或选择字段。

使用以下构造函数创建 TextField 对象:

twf.TextField(size, value = None)

如果未提及,TextField 对象将采用默认大小,并且最初为空白。在声明 TextArea 对象时,可以提及行数和列数。

twf.TextArea("",rows = 5, cols = 30)

NumberField 对象是一个 TextField,它只能接受数字。在右侧边框上生成上下箭头以增加或减少其中的数字。初始值也可以作为构造函数中的参数指定。

twf.NumberField(value)

在 CalendarDatePicker 框的右侧,显示一个日历按钮。按下时,会弹出一个日期选择器。用户可以在框中手动键入日期或从日期选择器中选择。

twd.CalendarDatePicker()

EmailField 对象呈现一个 TextField,但其中的文本必须采用电子邮件格式。

EmailID = twf.EmailField()

以下表单还具有 RadioButtonList。此类的构造函数包含一个 List 对象作为 options 参数的值。将为每个选项呈现一个单选按钮。默认选择由 value 参数指定。

twf.RadioButtonList(options = ["option1","option2"],value = option1)

CheckBoxList 为列表中的每个选项呈现复选框。

twf.CheckBoxList(options = [option1, option2, option3])

下拉列表在此 ToscaWidgets 库中称为 SingleSelectfield。对应于 options 参数的 List 对象中的项目构成下拉列表。可见标题设置为 prompt_text 参数的值。

twf.SingleSelectField(prompt_text = 'text', options=['item1', 'item2', 'item3'])

默认情况下,表单会显示一个 Submit 按钮,其标题为“保存”。为了显示其他标题,请创建 SubmitButton 对象并将其指定为 value 参数。

twf.SubmitButton(value = 'Submit')

表单提交到一个 URL,该 URL 指定为表单的 action 参数的值。默认情况下,表单数据通过 http POST 方法提交。

action = 'URL'

在以下代码中,使用上述解释的小部件设计了一个名为 AdmissionForm 的表单。在 RootController 类之前将此代码添加到 root.py 中。

class AdmissionForm(twf.Form):
   class child(twf.TableLayout):
      NameOfStudent = twf.TextField(size = 20)
      AddressForCorrespondance = twf.TextArea("", rows = 5, cols = 30)
      PINCODE = twf.NumberField(value = 431602)
      DateOfBirth = twd.CalendarDatePicker()
      EmailID = twf.EmailField()
      Gender = twf.RadioButtonList(options = ["Male","Female"],value = 'Male')
      Subjects = twf.CheckBoxList(options = ['TurboGears', 'Flask', 'Django', 'Pyramid'])

      MediumOfInstruction = twf.SingleSelectField(prompt_text = 'choose',
         options = ['English', 'Hindi', 'Marathi', 'Telugu'])
			
      action = '/save_form'
      submit = twf.SubmitButton(value ='Submit')

现在将此代码保存为 templates 目录中的twform.html

<!DOCTYPE html>
<html xmlns = "http://www.w3.org/1999/xhtml"
   xmlns:py = "http://genshi.edgewall.org/"
   lang = "en">
   
   <head>
      <title>TurboGears Form Example</title>
   </head>
   
   <body>
      <div id = "tw form">
         ${form.display(value = dict(title = 'default title'))}
      </div>
   </body>
   
</html>

在 RootController 类(在 root.py 中)中,添加以下控制器函数:

@expose('hello.templates.twform')
def twform(self, *args, **kw):
   return dict(page = 'twform', form = MovieForm)

在 AdmissionForm 类中,我们有stipulated/save_form作为操作 URL。因此,在 RootController 中添加save_form()函数。

@expose()
def save_movie(self, **kw):
   return str(kw)

确保服务器正在运行(使用 gearbox serve)。在浏览器中输入https://127.0.0.1:8080/twform

Forms

按下提交按钮会将此数据发布到save_form() URL,该 URL 将以字典对象的形式显示表单数据。

{
   'EmailID': u'[email protected]', 
   'NameOfStudent': u'Malhar Lathkar', 
   'Gender': u'Male', 
   'PINCODE': u'431602', 
   'DateOfBirth': u'2015-12-29', 
   'Subjects': [u'TurboGears', u'Flask', u'Django'], 
   'MediumOfInstruction': u'', 
   'AddressForCorrespondance': u'Shivaji Nagar\r\nNanded\r\nMaharashtra'
}

TurboGears – 验证

一个好的表单小部件库应该具有输入验证功能。例如,用户应该被迫在必填字段中输入数据,或者验证电子邮件字段是否包含有效的电子邮件,而无需使用任何其他编程方法(如 JavaScript 函数)进行验证。

早期版本的 ToscaWidgets 表单库用于依赖于 FormEncode 模块以进行验证支持。ToscaWidgets2 现在在 tw2.core 模块中提供了内置的验证支持。但是,仍然可以使用 FormEncode 验证技术。

为了使 ToscaWidgets 表单服从验证,使用@validate 装饰器。

@validate(form, error_handler, validators)
  • 'form'是要验证的 ToscaWidgets 表单对象。

  • 'error-handler'是用于处理表单错误的控制器方法。

  • 'validators'是一个包含 FormEncode 验证器的字典对象。

验证器类型

tw2.core 模块包含一个验证器类,其他验证器从该类继承。也可以基于它设计自定义验证器。下面描述了一些重要的验证器:

LengthValidator - 检查值是否具有规定的长度。最小和最大限制由 min 和 max 参数定义。可以将长度低于和高于 min 和 max 的自定义消息指定为 tooshort 和 toolong 参数。

tw2.core.LengthValidator(min = minval, max = maxval, 
   msgs = { 'tooshort': (‘message for short length’), 
   'toolong': (‘message for long length)})

RangeValidator − 通常与 RangeField 一起使用。它用于验证数值字段的值是否在最小值和最大值限制范围内。可以自定义 tooshort 和 toolong 参数的消息。

tw2.core.RangeValidator(min = minval, max = maxval, 
   msgs = { 'tooshort': (‘message for short length’), 
   'toolong': (‘message for long length)})

IntValidator − 此类派生自 RangeValidator。它通常用于验证普通文本字段中的输入是否包含整数数据。可以设置最小值和最大值限制以及错误消息。此外,可以将非整数输入的错误消息指定为 'notint' 参数。

tw2.core.IntValidator(msgs = {‘notint’:’Must be Integer’})

OneOfValidator − 此验证器强制用户只能从列表中可用的选项中选择一个值。

tw2.core.OneOfValidator(values = [option1, option2,..], 
   msgs = {‘notinlist’:’Not in List’}}

DateValidator − 非常有用,可以确保用户输入的日期有效。日期格式(默认为 Y-M-D)和错误消息可自定义。还可以指定最小和最大日期限制。DateTimeValidator 也可用于验证 DateTime 类的对象。

tw2.core.DateValidator(msgs = {format = ’%Y-%m-%d’, 
   'baddatetime': ('baddate', ('Must follow date format $format_str'))}

EmailValidator − 根据有效的电子邮件地址验证用户输入。此类继承自更通用的 RegexValidator。

tw2.core.EmailValidator(msgs = {'badregex': ('bademail', 
   ('Must be a valid email address')) }

UrlValidator − 此类也继承自 RegexValidator。它验证用户输入是否为有效的 URL。

tw2.core.UrlValidator(msgs = {'badregex': ('badurl', ('Must be a valid URL’)) }

MatchValidator − 确认一个字段的值是否与另一个字段匹配。这在要求用户选择和确认密码字段的情况下特别有用。下面显示了 MatchValidator 的典型用法:

import tw2.core as twc
import tw2.forms as twf
  
  class AdmissionForm(twf.Form):
      class child(twf.TableLayout):
         validator = twc.MatchValidator('pw', 'pwconfirm')
         pw = twf.PasswordField()
         pwconfirm = twf.PasswordField()

还可以构建一个复合验证器,如果任何一个检查通过,则希望验证成功。在其他情况下,您可能希望只有在输入通过所有检查时,验证才成功。为此,tw2.core 提供了 Any 和 All 验证器,它们是可扩展的 CompoundValidator 的子类。

TurboGears – 闪存消息

TurboGears 提供了一个非常方便的消息系统,用于以非干扰的方式向用户通知信息。tg 模块中的 TGFlash 类提供对闪存消息的支持,这些消息存储在普通 cookie 中。此类支持在服务器端以及通过 JavaScript 在客户端获取闪存消息。

TGFlash 类的 render() 方法,当从 Python 本身使用时,可以从模板中调用以呈现闪存消息。如果在 JavaScript 中使用,它会提供一个 WebFlash 对象。它公开 payload()render() 方法来获取当前闪存消息并从 JavaScript 呈现它。

当使用 'quickstart' 创建 TurboGears 项目时,它有一个 Master.html 模板。它包含该闪存对象变量的定义。从控制器接收到的此闪存消息的内容替换了此模板中标记的占位符。

<py:with vars = "flash = tg.flash_obj.render('flash', use_js = False)">
   <div py:if = "flash" py:replace = "Markup(flash)" />
</py:with>

tg.flash_obj 是 WebFlash 对象,通过包含 master.html 模板,可以在任何呈现的模板内部使用它。此对象允许检索当前闪存消息并显示它。

闪存消息存储在 cookie 中(默认名称为 webflash),方法是使用 tg.flash() 方法。然后将消息和状态参数传递给它。

tg.flash('Message', 'status')

如果调用的 flash 方法执行重定向,则闪存将在重定向页面中可见。如果方法直接公开模板,则闪存将在模板本身中可见。

可以通过将 CSS 样式应用于状态代码来自定义闪存消息的外观。'quickstarted' 项目包含错误、警告、信息和确定状态代码,这些代码由样式表 public/css/style.css 自定义。还可以添加更多具有样式的状态代码。

#flash > .warning {
   color: #c09853;
   background-color: #fcf8e3;
   border-color: #fbeed5;
}

#flash > .ok {
   color: #468847;
   background-color: #dff0d8;
   border-color: #d6e9c6;
}

#flash > .error {
   color: #b94a48;
   background-color: #f2dede;
   border-color: #eed3d7;
}

#flash > .info {
   color: #3a87ad;
   background-color: #d9edf7;
   border-color: #bce8f1;
}

此外部样式表需要包含在模板中:

<link rel = "stylesheet" type = "text/css" media = "screen" 
   href = "${tg.url('/css/style.css')}" />

任何闪存消息支持的配置都可以通过为 TGFlash 对象的 configure() 方法设置参数或在 app_cfg.py(在 config 文件夹中)中设置参数来实现。可配置的参数为:

序号 参数 & 描述
1

flash.cookie_name

用于存储闪存消息的 cookie 的名称。默认为 webflash

2

flash.default_status

如果未指定,则为默认消息状态(默认为 ok)

3

flash.template

呈现时用作 闪存模板

4

flash.allow_html

打开/关闭 闪存消息中的转义,默认情况下不允许 HTML。

5

flash.js_call

从 JavaScript 显示闪存时将运行的 JavaScript 代码。默认为 webflash.render()

6

flash.js_template

用于替换闪存消息的完整 JavaScript 支持的 string.Template 实例。

  • pop_payload() − 函数 获取当前闪存消息、状态 和相关信息。获取闪存消息将删除 cookie。

  • render(container_id, use_js=True) − 在模板内呈现闪存消息或为其提供 Javascript 支持。

  • container_id 是显示消息的 DIV,而 use_js 在将闪存呈现为 HTML 或用于 JavaScript 使用之间切换。

  • status − 只获取当前闪存状态,获取闪存状态将删除 cookie。

  • message − 只获取当前闪存消息,获取闪存消息将删除 cookie。

如何创建简单的闪存消息?

在以下示例中,在根控制器类中提供了 flash() 方法。它调用一个 flash() 消息,该消息呈现给公开的模板 flash.html

from hello.lib.base import BaseController
from tg import expose, flash, redirect, request

class RootController(BaseController):
   @expose('hello.templates.flash')
   def flash(self, user = None):
      
      if user:
         flash(message = "Welcome "+user,status = "ok")
      else:
         flash(message = "Welcome Guest",status = "info")
      return {}

在 templates 文件夹中创建 flash.html 的代码如下

<html xmlns = "http://www.w3.org/1999/xhtml"
   xmlns:py = "http://genshi.edgewall.org/"
   xmlns:xi = "http://www.w3.org/2001/XInclude">

   <head>
      <title>TurboGears 2.3: Flash messages>/title>
      <link rel = "stylesheet" type = "text/css" media = "screen"
         href = "${tg.url('/css/style.css')}" />
			
      <py:with vars = "flash = tg.flash_obj.render('flash', use_js = False)">
         <div py:if = "flash" py:replace = "Markup(flash)" />
      </py:with>
		
   </head>

   <body>
      <h2>Hello TurboGears</h2>
   </body>
	
</html>

启动服务器并在浏览器中输入 https://127.0.0.1:8080/flash?user=MVL

Flash Message

将 URL 更改为 https://127.0.0.1:8080/flash 并查看闪存消息,其格式根据 style.css 中的定义有所不同。

Message

TurboGears – Cookie 和会话

通常需要保存与用户浏览器关联的简单浏览数据。会话是最常用的技术。会话表示不需要以更持久的方式(如磁盘文件或数据库)存储的数据。

但是,TurboGears 中的会话数据可以由文件系统、数据库或哈希 cookie 值支持。少量会话数据通常保存在 cookie 中,但对于大量会话数据,则使用 MemCache。

MemCache 是一个系统级守护进程。它提供对缓存数据的快速访问,并且具有极强的可扩展性。但是,它仅适用于安全服务器,因此必须由系统管理员维护和保护。

Beaker 在会话管理中的作用

TurboGears 使用 Beaker 进行会话管理。gearbox 快速启动的项目默认配置为使用哈希 cookie 存储会话数据。

每次客户端连接时,会话中间件(Beaker)都会使用在配置文件中定义的 cookie 名称检查 cookie。如果找不到 cookie,它将在浏览器中设置。在所有后续访问中,中间件将找到 cookie 并使用它。

为了启用会话管理,应通过以下导入语句将会话类合并到项目中:

from tg import session

在会话变量中保存数据:

session[‘key’] = value
session.save()

检索会话变量:

return session[‘key’]

请注意,您需要显式保存会话才能将您的键存储在该会话中。

会话对象的 delete() 方法将擦除所有用户会话:

session.delete()

尽管在任何给定的生产环境中删除所有用户会话并不常见,但您通常会在完成可用性或功能测试后执行此操作以进行清理。

下面是一个演示会话的简单示例。RootController 类有一个 setsession() 方法,该方法设置一个会话变量。

from hello.lib.base import BaseController
from tg import expose, session
class RootController(BaseController):
   
   @expose()
   def setsession(self):
      session['user'] = 'MVL'
      session.save()
      
      str = "<b>sessionVariable set to "+session['user'] 
      str = str+"<br><a href = '/getsession'>click here to retrieve</a></b>"
      return str
   
   @expose()
   def getsession(self):
      return "<b>value of session variable retrieved " +session['user'] +"</b>"

输入 https://127.0.0.1:8080/setsession

Session

浏览器中的链接指向 https://127.0.0.1:8080/getsession,它检索并显示会话变量:

Variable

TurboGears – 缓存

为了提高 Web 应用程序的性能,尤其是在涉及冗长操作时,会使用缓存技术。TurboGears 提供两种类型的缓存技术:

全页缓存

它在 HTTP 协议级别工作,通过让用户的浏览器或中间代理服务器(如 Squid)拦截请求并返回文件的缓存副本,从而避免对服务器的整个请求。

应用程序级缓存

这在应用程序服务器内工作,以缓存计算出的值,通常是复杂数据库查询的结果,以便将来的请求可以避免重新计算这些值。对于 Web 应用程序,应用程序级缓存提供了一种灵活的方式来缓存复杂查询的结果,以便给定控制器方法的总负载可以减少到几个用户特定或情况特定的查询,以及模板的渲染开销。

应用程序级缓存

如前所述,'quickstarted' TurboGears 项目配置为启用 Beaker 包以支持缓存。Beaker 支持以下用于缓存存储的后端:

  • memory − 用于每个进程存储。它非常快。

  • filesystem − 每个进程存储以及多进程。

  • DBM 数据库 − 每个进程、多进程,速度相当快。

  • SQLAlchemy 数据库 − 每个数据库服务器存储。与上述选项相比,速度较慢。

  • Memcached − 基于多服务器内存的缓存。

控制器缓存

对于快速控制器缓存,可以使用 cached() 装饰器。整个控制器主体根据请求的各种参数进行缓存。tg.decorators.cached() 装饰器的定义如下

tg.decorators.cached(key, expire, type, 
   query-args, cache_headers, invalidate_on_startup, cache_response)

参数的描述如下:

序号 参数 & 描述
1

key

指定用于生成缓存键的控制器参数。

2

expire

缓存过期前的时间(秒),默认为“永不过期”。

3

Type

dbm、memory、file、memcached 或 None。

4

cache_headers

指示响应头的标头名称元组。

5

invalidate_on_startup

如果为 True,则每次应用程序启动或重新启动时都会使缓存失效。

6

cache_response

响应是否应缓存,默认为 True。

以下是一个控制器缓存的示例:

@cached(expire = 100, type = 'memory')
@expose()
def simple(self):
   return "This is a cached controller!"

模板级缓存

如果 Genshi 模板引擎的内容没有更改,则它会从缓存中检索模板。此缓存的默认大小为 25。默认情况下,模板的自动重新加载为真。为了提高性能,可以在 app_cfg.py 中进行以下设置:

[app:main]
genshi.max_cache_size = 100
auto_reload_templates = false

要缓存模板,您只需从呈现缓存模板的控制器返回 tg_cache 选项即可。

tg_cache 是一个字典,它接受以下键:

  • key − 缓存键。默认值:None。

  • expire − 缓存必须保持活动状态的时间。默认值:永不过期

  • 类型 − memory, dbm, memcached。默认值:dbm。

以下示例说明了模板缓存 -

@expose(hello.templates.user')
def user(self, username):
   return dict(user = username, tg_cache = dict(key = user, expire = 900))

TurboGears – SQLAlchemy

虽然可以在 TurboGears 应用程序中使用 SQL 对任何关系数据库执行 CRUD 操作,但建议使用 SQLAlchemy,这是一个强大的 Python 工具包,是一个强大的对象关系映射器,它为应用程序开发人员提供了 SQL 的全部功能和灵活性。除了通过 SQLAlchemy 支持基于 SQL 的数据库外,TurboGears 还通过 Ming 支持 MongoDB 数据库。在本节中,将讨论 SQLAlchemy 的功能。

Sql

什么是 ORM(对象关系映射)?

大多数编程语言平台都是面向对象的。另一方面,RDBMS 服务器中的数据存储为表。对象关系映射是一种将对象参数映射到底层 RDBMS 表结构的技术。ORM API 提供执行 CRUD 操作的方法,而无需编写原始 SQL 语句。

Orm

当使用 gearbox 工具包中的“quickstart”命令创建 TurboGears 项目时,以下配置设置默认情况下启用 SQLAlchemy 支持 -

config['use_sqlalchemy'] = True
config['sqlalchemy.url'] = 'sqlite:///devdata.db'

“quickstarted”项目还在其中创建了一个 models 包。例如,“Hello”项目将拥有 Hello\hello\model。在此包中创建了以下文件 -

  • __init__.py − 这是设置数据库访问的地方。应用程序的模型对象在此模块中导入。它还具有 DBSession - 一个全局会话管理器,以及一个 DeclarativeBase,它是所有模型类的基类。

  • auth.py − 这是定义身份验证堆栈使用的模型的地方。其他数据库模型存储在此包中,作为单独的模块,并在 __init__.py 中添加。

TurboGears – 创建模型

让我们添加一个学生模型,它将在我们的sqlite数据库中设置一个学生表。

Hello\hello\model\student.py

from sqlalchemy import *
from sqlalchemy.orm import mapper, relation, relation, backref
from sqlalchemy import Table, ForeignKey, Column
from sqlalchemy.types import Integer, Unicode, DateTime

from hello.model import DeclarativeBase, metadata, DBSession
from datetime import datetime

class student(DeclarativeBase):
   __tablename__ = 'student'

   uid = Column(Integer, primary_key = True)
   name = Column(Unicode(20), nullable = False, default = '')
   city = Column(Unicode(20), nullable = False, default = '')
   address = Column(Unicode(100), nullable = False, default = '')
   pincode = Column(Unicode(10), nullable = False, default = '')

现在将此模型添加到init_model()函数中,该函数位于__init__.py内。此函数已包含其中的 auth 模型。在它下面添加我们的学生模型。

# Import your model modules here.
from hello.model.auth import User, Group, Permission
from hello.model.student import student

如果希望在设置模型时使用某些数据初始化表,请将其添加到 websetup 包中的 bootstrap.py 中。在bootstrap()函数中添加以下语句。

s1 = model.student()
s1.name = 'M.V.Lathkar'
s1.city = 'Nanded'
s1.address = 'Shivaji Nagar'
s1.pincode = '431602'

model.DBSession.add(s1)
model.DBSession.flush()
transaction.commit()

通过运行 gearbox 的 setup-app 命令初始化模型 -

gearbox setup-app

SQLAlchemy 的 Session 对象管理 ORM 对象的所有持久性操作。

TurboGears – CRUD 操作

以下会话方法执行 CRUD 操作 -

  • DBSession.add(模型对象) − 将记录插入映射表。

  • DBSession.delete(模型对象) − 从表中删除记录。

  • DBSession.query(模型).all() − 从表中检索所有记录(对应于 SELECT 查询)。

您可以通过使用 filter 属性将过滤器应用于检索到的记录集。例如,为了检索 students 表中 city = 'Hyderabad' 的记录,请使用以下语句 -

DBSession.query(model.student).filter_by(city = ’Hyderabad’).all()

我们现在将了解如何通过控制器 URL 与模型交互。

首先让我们设计一个 ToscaWidgets 表单来输入学生的数据

Hello\hello\controllers.studentform.py

import tw2.core as twc
import tw2.forms as twf

class StudentForm(twf.Form):
   class child(twf.TableLayout):
      name = twf.TextField(size = 20)
      city = twf.TextField()
      address = twf.TextArea("",rows = 5, cols = 30)
      pincode = twf.NumberField()

   action = '/save_record'
   submit = twf.SubmitButton(value = 'Submit')

在 RootController(Hello 应用程序的 root.py)中,添加以下函数映射“/add”URL -

from hello.controllers.studentform import StudentForm

class RootController(BaseController):
   @expose('hello.templates.studentform')
   def add(self, *args, **kw):
      return dict(page='studentform', form = StudentForm)

将以下 HTML 代码另存为模板文件夹中的studentform.html -

<!DOCTYPE html>
<html xmlns = "http://www.w3.org/1999/xhtml"
   xmlns:py = "http://genshi.edgewall.org/"
   lang = "en">
   
   <head>
      <title>Student Registration Form</title>
   </head>
   
   <body>
      <div id = "getting_started">
         ${form.display(value = dict(title = 'Enter data'))}
      </div>
   </body>

</html>

在启动服务器后,在浏览器中输入https://127.0.0.1:8080/add。以下学生信息表单将在浏览器中打开 -

Registration

上面表单设计为提交到'/save_record' URL。因此,需要在root.py中添加save_record()函数来公开它。来自 studentform 的数据由此函数作为dict()对象接收。它用于在学生模型底层的学生表中添加新记录。

@expose()
#@validate(form = AdmissionForm, error_handler = index1)

def save_record(self, **kw):
   newstudent = student(name = kw['name'],city = kw['city'],
      address = kw['address'], pincode = kw['pincode'])
   DBSession.add(newstudent)
   flash(message = "new entry added successfully")
   redirect("/listrec")

请注意,成功添加后,浏览器将重定向到'/listrec' URL。此 URL 由listrec() 函数公开。此函数选择学生表中的所有记录,并将它们以 dict 对象的形式发送到 studentlist.html 模板。此listrec()函数如下 -

@expose ("hello.templates.studentlist")
def listrec(self):
   entries = DBSession.query(student).all()
   return dict(entries = entries)

studentlist.html 模板使用 py:for 指令迭代 entries 字典对象。studentlist.html 模板如下 -

<html xmlns = "http://www.w3.org/1999/xhtml"
   xmlns:py = "http://genshi.edgewall.org/">
   
   <head>
      <link rel = "stylesheet" type = "text/css" media = "screen" 
         href = "${tg.url('/css/style.css')}" />
      <title>Welcome to TurboGears</title>
   </head>
   
   <body>
      <h1>Welcome to TurboGears</h1>
      
      <py:with vars = "flash = tg.flash_obj.render('flash', use_js = False)">
         <div py:if = "flash" py:replace = "Markup(flash)" />
      </py:with>
      
      <h2>Current Entries</h2>
      
      <table border = '1'>
         <thead>
            <tr>
               <th>Name</th>
               <th>City</th>
               <th>Address</th>
               <th>Pincode</th>
            </tr>
         </thead>
         
         <tbody>
            <py:for each = "entry in entries">
               <tr>
                  <td>${entry.name}</td>
                  <td>${entry.city}</td>
                  <td>${entry.address}</td>
                  <td>${entry.pincode}</td>
               </tr>
            </py:for>
         </tbody>
         
      </table>
   
   </body>
</html>

现在重新访问https://127.0.0.1:8080/add并在表单中输入数据。通过单击提交按钮,它将把浏览器带到 studentlist.html。它还会显示“新记录添加成功”消息。

Entries

TurboGears – 数据网格

ToscaWidgets 包含一个 DataGrid 控件,它提供了一种以表格形式快速呈现数据的方法。DataGrid 对象声明如下 -

from tw2.forms import DataGrid
student_grid = DataGrid(fields = [('Name', 'name'),('City', 'city'),
   ('Address','address'), ('PINCODE', 'pincode')])

现在,showgrid() 函数检索学生表中的所有记录并将数据公开到 grid.html 模板。首先是 showgrid() 函数的代码,然后是 grid.html 代码 -

showgrid()

@expose('hello.templates.grid')
def showgrid(self):
   data = DBSession.query(student).all()
   return dict(page = 'grid', grid = student_grid, data = data)

grid.html

<!DOCTYPE html>
<html xmlns = "http://www.w3.org/1999/xhtml"
   xmlns:py = "http://genshi.edgewall.org/"
   lang = "en">
   
   <head>
      <title>Student Registration Form</title>
   </head>
   
   <body>
      <div id = "getting_started">
         <div>${grid.display(value = data)}</div>
      </div>
   </body>

</html>

当在浏览器中输入https://127.0.0.1:8080/showlist URL 时,将显示以下表格数据 -

Form

TurboGears – 分页

TurboGears 提供了一个名为 paginate() 的便捷装饰器,用于将输出划分为页面。此装饰器与 expose() 装饰器组合使用。@Paginate() 装饰器将查询结果的字典对象作为参数。此外,每页记录数由 items_per_page 属性的值决定。确保将 paginate 函数从 tg.decorators 导入到您的代码中。

将 root.py 中的 listrec() 函数重写如下 -

from tg.decorators import paginate
class RootController(BaseController):
   @expose ("hello.templates.studentlist")
   @paginate("entries", items_per_page = 3)
	
   def listrec(self):
      entries = DBSession.query(student).all()
      return dict(entries = entries)

每页项目设置为三个。

在 studentlist.html 模板中,通过在 py:for 指令下方添加 tmpl_context.paginators.entries.pager() 来启用页面导航。此模板的代码应如下 -

<html xmlns = "http://www.w3.org/1999/xhtml"
   xmlns:py = "http://genshi.edgewall.org/">
   
   <head>
      <link rel = "stylesheet" type = "text/css" media = "screen" 
         href = "${tg.url('/css/style.css')}" />
      <title>Welcome to TurboGears</title>
   </head>
   
   <body>
      
      <h1>Welcome to TurboGears</h1>
		
      <py:with vars = "flash = tg.flash_obj.render('flash', use_js = False)">
         <div py:if = "flash" py:replace = "Markup(flash)" />
      </py:with>
      
      <h2>Current Entries</h2>
		
      <table border = '1'>
         <thead>
            <tr>
               <th>Name</th>
               <th>City</th>
               <th>Address</th>
               <th>Pincode</th>
            </tr>
         </thead>
         
         <tbody>
            <py:for each = "entry in entries">
               <tr>
                  <td>${entry.name}</td>
                  <td>${entry.city}</td>
                  <td>${entry.address}</td>
                  <td>${entry.pincode}</td>
               </tr>
            </py:for>
				
            <div>${tmpl_context.paginators.entries.pager()}</div>
         </tbody>
         
      </table>
   
   </body>

</html>

在浏览器中输入https://127.0.0.1:8080/listrec。显示表中第一页的记录。在此表的顶部,还可以看到指向页码的链接。

Record

如何为数据网格添加分页支持

也可以为数据网格添加分页支持。在以下示例中,分页数据网格设计为显示操作按钮。为了激活操作按钮,数据网格对象使用以下代码构建 -

student_grid = DataGrid(fields = [('Name', 'name'),('City', 'city'),
   ('Address','address'), ('PINCODE', 'pincode'),
   ('Action', lambda obj:genshi.Markup('<a
      href = "%s">Edit</a>' % url('/edit',
      params = dict(name = obj.name)))) ])

此处,操作按钮链接到数据网格中每行名称参数。

showgrid()函数重写如下 -

@expose('hello.templates.grid')
@paginate("data", items_per_page = 3)

def showgrid(self):
   data = DBSession.query(student).all()
   return dict(page = 'grid', grid = student_grid, data = data)

浏览器显示分页数据网格如下 -

Registration Form

通过单击第三行中的“编辑”按钮,它将重定向到以下 URLhttps://127.0.0.1:8080/edit?name=Rajesh+Patil

TurboGears – 管理员访问

TurboGears 提供了 tgext.admin 扩展,该扩展由 tgext.crud 和 sprox 提供支持。此 Sprox 是一个用于直接从数据库模式创建 Web 小部件的包。这可用于自动创建简单的管理页面,并且是为新快速启动的应用程序中的 /admin 页面提供支持的工具包。

默认情况下,管理员将为项目模型 __init__.py 中导入的所有模型提供自动生成的访问权限。

如何创建 TurboGears 管理员

默认的 TurboGears 管理员作为 AdminController 类的对象创建 -

from tgext.admin.controller import AdminController

class RootController(BaseController):
   admin = AdminController(model, DBSession, config_type = TGAdminConfig)

这将使用默认的 TurboGears 管理员配置为所有模型创建管理员。

通过管理器,在设置阶段创建了一个用户。现在,可以访问https://127.0.0.1:8080/admin处的 TurboGears 管理员。第一次访问此页面时,将要求进行身份验证。您可以简单地提供 setup-app 命令为我们创建的用户帐户的用户名和密码 -

Username: manager
Password: managepass

为了登录到快速启动的项目,请将以下函数添加到 RootController 类(controllers/root.py)。

from hello.lib.base import BaseController
from tg import expose, flash, redirect, request,url, lurl
from tg import redirect, validate
from hello import model
from hello.model import DBSession
from tgext.admin.tgadminconfig import BootstrapTGAdminConfig as TGAdminConfig
from tgext.admin.controller import AdminController
from tg.exceptions import HTTPFound

class RootController(BaseController):
   admin = AdminController(model, DBSession, config_type =  TGAdminConfig)
   
   @expose('hello.templates.index')
   def index(self):
      return dict(page = 'index')
   
   @expose('hello.templates.login')
   def login(self, came_from = lurl('/'), failure = None,    login = ''):
	
      if failure is not None:
         if failure == 'user-not-found':
            flash(_('User not found'), 'error')
         elif failure == 'invalid-password':
            flash(_('Invalid Password'), 'error')
			
      login_counter = request.environ.get('repoze.who.logins', 0)
		
      if failure is None and login_counter > 0:
         flash(_('Wrong credentials'), 'warning')
		 
      return dict(page = 'login', login_counter = str(login_counter), 
         came_from = came_from, login = login)
   @expose()
	
   def post_login(self, came_from = lurl('/')):
      if not request.identity:
         
         login_counter = request.environ.get('repoze.who.logins', 0) + 1
            redirect('/login', params = dict(came_from = came_from,
            __logins = login_counter))
		
         userid = request.identity['repoze.who.userid']
         flash(('Welcome back, %s!') % userid)
			
         return HTTPFound(location = came_from)

启动服务器并访问https://127.0.0.1:8080/login后,登录到“快速启动”应用程序,然后输入上面显示的管理器凭据。浏览器将显示如下所示的管理员页面 -

Admin pade

此页面显示在此应用程序中创建的所有模型。您可以单击任何模型以查看其中的条目列表 -

Student Listing

此数据网格顶部的“新建”按钮允许添加记录。同样,此数据网格操作列中还提供了用于编辑和删除记录的操作按钮。还显示了一个搜索框以有条件地选择记录。

DataGrid

TurboGears – 授权和身份验证

TurboGears 应用程序由 gearbox 工具包的 quickstart 和 setup-app 选项创建,默认情况下启用了授权和身份验证支持。auth.py 中声明的模型根据 bootstrap.py 中分配的值进行设置和初始化。

以下模型在 auth.py 中声明 -

用户模型

用户模型包含 tg_user 表的设计。此表由 repose.who 包使用。此 repose.who 包是一个功能强大且可扩展的 WSGI 应用程序身份验证库。用户模型的结构如下 -

class User(DeclarativeBase):

"""
   __tablename__ = 'tg_user'
   
   user_id = Column(Integer, autoincrement = True, primary_key=True)
   user_name = Column(Unicode(16), unique = True, nullable = False)
   email_address = Column(Unicode(255), unique = True,nullable=False)
                                             
   display_name = Column(Unicode(255))
   _password = Column('password', Unicode(128))
   created = Column(DateTime, default = datetime.now)

此组模型包含 tg_group 表的定义。其定义在 auth.py 中给出,如下所示 -

class Group(DeclarativeBase):
   __tablename__ = 'tg_group'
   
   group_id = Column(Integer, autoincrement = True,primary_key = True)
   group_name = Column(Unicode(16),unique = True,nullable = False)
   display_name = Column(Unicode(255))
   created = Column(DateTime, default = datetime.now)

还设置了另一个模型权限,其中包含权限定义。

class Permission(DeclarativeBase):
   __tablename__ = 'tg_permission'
   
   permission_id = Column(Integer,autoincrement = True,primary_key = True)
   permission_name = Column(Unicode(63), unique = True, nullable = False)
   description = Column(Unicode(255))

在设置模型时,以下数据添加到这些表中 -

u = model.User()
u.user_name = 'manager'
u.display_name = 'Example manager'
u.email_address = '[email protected]'
u.password = 'managepass'

model.DBSession.add(u)
g = model.Group()
g.group_name = 'managers'
g.display_name = 'Managers Group'
g.users.append(u)

model.DBSession.add(g)
p = model.Permission()
p.permission_name = 'manage'
p.description = 'This permission gives an administrative right'
p.groups.append(g)

model.DBSession.add(p)
u1 = model.User()
u1.user_name = 'editor'
u1.display_name = 'Example editor'
u1.email_address = '[email protected]'
u1.password = 'editpass'

model.DBSession.add(u1)

谓词模型

tg 包中的谓词模块包含谓词检查器的定义。谓词是用户能够访问请求的源必须满足的条件。这样的谓词或条件可以由多个谓词组成 - 这些称为复合谓词。操作控制器或控制器可能只有一个谓词,无论是单一的还是复合的。

如果用户未登录或没有适当的权限,则此谓词检查器将抛出 401(HTTP 未授权),repoze.who 中间件会捕获此错误以显示登录页面,允许用户登录,并在用户完成操作后将其重定向回正确的页面。

tg.predicates 模块中定义的不同条件或谓词为 -

序号 tg.predicates 模块和描述
1

All

检查是否满足所有指定的谓词

2

Any

检查是否满足指定的谓词中的至少一个

3

is_user

检查已认证用户的用户名是否为指定的用户

4

in_group

检查用户是否属于特定组。

5

in_all_groups

检查用户是否属于所有指定的组。

6

in_any_group

检查用户是否属于指定的组中的至少一个。

7

is_anonymous

检查当前用户是否为匿名用户。

8

has_permission

检查当前用户是否具有指定的权限。

9

has_all_permissions

检查当前用户是否已授予所有指定的权限。

10

has_any_permission

检查用户是否具有指定的权限中的至少一个。

例如,如果您有一个谓词,即授予属于客户组的用户访问权限,则可以使用以下内置谓词检查器 -

from tg.predicates import in_group
p in_group(‘customers’)

以下谓词检查器将授予“root”用户或任何具有“manage”权限的用户访问权限 -

from tg.predicates import Any, is_user, has_permission
p = Any(is_user('root'), has_permission('manage'), 
   sg = 'Only administrators can remove blog posts')

TurboGears - 使用 MongoDB

TurboGears 也支持 MongoDB 文档数据库。它使用 Ming,一个对象文档映射 API。Ming 的用法与 SQLAlchemy 非常相似。Ming 查询语言使得将基于 SQLAlchemy 的 TurboGears 项目移植到 Ming 成为可能。

什么是 PyMongo

PyMongo 是一个 Python 发行版,包含用于处理 MongoDB 的工具。Ming 扩展了 PyMongo,提供了以下功能:

  • 声明式模型
  • 模式验证和转换
  • 模式演变
  • 纯内存 MongoDB 实现
  • 工作单元
  • 标识映射
  • 一对多、多对一和多对多关系

首先,您需要下载并安装 MongoDB。最新的 MongoDB 发行版可以从 https://www.mongodb.org/downloads 下载

在 Windows 上,通过提供 -dbpath 选项启动 MongoDB 服务器:

C:\mongodb\bin>Mongod --dbpath d:\mongo

D:\mongo 文件夹被指定用于存储 MongoDB 数据库。服务器开始监听 https://127.0.0.1:27017。现在,要启动 MongoDB shell,请使用以下命令:

C:\mongodb\bin>Mongo

我们的 MongoDB 环境现在已准备就绪。

现在使用 -ming 选项创建一个 TurboGears 项目:

gearbox quickstart --ming Hello

此快速启动项目将提供一个身份验证和授权层,类似于 SQLAlchemy 版本中提供的层。此应用程序现在将尝试连接到本地机器上端口 27017 上的服务器。项目文件夹中的 development.ini 文件包含以下设置:

ming.url = mongodb://127.0.0.1:27017/
ming.db = hello

使用以下命令设置项目:

Python setup.py develop

项目文件夹包含 models 子文件夹,其中包含以下文件:

  • __init__.py - 这是设置数据库访问的地方。您的集合应该导入到此模块中。例如,我们将在此包中添加 student 集合。

  • session.py - 此文件定义数据库连接的会话。每次必须声明MappedClass以指定会话来执行查询时,都需要导入此文件。

  • auth.py - 如果您在快速入门中启用了身份验证和授权,则会创建此文件。它定义了三个集合repoze.who,它进一步依赖于:User、Group 和 Permission。

定义您的集合

默认情况下,TurboGears 以声明方式配置 Ming。这类似于 SQLAlchemy 的声明式支持,需要每个模型都继承自 MappedClass 类。

MappedClass 要求在内部提供一个 __mongometa__ 子类,该子类进一步提供有关存储文档的集合名称以及用于存储文档的会话的详细信息。

MappedClass 还包含文档中字段的定义。Ming 的 odm 模块包含不同类型字段属性的定义:

  • FieldProperty
  • ForeignIdProperty
  • RelationProperty

ming.schema 模块定义了以下数据类型:

  • ming.schema.Anything
  • ming.schema.Array
  • ming.schema.Binary
  • ming.schema.Bool
  • ming.schema.Float
  • ming.schema.Int
  • ming.schema.ObjectId
  • ming.schema.Scalar
  • ming.schema.String

要在此模型中添加 student 集合,请将以下代码另存为 hello/models 文件夹中的 student.py。

Hello\models\student.py

from ming import schema
from ming.odm import MappedClass
from ming.odm import FieldProperty, ForeignIdProperty
from hello.model import DBSession
   
Class student(MappedClass):
   class __mongometa__:
      session = DBSession
      name = 'student'
      
   _id = FieldProperty(schema.ObjectId)
   name = FieldProperty(schema.String(required = True))
   city = FieldProperty(schema.String(if_missing = ''))
   address = FieldProperty(schema.String(if_missing = ''))
   pincode = FieldProperty(schema.String(if_missing = ''))

最后,将此模型包含在 hello\models\__init__.py 中

# Import your model modules here.
from hello.model.auth import User, Group, Permission
from hello.model.student import student

要设置这些模型,请运行以下 gearbox 命令:

Gearbox setup-app

使用以下 gearbox 命令启动服务器:

Gearbox serve –reload –debug

打开此应用程序的主页 (https://127.0.0.1:8080/) 并使用管理员凭据登录。此应用程序的管理员页面将显示设置的模型列表。(以管理员身份登录,密码 managepass)

Homepage Application

集合的创建也可以在 MongoDB Web 界面以及 MongoDB shell 中验证。

ODMSession 用于使用以下函数执行多个数据库操作:

  • model.query.find()
  • model.query.find_and_modify()
  • model.remove()
  • model.update()
  • model.flush()

设计 ToscoWidget 表单

我们现在将设计一个 ToscoWidget 表单来输入学生数据并将其添加到 student 模型的基础表中。

以下是创建 studentform.py 的代码:

Hello\controllers\studentform.py

import tw2.core as twc
import tw2.forms as twf
   
class StudentForm(twf.Form):

   class child(twf.TableLayout):
      name = twf.TextField(size = 20)
      city = twf.TextField()
      address = twf.TextArea("",rows = 5, cols = 30)
      pincode = twf.NumberField()
		
   action = '/save_record'
   submit = twf.SubmitButton(value = 'Submit')     

在应用程序的 Rootcontroller '/add' URL 中调用 add() 函数,该函数将在浏览器中打开上面设计的表单。然后,其提交按钮将调用 save_record() 函数。它检索表单数据并将其保存在 student 表中,并将应用程序重定向到 '/listrec' URL,该 URL 公开了 studentlist 模板。

此活动的 root.py 如下所示:

Hello/controllers/root.py

from hello.lib.base import BaseController
from tg import expose, flash, redirect, request,url, lurl
from tg import redirect, validate
from hello import model
from hello.model import DBSession
from hello.model.student import student
   
from hello.controllers.studentform import StudentForm
   
class RootController(BaseController):
   @expose()
   def index(self):
      return "<h1>Hello World</h1>"
         
   @expose ("hello.templates.studentlist")
   def listrec(self):
      entries = student.query.find()
      return dict(entries = entries)
               
   @expose('hello.templates.studentform')
   def add(self, *args, **kw):
      return dict(page = 'studentform', form = StudentForm)
         
   @expose()
   def save_record(self, **kw):
      newstudent = student(name = kw['name'],city = kw['city'],
         address = kw['address'], pincode = kw['pincode'])
      DBSession.flush()
      flash(message = "new entry added successfully")
      redirect("/listrec")

以下模板在 templates 文件夹中创建:

Hello\templates\studentform.html

<!DOCTYPE html>
<html xmlns = "http://www.w3.org/1999/xhtml" 
   xmlns:py = "http://genshi.edgewall.org/" lang = "en">
	
   <head>
      <title>Student Registration Form</title>
   </head>
	

   <body>
      <div id = "getting_started">
         ${form.display(value = dict(title = 'Enter data'))}
      </div>
   </body>
	
</html>

Hello\templates\studentlist.html

<html xmlns = "http://www.w3.org/1999/xhtml" 
   xmlns:py = "http://genshi.edgewall.org/">

   <head>
      <link rel = "stylesheet" type = "text/css" media = "screen" 
         href = ${tg.url('/css/style.css')}" />
      <title>Welcome to TurboGears</title>
   </head>
   
   <body>
      <h1>Welcome to TurboGears</h1>
		
      <py:with vars = "flash = tg.flash_obj.render('flash', use_js = False)">
         <div py:if = "flash" py:replace = "Markup(flash)" />
      </py:with>
      
      <h2>Current Entries</h2>
		
      <table border = '1'>
         <thead>
            <tr>
               <th>Name</th>
               <th>City</th>
               <th>Address</th>
               <th>Pincode</th>
            </tr>
         </thead>
         
         <tbody>
            <py:for each = "entry in entries">
               <tr>
                  <td>${entry.name}</td>
                  <td>${entry.city}</td>
                  <td>${entry.address}</td>
                  <td>${entry.pincode}</td>
               </tr>
            </py:for>
         </tbody>
      </table>
		
   </body>
	
</html>

重新启动服务器并在浏览器中输入 https://127.0.0.1:8080/add

Student Registration Form

每次添加数据并按下提交按钮时,将显示当前条目的列表。

New Entry Output

TurboGears - 脚手架

Gearbox 工具包包含 scaffold 命令,该命令对于快速创建 TurboGears 应用程序的新组件非常有用。由 gearbox 的 quickstart 命令生成的应用程序在 model 文件夹(model.py.template)、templates 文件夹(template.html.template)和 controllers 文件夹(controller.py.template)中有一个骨架模板。这些“.template”文件用作创建应用程序新脚手架的基础。

例如,为了创建一个名为 mymodel 的新模型,只需运行以下命令:

gearbox scaffold model mymodel

此命令将在 model/mymodel.py 中生成定义了 newmodel 类的文件。

# -*- coding: utf-8 -*-
"""Mymodel model module."""
from sqlalchemy import *
from sqlalchemy import Table, ForeignKey, Column
from sqlalchemy.types import Integer, Unicode, DateTime, LargeBinary
from sqlalchemy.orm import relationship, backref
from hello.model import DeclarativeBase, metadata, DBSession

class Mymodel(DeclarativeBase):
   __tablename__ = 'mymodels'
   
   uid = Column(Integer, primary_key = True)
   data = Column(Unicode(255), nullable = False)
   
   user_id = Column(Integer, ForeignKey('tg_user.user_id'), index = True)
   user = relationship('User', uselist = False,
      backref = backref('mymodels',cascade = 'all, delete-orphan'))
   __all__ = ['Mymodel']

用户现在可以根据自己的需要修改表结构,然后将其导入model/__init__.py以使模型在应用程序中可用。

为了创建一个模型、一个处理它的控制器类和一个索引页面,所有这三个组件都可以通过以下命令同时创建。

gearbox scaffold model controller template mymodel

此命令将在 controllers\mymodel.py 中生成已正确定义 MymodelController 类的文件。

# -*- coding: utf-8 -*-
"""Mymodel controller module"""

from tg import expose, redirect, validate, flash, url
# from tg.i18n import ugettext as _
# from tg import predicates

from hello.lib.base import BaseController
# from hello.model import DBSession

class MymodelController(BaseController):
   # Uncomment this line if your controller requires an authenticated user
   # allow_only = predicates.not_anonymous()
   
   @expose('hello.templates.mymodel')
   def index(self, **kw):
      return dict(page = 'mymodel-index')

要开始使用此控制器,请将其安装到您的应用程序 RootController 中以定义 MymodelController 的实例。在 controllers\root.py 中添加以下行:

From hello.controller.mymodel import MymodelController

class RootController(BaseController): mymodel = MymodelController()

一个模板脚手架 templates\mymodel.html 也将在 templates 文件夹中创建。它将充当“/mymodel”URL 的索引页面。

在 templates 文件夹中生成的mymodel.html 文件如下所示:

<html xmlns = "http://www.w3.org/1999/xhtml"
   xmlns:py = "http://genshi.edgewall.org/"
   xmlns:xi = "http://www.w3.org/2001/XInclude">
	
   <xi:include href = "master.html" />
	
   <head>
      <title>Mymodel</title>
   </head>
	
   <body>
      <div class = "row">
         <div class = "col-md-12">
            <h2>Mymodel</h2>
            <p>Template page for Mymodel</p>
         </div>
      </div>
   </body>
	
</html>

TurboGears - 钩子

在 TurboGears 中有三种方法可以在现有应用程序内插入行为。

  • 钩子 - 这是一种机制,通过该机制可以定义一个事件,并在事件发出时通知已注册的侦听器。

  • 控制器包装器 - 它位于 TurboGears 和控制器之间,因此可以像装饰器一样扩展控制器。因此,它可以附加到任何第三方控制器应用程序。

  • 应用程序包装器 - 它类似于任何 WSGI 中间件,但仅在 TurboGears 上下文中工作。

本章将讨论如何在现有应用程序中使用钩子。

钩子

钩子是在应用程序的配置文件app_cfg.py中注册的事件。然后,任何控制器都通过事件装饰器挂接到这些事件。

TurboGears 中定义了以下钩子:

序号 钩子 & 描述
1

Startup()

仅限于应用程序范围,在应用程序启动时调用。

2

shutdown()

仅限于应用程序范围,在应用程序退出时调用。

3

configure_new_app

应用程序配置器创建了新的应用程序。

4

before_config(app)

仅限于应用程序范围,在创建应用程序后立即调用,但在设置选项和中间件之前调用。

5

after_config(app)

仅限于应用程序范围,在完成所有设置后调用。

6

before_validate

在执行验证之前调用。

7

before_call

在验证后调用,在调用实际的控制器方法之前调用。

8

before_render

在呈现控制器模板之前调用,输出是控制器返回值。

9

after_render

在完成呈现控制器模板后调用。

注册钩子

为了注册钩子,请在app_cfg.py中创建函数,然后使用以下代码注册它们:

tg.hooks.register(hookane, function, controller)

在以下代码中,on_startup、on_shutdown 和 before_render 钩子在 app_cfg.py 中注册。

def on_startup():
   print 'hello, startup world'
   
def on_shutdown():
   print 'hello, shutdown world'
   
def before_render(remainder, params, output):
   print 'system wide before render'
   
# ... (base_config init code)
tg.hooks.register('startup', on_startup)
tg.hooks.register('shutdown', on_shutdown)
tg.hooks.register('before_render', before_render)

before_render 钩子与 Rootcontroller 中的控制器函数一起注册。在 controllers\root.py 中添加以下代码。

from tg.decorators import before_render

class RootController(BaseController):
   @expose('hello.templates.index')
   @before_render(before_render_cb)
	
   def index(self, *args, **kw):
      return dict(page = 'index')

当应用程序启动时,启动消息会显示在控制台中。

hello, startup world
Starting Standard HTTP server on http://127.0.0.1:8080

当在浏览器中输入“/”URL 时,控制台会显示与 before_render 钩子相对应的一条消息。

system wide before render
Going to render {'page': 'index'}

TurboGears - 编写扩展

TurboGears 扩展由tgext.* 包标识。Gearbox 工具包提供 tgext 命令来创建示例扩展。例如:

gearbox tgext -n myextension

此命令的其他可选参数为:

  • --author - 包作者的姓名。

  • --email - 包作者的电子邮件。

  • --licence - 用于包的许可证。默认为 MIT。

  • --description - 包的描述。

  • --keywords - 包关键字(默认:turbogears2.extension)。

这将创建一个 tgext.myextension 目录,其中包含一个简单的示例扩展。

运行目录内的 setup.py

Python setup.py install

tgext/myextension文件夹内的_init_.py文件包含:

  • Plugme 函数 - 这是扩展的入口点。

  • SetupExtension 类 - 扩展初始化在此处进行。

  • On_startup 函数 - 类内部是一个在类内部的 __call__ 函数上注册的钩子。

tgext\myextension\__init__.py的简要版本。

from tg import config
from tg import hooks
from tg.configuration import milestones

import logging
log = logging.getLogger('tgext.myextension')

def plugme(configurator, options = None):
   if options is None:
      options = {}
   log.info('Setting up tgext.myextension extension...')
   milestones.config_ready.register(SetupExtension(configurator))
   
   return dict(appid='tgext.myextension')
	
class SetupExtension(object):
   def __init__(self, configurator):
      self.configurator = configurator
      
   def __call__(self):
      log.info('>>> Public files path is %s' % config['paths']['static_files'])
      hooks.register('startup', self.on_startup)
      
   def echo_wrapper_factory(handler, config):
      def echo_wrapper(controller, environ, context):
         log.info('Serving: %s' % context.request.path)
         return handler(controller, environ, context)
      return echo_wrapper
      
   self.configurator.register_wrapper(echo_wrapper_factory)
   
   def on_startup(self):
      log.info('+ Application Running!')

安装扩展后,通过在应用程序的app_cfg.py配置文件中进行以下添加来启用它。

from tgext.myextension import plugme

plugme(base_config)

如果我们使用 gearbox server 命令启动服务器,则可以通过以下方式在控制台中查看新注册扩展的通知:

14:29:13,250 INFO [tgext.myextension] Setting up tgext.myextension extension...
14:29:13,453 INFO [tgext.myextension] >>> Public files path is c:\tghello\hello\hello\public
14:29:13,453 INFO [tgext.myextension] + Application Running!

Starting Standard HTTP server on http://127.0.0.1:8080

TurboGears - 可插拔应用程序

如果您的扩展需要公开模型和控制器,您可能需要查看可插拔应用程序,这些应用程序旨在创建可重用的 Turbogears 应用程序,这些应用程序可以插入其他应用程序以扩展其功能。

使用以下 gearbox 命令创建可插拔应用程序:

gearbox quickstart-pluggable plugtest

这些可插拔应用程序可以定义自己的:

  • 控制器 - 应用被清除时,将会自动挂载。

  • 模型 - 在已挂载的应用内外都可用。

  • 辅助函数 - 可以自动在应用模板的“H”对象中暴露。

  • 引导程序 - 调用 setup-app 时将执行。

  • 静态文件 - 可在其自己的私有路径下访问。

安装此 plugtest 应用并通过在app_cfg.py中进行以下修改来挂载它。

from tgext.pluggable import plug
plug(base_config, plugtest)

TurboGears – RESTful 应用

REST 代表表述性状态转移。REST 是基于 Web 标准的架构,使用 HTTP 协议进行数据通信。它围绕资源展开,其中每个组件都是一个资源,并且可以使用 HTTP 标准方法通过通用接口访问资源。REST 最初由Roy Fielding 于 2000 年提出。

什么是 RestController

TurboGears 中的 RestController 提供了一种机制来访问请求的方法,而不仅仅是 URL。标准的 HTTP 动词包括:GET、POST、PUT 和 DELETE。RestController 支持这些方法,并且还添加了一些 URL 分发快捷方式,使用户更轻松地以表单和列表的形式显示数据。

为了解释 RESTful 如何与 TurboGears 协同工作,我们将定义一个简单的 Web 服务,该服务公开学生列表。

下面给出学生模型的代码:

model\student.py

# -* - coding: utf-8 -*-
from sqlalchemy import *

from sqlalchemy.orm import mapper, relation, relation, backref
from sqlalchemy import Table, ForeignKey, Column
from sqlalchemy.types import Integer, Unicode, DateTime
from hello.model import DeclarativeBase, metadata, DBSession
from datetime import datetime

class student(DeclarativeBase):
   __tablename__ = 'student'
   
   uid = Column(Integer, primary_key = True)
   name = Column(Unicode(20), nullable = False, default = '')
   city = Column(Unicode(20), nullable = False, default = '')
   address = Column(Unicode(100), nullable = False, default = '')
   pincode = Column(Unicode(10), nullable = False, default = '')

现在,基于 RestController 创建一个控制器,并提供一个视图函数以 JSON 格式列出学生列表。

Controllers\student.py

from tg import RestController
from tg import expose
from hello import model
from hello.model import DBSession
from hello.model.student import student
from tg.decorators import with_trailing_slash

class StudentController(RestController):
   @expose('json')
   def get_all(self):
      students = DBSession.query(student).all()
      return dict(students=students)

通过在root.py中加入以下几行,将此 StudentController 挂载到应用的 RootController 中:

from hello.controllers.student import StudentController

class RootController(BaseController):

   students = StudentController()

访问https://127.0.0.1:8080/students 将以 JSON 格式提供学生列表。

我们使用 post 方法来定义如何将学生保存到数据库中。每当使用 POST 请求访问https://127.0.0.1:8080/student URL 时,都会调用此方法:

@expose('json')
def post(self, name, city, address, pincode):
   newstudent = student(name = name, city = city, address = address, pincode = pincode)
   DBSession.add(newstudent)
   DBSession.flush()
   return dict(student = newstudent)

使用get_one()方法,我们可以将数据库中的一项显示给用户:

@expose('json')
def get_one(self, movie_id):
   newstudent = DBSession.query(student).get(uid)
   return dict(movie = movie)

PUT 是使用 REST 更新现有记录的方法:

@expose('json')
def put(self, name = name, city = city, address =  address, pincode = pincode, **kw):
   newstudent = DBSession.query(student).get(name)
   newstudent.name = name
   newstudent.city = city
   newstudent.address = address
   newstudent.pincode = pincode
   return dict(student = newstudent)

delete 的核心功能附加到 post_delete 方法中。在这里,我们实际上从数据库中删除记录,然后重定向回列表页面:

@expose('json')
def post_delete(self, uid, **kw):
   newstudent = DBSession.query(student).get(uid)
   DBSession.delete(newstudent)
   return dict(movie = newstudent.uid)

TurboGears - 部署

要从开发环境切换到成熟的生产环境,需要将应用部署到真实的 Web 服务器上。根据您的情况,有多种可用于部署 TurboGears Web 应用的选项。

带有 mod_wsgi 的 Apache

mod_wsgi 是由 Graham Dumpleton 开发的 Apache 模块。它允许使用 Apache Web 服务器提供 WSGI 程序。

首先,如果尚未安装,请为您的平台安装 Apache 2.X。安装 Apache 后,安装 mod_wsgi。在服务器上创建并激活 Python 虚拟环境,并在其中安装 TurboGears。

在应用目录中安装您的应用,然后创建一个名为app.wsgi的脚本。

如下配置 Apache 安装:

<VirtualHost *:80>
   ServerName www.site1.com
   WSGIProcessGroup www.site1.com
   WSGIDaemonProcess www.site1.com user = <username> 
      group = www-data threads = 4 python-path = <pythonpath>
   WSGIScriptAlias myapp/app.wsgi
	
   #Serve static files directly without TurboGears
   Alias /images
   Alias /css
   Alias /js
   CustomLog
   ErrorLog
</VirtualHost>

重启 Apache

在浏览器中输入http://www.site1.com/以访问应用。

Circus 和 Chaussette 下的 TurboGears

Circus 是一个进程和套接字管理器。它可以用于监控和控制进程和套接字。当与 Chaussette WSGI 服务器配对时,它可以成为部署应用和管理应用所需任何相关进程的强大工具。

TurboGears - GoogleAppEngine

从以下 URL 安装适用于 Python 的 Google AppEngine SDK:https://cloud.google.coms

在您的系统上安装 Google AppEngine。然后打开 Google 开发者控制台并使用您的 Google 帐户登录:https://console.developers.google.com/start

创建一个名为mytgapp的新项目:

Mytgapp Project

使用 Google AppEngine 启动器,创建一个名为mytgapp的新应用。

New Application

Google App Engine Launcher

将在指定的目录中创建以下文件:

  • app.yaml
  • favicon.ico
  • index.yaml
  • main.py

默认情况下,创建的应用依赖于 Webapp2 框架。要删除此依赖项,请编辑 app.yaml 文件并删除以下部分:

libraries:
   - name: webapp2
   version: "2.5.2"

在名为 mytgapp 的目录中创建一个临时虚拟环境,并安装 TurboGears。在其中创建一个 TurboGears 应用。现在,我们可以继续编辑由 AppEngine 启动以运行我们的应用的main.py文件,并在其中实际编写 TurboGears 应用。

main.py中添加以下内容:

import os
import site
site.addsitedir(os.path.join(os.path.dirname(__file__), 'packages'))
from tg import expose, TGController, AppConfig

class RootController(TGController):
   @expose()
   def index(self):
      return "<h1>Hello World</h1>"
		
config = AppConfig(minimal = True, root_controller = RootController())
app = config.make_wsgi_app()

现在从 AppEngine 启动器运行应用,然后单击“浏览”按钮以查看应用在本地主机上是否正常工作。

我们已经在开发者控制台中创建了一个名为 mytgapp 的项目。现在,单击启动器中的“部署”按钮。部署过程完成后,访问http://mytgapp.appspot.com/以在线查看我们的应用。

mytgapp appspot
广告