快速参观Pyramid

Pyramid让你从小处开始,大处结束。这个 Quick Tour Pyramid是为那些想要评估Pyramid的人设计的,不管你是刚接触到PythonWeb框架的人,还是急于成为专业人士的人。对于每个主题的更详细的处理,请给出 Pyramid快速教程 尝试一下。

如果希望剪切和粘贴此教程中的示例代码,可以浏览位于 Pyramid repository in the directory "docs/quick_tour" <https://github.com/Pylons/pyramid/> . 如果您已经下载了源代码,您将在同一位置找到tour。

安装

一旦您有了标准的Python环境设置,开始使用Pyramid就轻而易举了。不幸的是,在Python中,“标准”并不是那么简单。对于这个快速旅行,它意味着 PythonvenvpipSetuptools .

为了节省一点输入时间,并确保使用安装在虚拟环境中的模块、脚本和包,我们还将设置一个环境变量。

例如,对于Linux上的python 3:

# set an environment variable to where you want your virtual environment
$ export VENV=~/env
# create the virtual environment
$ python3 -m venv $VENV
# install pyramid
$ $VENV/bin/pip install pyramid
# or for a specific released version
$ $VENV/bin/pip install "pyramid==2.0.2"

对于Windows:

# set an environment variable to where you want your virtual environment
c:\> set VENV=c:\env
# create the virtual environment
c:\> python -m venv %VENV%
# install pyramid
c:\> %VENV%\Scripts\pip install pyramid
# or for a specific released version
c:\> %VENV%\Scripts\pip install "pyramid==2.0.2"

从2.0版开始,Pyramid仅在Python3上运行。为了简单起见,剩下的示例将只显示unix命令。

你好世界

微框架研究表明,学习最好从一个非常小的第一步开始。下面是Pyramid中的一个小应用程序:

 1from wsgiref.simple_server import make_server
 2from pyramid.config import Configurator
 3from pyramid.response import Response
 4
 5
 6def hello_world(request):
 7    return Response('Hello World!')
 8
 9
10if __name__ == '__main__':
11    with Configurator() as config:
12        config.add_route('hello', '/')
13        config.add_view(hello_world, route_name='hello')
14        app = config.make_wsgi_app()
15    server = make_server('0.0.0.0', 6543, app)
16    server.serve_forever()

这个简单的例子很容易运行。将此保存为 app.py 运行它:

$ $VENV/bin/python ./app.py

接下来在浏览器中打开http://localhost:6543/,您将看到 Hello World! 消息。

不熟悉python web编程?如果是这样的话,模块中的一些行将进行价值解释:

  1. Lines 6-7 . 实现生成 response .

  2. Line 10 . if __name__ == '__main__': 是python的说法“从命令行运行时从这里开始”。

  3. Lines 11-13 . 使用Pyramid's configurator 在一个 context manager 连接 view 特定URL的代码 route .

  4. Lines 14-16 . 发表一篇文章 WSGI 使用HTTP服务器的应用程序。

如本例所示, configurator 在金字塔发展中起着核心作用。从松散耦合的部分通过 应用程序配置 是金字塔中的一个中心思想,我们将定期在此重访 快速旅游 .

处理Web请求和响应

为Web开发意味着处理Web请求。由于这是Web应用程序的关键部分,因此Web开发人员需要一套功能强大、成熟的Web请求软件。

Pyramid一直很好地适应了现有的python web开发世界(虚拟环境、打包、cookiecutters,第一个使用python 3的公司之一,等等)。Pyramid变成了著名的 WebOb 用于请求和响应处理的python库。在上面的例子中,Pyramid手 hello_worldrequest 那就是 based on WebOb .

让我们看看请求和响应的一些功能:

def hello_world(request):
    # Some parameters from a request such as /?name=lisa
    url = request.url
    name = request.params.get('name', 'No Name Provided')

    body = 'URL %s with name: %s' % (url, name)
    return Response(
        content_type="text/plain",
        body=body
    )

在这个Pyramid视图中,我们从 request.url . 如果您访问了http://localhost:6543/?name=alice在浏览器中,名称包含在响应的正文中:

URL http://localhost:6543/?name=alice with name: alice

最后,我们设置响应的内容类型,并返回响应。

意见

对于上面的示例, hello_world 功能是一个“视图”。在Pyramid视图中,是接受Web请求和返回响应的主要方式。

到目前为止,我们的示例将所有内容放在一个文件中:

  • 视图功能

  • 它在配置程序中的注册

  • 将其映射到URL的路由

  • wsgi应用程序启动程序

让我们将视图移到它们自己的视图中 views.py 模块和更改 app.py 要扫描该模块,请查找设置视图的装饰器。

首先我们修订了 app.py

 1from wsgiref.simple_server import make_server
 2from pyramid.config import Configurator
 3
 4if __name__ == '__main__':
 5    with Configurator() as config:
 6        config.add_route('home', '/')
 7        config.add_route('hello', '/howdy')
 8        config.add_route('redirect', '/goto')
 9        config.add_route('exception', '/problem')
10        config.scan('views')
11        app = config.make_wsgi_app()
12    server = make_server('0.0.0.0', 6543, app)
13    server.serve_forever()

我们添加了更多的路由,但也删除了视图代码。我们的视图及其注册(通过装饰器)现在位于一个模块中 views.py ,通过扫描 config.scan('views') .

我们现在有一个 views.py 重点处理请求和响应的模块:

 1from html import escape
 2
 3from pyramid.httpexceptions import HTTPFound
 4from pyramid.response import Response
 5from pyramid.view import view_config
 6
 7
 8# First view, available at http://localhost:6543/
 9@view_config(route_name='home')
10def home_view(request):
11    return Response('<p>Visit <a href="/howdy?name=lisa">hello</a></p>')
12
13
14# /howdy?name=alice which links to the next view
15@view_config(route_name='hello')
16def hello_view(request):
17    name = request.params.get('name', 'No Name')
18    body = '<p>Hi %s, this <a href="/goto">redirects</a></p>'
19    # Python html.escape to prevent Cross-Site Scripting (XSS) [CWE 79]
20    return Response(body % escape(name))
21
22
23# /goto which issues HTTP redirect to the last view
24@view_config(route_name='redirect')
25def redirect_view(request):
26    return HTTPFound(location="/problem")
27
28
29# /problem which causes a site error
30@view_config(route_name='exception')
31def exception_view(request):
32    raise Exception()

我们有四种观点,每种观点都是相互引导的。如果从http://localhost:6543/开始,将得到一个响应,其中包含指向下一个视图的链接。这个 hello_view (网址为 /howdy )链接到 redirect_view ,将发出重定向到最终视图。

早些时候我们看到 config.add_view 作为配置视图的一种方法。本节介绍 @view_config . Pyramid的配置支持 imperative configuration ,比如 config.add_view 在上一个示例中。您也可以使用 declarative configuration 其中一条 Python decorator 放置在视图上方的行上。这两种方法都会产生相同的最终配置,因此通常只是一个简单的品味问题。

路由

编写Web应用程序通常意味着复杂的URL设计。我们刚刚看到一些Pyramid的请求和意见。让我们看看有助于路由的功能。

上面我们看到了将URL路由到Pyramid中视图的基础知识:

  • 项目的“设置”代码注册了一个路由名称,以便在匹配部分URL时使用。

  • 在其他地方,视图被配置为调用该路由名称。

备注

为什么要这样做两次?其他的PythonWeb框架允许您创建一个路由,并在一个步骤中将其与视图关联。如中所示 路由需要相对排序 ,多个路由可能匹配相同的URL模式。Pyramid并没有提供帮助猜测的方法,而是让您在排序时更加明确。Pyramid还提供了避免这个问题的设施。

如果我们希望URL的一部分在我的视图中作为数据可用怎么办?我们可以使用此路由声明,例如:

6        config.add_route('hello', '/howdy/{first}/{last}')

使用此,URL如 /howdy/amy/smith 将分配 amyfirstsmithlast . 然后我们可以在我们的视图中使用这些数据:

5@view_config(route_name='hello')
6def hello_world(request):
7    body = '<h1>Hi %(first)s %(last)s!</h1>' % request.matchdict
8    return Response(body)

request.matchdict 包含URL中与路由声明中的“替换模式”(大括号)匹配的值。然后可以在视图中使用此信息。

模板法

哎哟。我们一直在创造自己的 Response 并用HTML填充响应体。通常不会在Python中直接嵌入HTML字符串,而是使用模板语言。

Pyramid并不要求特定的数据库系统、表单库等。它鼓励可替换性。这同样适用于模板化,这是幸运的:开发人员对模板语言有很强的看法。也就是说,塔架项目正式支持变色龙、jinja2和Mako的捆绑。在这一步中,让我们使用变色龙。

让我们添加 pyramid_chameleon Pyramid add-on 使变色龙成为 renderer 在我们的Pyramid应用程序中:

$VENV/bin/pip install pyramid_chameleon

安装包后,我们可以将模板绑定包含到配置中 app.py

6        config.add_route('hello', '/howdy/{name}')
7        config.include('pyramid_chameleon')
8        config.scan('views')

现在让我们改变我们的 views.py 文件:

1from pyramid.view import view_config
2
3
4@view_config(route_name='hello', renderer='hello_world.pt')
5def hello_world(request):
6    return dict(name=request.matchdict['name'])

啊,看起来好多了。我们有一个关注Python代码的视图。我们的 @view_config decorator指定 renderer 指向我们的模板文件。然后,我们的视图只返回数据,这些数据随后被提供给我们的模板。 hello_world.pt

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Quick Glance</title>
</head>
<body>
<h1>Hello ${name}</h1>
</body>
</html>

因为我们的观点回来了 dict(name=request.matchdict['name']) 我们可以使用 name 作为模板中的变量,通过 ${{name}} .

jinja2模板

我们只是说Pyramid不喜欢一种模板语言而不是另一种。是时候证明了。Jinja2是一个流行的模板系统,仿照Django的模板。让我们添加 pyramid_jinja2 Pyramid add-on 使Jinja2成为 renderer 在我们的Pyramid应用中:

$VENV/bin/pip install pyramid_jinja2

安装包后,我们可以将模板绑定包含到配置中:

6        config.add_route('hello', '/howdy/{name}')
7        config.include('pyramid_jinja2')
8        config.scan('views')

我们视图中唯一的变化是将渲染器指向 .jinja2 文件:

4@view_config(route_name='hello', renderer='hello_world.jinja2')
5def hello_world(request):
6    return dict(name=request.matchdict['name'])

我们的jinja2模板与之前的模板非常相似:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Hello World</title>
</head>
<body>
<h1>Hello {{ name }}!</h1>
</body>
</html>

Pyramid的模板附加组件在应用程序中注册了一种新的渲染器。渲染器注册映射到不同类型的文件扩展名。在这种情况下,将扩展名从 .pt.jinja2 通过 pyramid_jinja2 渲染器。

Static Assets

当然,网络不仅仅是标记。您需要Static Assets:css、js和images。让我们将Web应用程序指向一个目录,从中Pyramid将提供一些Static Assets。首先,我们让我们再次调用 configurator in app.py

6        config.add_route('hello', '/howdy/{name}')
7        config.add_static_view(name='static', path='static')
8        config.include('pyramid_jinja2')

这会告诉我们的wsgi应用程序将http://localhost:6543/static/下的请求映射到 static 我们的python模块旁边的目录。

接下来,创建一个名为``static`` 的目录,并将“app.css 放在其中

body {
    margin: 2em;
    font-family: sans-serif;
}

我们现在要做的就是在 <head> 我们的Jinja2模板, hello_world.jinja2

4    <title>Hello World</title>
5    <link rel="stylesheet" href="/static/app.css"/>
6</head>

此链接假定我们的CSS位于以 /static/ . 如果网站后来被移到 /somesite/static/ ?或者可能是Web开发人员更改了磁盘上的排列?Pyramid提供了一个帮助器,允许在URL生成上实现灵活性:

4    <title>Hello World</title>
5    <link rel="stylesheet" href="{{ request.static_url('__main__:static/app.css') }}"/>
6</head>

通过使用 request.static_url 要生成Static Assets的完整URL,请确保与配置保持同步,并在以后获得重构灵活性。

回到JSON

现代Web应用程序不仅仅是呈现的HTML。动态页面现在使用JavaScript通过请求服务器数据作为JSON来更新浏览器中的UI。Pyramid通过JSON渲染器支持这一点:

 9@view_config(route_name='hello_json', renderer='json')
10def hello_json(request):
11    return [1, 2, 3]

这连接了一个通过JSON返回一些数据的视图。 renderer 它调用Python的JSON支持将数据序列化为JSON,并设置相应的HTTP头。

我们还需要添加一条路线到 app.py 以便我们的应用程序知道如何响应 hello.json .

6        config.add_route('hello', '/howdy/{name}')
7        config.add_route('hello_json', 'hello.json')
8        config.add_static_view(name='static', path='static')

视图类

到目前为止,我们的观点是简单的、独立的功能。很多时候你的观点是相关的。它们可能有不同的方法来查看或处理相同的数据,或者它们可能是一个处理多个操作的RESTAPI。将这些分组为 view class 有意义,实现以下目标。

  • 组视图

  • 集中一些重复的默认值

  • 分享一些州和帮助者

下面显示了一个“Hello World”示例,其中包含三个操作:查看表单、保存更改或按 views.py

 7# One route, at /howdy/amy, so don't repeat on each @view_config
 8@view_defaults(route_name='hello')
 9class HelloWorldViews:
10    def __init__(self, request):
11        self.request = request
12        # Our templates can now say {{ view.name }}
13        self.name = request.matchdict['name']
14
15    # Retrieving /howdy/amy the first time
16    @view_config(renderer='hello.jinja2')
17    def hello_view(self):
18        return dict()
19
20    # Posting to /howdy/amy via the "Edit" submit button
21    @view_config(request_param='form.edit', renderer='edit.jinja2')
22    def edit_view(self):
23        print('Edited')
24        return dict()
25
26    # Posting to /howdy/amy via the "Delete" submit button
27    @view_config(request_param='form.delete', renderer='delete.jinja2')
28    def delete_view(self):
29        print('Deleted')
30        return dict()

如您所见,这三个视图在逻辑上分组在一起。明确地:

  • 当您转到 /howdy/amy . 此URL映射到 hello 我们使用可选的 @view_defaults .

  • 当表单数据包含具有 form.edit ,例如单击 <input type="submit" name="form.edit" value="Save"> . 此规则在中指定 @view_config 为了这个观点。

  • 单击按钮时返回第三个视图,如 <input type="submit" name="form.delete" value="Delete"> .

只需要一条路线,在视图类顶部的一个位置中声明。此外,转让 name 是在 __init__ 功能。然后我们的模板可以使用 {{{{ view.name }}}} .

Pyramid视图类与内置谓词和自定义谓词相结合,提供了更多的功能:

  • 所有与功能视图相同的视图配置参数

  • 基于请求中的信息或数据(如 request_paramrequest_methodacceptheaderxhrcontainment ,和自定义谓词。

参见

参见: View ClassesMore View Classes 在快速教程中, 将可调用视图定义为类查看和路由谓词 .

使用CookieCutters快速启动项目

到目前为止,我们已经完成了 Quick Tour 作为单个python文件。没有python包,没有结构。不过,大多数Pyramid项目并不是这样发展的。

为了简化启动过程,塔架项目提供了一个 cookiecutter 从项目模板生成一个示例金字塔项目。这个厨师将安装金字塔和它的依赖性以及。

首先,您需要安装cookiecutter。

$VENV/bin/pip install cookiecutter

让我们用cookiecutter pyramid-cookiecutter-starter 要在当前目录中创建一个starter pyramid项目,请按照下面所示的提示输入以下命令的值。

$VENV/bin/cookiecutter gh:Pylons/pyramid-cookiecutter-starter --checkout main

If prompted for the first item, accept the default yes 按回车键。

You've cloned ~/.cookiecutters/pyramid-cookiecutter-starter before.
Is it okay to delete and re-clone it? [yes]: yes
project_name [Pyramid Scaffold]: hello_world
repo_name [hello_world]: hello_world
Select template_language:
1 - jinja2
2 - chameleon
3 - mako
Choose from 1, 2, 3 [1]: 1
Select backend:
1 - none
2 - sqlalchemy
3 - zodb
Choose from 1, 2, 3 [1]: 1

然后我们运行以下命令。

# Change directory into your newly created project.
cd hello_world
# Create a new virtual environment...
python3 -m venv env
# ...where we upgrade packaging tools...
env/bin/pip install --upgrade pip setuptools
# ...and into which we install our project and its testing requirements.
env/bin/pip install -e ".[testing]"
# Reset our environment variable for a new virtual environment.
export VENV=~/hello_world/env

我们正朝着一个功能齐全的Pyramid项目前进,为Python标准(打包)和Pyramid配置提供了适当的设置。这包括运行应用程序的新方法:

$VENV/bin/pserve development.ini

让我们来看一看 pserve 更深入的配置。

应用程序运行方式 pserve

在CookiCutter之前,我们的项目在代码中混合了许多操作细节。为什么我的主代码应该关心我想要的HTTP服务器和运行的端口号?

pserve 是Pyramid的应用程序运行程序,将操作细节与代码分开。当你安装Pyramid时,一个叫做 pserve 是写给你的 bin 目录。这个程序是一个可执行的python模块。它非常小,通过进口获得大部分大脑。

你可以跑 pserve 具有 --help 看看它的一些选择。这样做表明你可以问 pserve 要查看开发文件并在更改时重新加载服务器,请执行以下操作:

$VENV/bin/pserve development.ini --reload

这个 pserve 命令有许多其他选项和操作。但是,大多数工作都来自于项目的连接,正如您提供给的配置文件中所表达的那样。 pserve . 让我们看看这个配置文件。

参见

参见: 这是什么 pserve 事情

配置 .ini 文件夹

早些时候 Quick Tour 我们第一次见到Pyramid的配置系统。在这一点上,我们用Python代码进行了所有配置。例如,在python代码中,为HTTP服务器选择的端口号就在那里。我们的cookiecutter已经把这个决定和更多的 development.ini 文件:

###
# app configuration
# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
###

[app:main]
use = egg:hello_world

pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = en
pyramid.includes =
    pyramid_debugtoolbar

# By default, the toolbar only appears for clients from IP addresses
# '127.0.0.1' and '::1'.
# debugtoolbar.hosts = 127.0.0.1 ::1

###
# wsgi server configuration
###

[server:main]
use = egg:waitress#main
listen = localhost:6543

###
# logging configuration
# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
###

[loggers]
keys = root, hello_world

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = INFO
handlers = console

[logger_hello_world]
level = DEBUG
handlers =
qualname = hello_world

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s

让我们快速高层次地看看。首先 .ini 文件分为以下部分:

  • [app:main] 配置我们的wsgi应用程序

  • [server:main] 保留我们的wsgi服务器设置

  • 之后的各个部分将配置我们的python日志记录系统

在这种配置中,我们有一些决定:

  1. WSGI应用程序: 我们的wsgi应用程序在哪个包中? use = egg:hello_world 在应用程序部分告诉配置要加载的应用程序。

  2. 通过自动重新加载模板更容易开发: 在开发模式下,编辑jinja2模板时不需要重新启动服务器。 pyramid.reload_templates = true 设置此策略,这在生产中可能有所不同。

  3. Web服务器的选择: use = egg:waitress#main 告诉 pserve 使用 waitress 服务器。

  4. 接口: listen = localhost:6543 告诉 waitress 在端口6543上侦听IPv4和IPv6的所有接口。

另外, development.ini 由这个cookiecutter生成,连接了python的标准日志记录。例如,我们现在将在控制台中看到每个请求的日志,以及追溯信息。

更容易开发 debugtoolbar

在介绍基础知识的同时,我们还想展示如何在开发和调试中提高生产力。例如,我们刚刚讨论了模板重新加载,之前我们展示了 --reload 用于重新加载应用程序。

pyramid_debugtoolbar 是一个流行的Pyramid加载项,它使您的浏览器中有几个工具可用。将它添加到项目中说明了配置的几个要点。

我们的cookiecutter pyramid-cookiecutter-starter 已将我们的包配置为包含附加组件 pyramid_debugtoolbar 在其 setup.py

11requires = [
12    'plaster_pastedeploy',
13    'pyramid',
14    'pyramid_jinja2',
15    'pyramid_debugtoolbar',
16    'waitress',
17]

它是在您以前运行时安装的:

$VENV/bin/pip install -e ".[testing]"

这个 pyramid_debugtoolbar 包是一个Pyramid附加组件,这意味着我们需要将它的配置包含到我们的Web应用程序中。cookiecutter已经为我们处理了 development.ini 使用 pyramid.includes 设施:

14pyramid.includes =
15    pyramid_debugtoolbar

现在,您将在浏览器窗口的右侧看到一个Pyramid徽标,单击后,将打开一个新窗口,提供调试信息。如果您的Web应用程序生成错误,您将在屏幕上看到一个很好的回溯。如果要禁用此工具栏,则无需更改代码:可以将其从 pyramid.includes 在相关 .ini 配置文件。

单元和功能测试以及 pytest

Yikes!我们走了这么远,还没有讨论测试。这一点尤其令人震惊,因为Pyramid在发布之前就对全面的测试覆盖做出了深刻的承诺。

我们的 pyramid-cookiecutter-starter 已生成Cookiecuter conftest.pytest_functional.py ,以及 test_views.py 中的模块。 tests 包中包含两个单元测试和两个功能测试。它还配置了 setup.py 对于测试要求,请执行以下操作: pytest 作为试跑者, WebTest 用于运行视图测试,并且 pytest-cov 向我们大喊未经测试的代码的工具:

19tests_require = [
20    'WebTest',
21    'pytest',
22    'pytest-cov',
23]
43    extras_require={
44        'testing': tests_require,
45    },

我们在运行命令时已经安装了测试需求 $VENV/bin/pip install -e ".[testing]" . 我们现在可以运行所有测试:

$VENV/bin/pytest --cov --cov-report=term-missing

这将产生以下输出。

=========================== test session starts ===========================
platform darwin -- Python 3.9.0, pytest-6.2.1, py-1.10.0, pluggy-0.13.1
rootdir: /<somepath>/hello_world, configfile: pytest.ini, testpaths: hello_world, tests
plugins: cov-2.10.1
collected 4 items

tests/test_functional.py ..                                          [ 50%]
tests/test_views.py ..                                               [100%]

---------- coverage: platform darwin, python 3.9.0-final-0 -----------
Name                            Stmts   Miss  Cover   Missing
-------------------------------------------------------------
hello_world/__init__.py             7      0   100%
hello_world/routes.py               3      0   100%
hello_world/views/__init__.py       0      0   100%
hello_world/views/default.py        4      0   100%
hello_world/views/notfound.py       5      0   100%
-------------------------------------------------------------
TOTAL                              19      0   100%

======================== 4 passed in 0.65 seconds =========================

我们的测试通过了,它的覆盖范围是完整的。我们的测试是什么样子的?

1def test_root(testapp):
2    res = testapp.get('/', status=200)
3    assert b'Pyramid' in res.body
4
5def test_notfound(testapp):
6    res = testapp.get('/badurl', status=404)
7    assert res.status_code == 404
 1from hello_world.views.default import my_view
 2from hello_world.views.notfound import notfound_view
 3
 4
 5def test_my_view(app_request):
 6    info = my_view(app_request)
 7    assert app_request.response.status_int == 200
 8    assert info['project'] == 'hello_world'
 9
10def test_notfound_view(app_request):
11    info = notfound_view(app_request)
12    assert app_request.response.status_int == 404
13    assert info == {}

Pyramid为测试编写提供助手,我们在测试设置和拆卸中使用它。我们的视图测试导入视图,发出一个虚拟请求,并查看视图是否返回我们期望的结果。我们的功能测试验证了从请求到web根目录的响应主体是否包含我们所期望的内容,以及向发出请求所需的响应代码 /badurl 结果在 404 .

Logging

了解我们的Web应用程序中发生了什么是很重要的。在开发中,我们可能需要收集一些输出。在生产中,我们可能需要检测其他人使用该站点时的情况。我们需要 Logging .

幸运的是,Pyramid使用普通的python方法进行日志记录。这个 development.ini 项目的文件有许多行,它们将日志配置为一些合理的默认值。然后,您会看到Pyramid发送的消息(例如,当一个新请求出现时)。

也许您想在代码中记录消息?在Python模块中,导入并设置中的日志记录 views/default.py

3import logging
4log = logging.getLogger(__name__)

现在,您可以在代码中记录消息:

7def my_view(request):
8    log.debug('Some Message')

这个日志 Some Message 在一 DEBUG 在您的 development.ini . 是什么控制的?配置文件中的以下重点部分:

34[loggers]
35keys = root, hello_world
36
37[handlers]
38keys = console
39
40[formatters]
41keys = generic
42
43[logger_root]
44level = INFO
45handlers = console
46
47[logger_hello_world]
48level = DEBUG
49handlers =
50qualname = hello_world

我们的应用程序,一个名为 hello_world ,设置为记录器,并配置为在 DEBUG 或更高级别。当您访问http://localhost:6543时,您的控制台现在将显示:

2016-12-25 03:03:57,059 DEBUG [hello_world.views:8][waitress] Some Message

参见

参见: Quick Tutorial Logging登录 .

会议

当人们使用您的Web应用程序时,他们经常执行需要保存半永久数据的任务。例如,购物车。这叫A session .

Pyramid对会话有基本的内置支持。第三方软件包,如 pyramid_redis_sessions 提供更丰富的会话支持。或者您可以创建自己的自定义会话引擎。让我们看看 built-in sessioning support . 在我们 __init__.py 我们首先导入所需的会话类型:

1from pyramid.config import Configurator
2from pyramid.session import SignedCookieSessionFactory

警告

如会话文档中所述,此示例实现不适用于具有安全含义的设置。

现在制作一个“工厂”并将其传递给 configuratorsession_factory 论点:

 9        config.include('.routes')
10        my_session_factory = SignedCookieSessionFactory('itsaseekreet')
11        config.set_session_factory(my_session_factory)
12        config.scan()

皮拉米德 request 对象现在有一个 session 可以在视图代码中使用的属性 views/default.py

 7def my_view(request):
 8    log.debug('Some Message')
 9    session = request.session
10    if 'counter' in session:
11        session['counter'] += 1
12    else:
13        session['counter'] = 0
14    return {'project': 'hello_world'}

我们需要更新我们的jinja2模板 templates/mytemplate.jinja2 要在会话中显示计数器增量:

4<div class="content">
5  <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Starter project</span></h1>
6  <p class="lead">Welcome to <span class="font-normal">{{project}}</span>, a&nbsp;Pyramid application generated&nbsp;by<br><span class="font-normal">Cookiecutter</span>.</p>
7  <p>Counter: {{ request.session.counter }}</p>
8</div>

数据库

Web应用程序意味着数据。数据意味着数据库。通常是SQL数据库。SQL数据库通常意味着一个“ORM”(对象关系映射器),在Python中,ORM通常会带来巨大的质量 SQLAlchemy 这是一个python包,可以大大简化与数据库的工作。

Pyramid和sqlacalchemy是好朋友。包括cookiecutter!

cd ~
env/bin/cookiecutter gh:Pylons/pyramid-cookiecutter-starter --checkout main

If prompted for the first item, accept the default yes 按回车键。

You've cloned ~/.cookiecutters/pyramid-cookiecutter-starter before.
Is it okay to delete and re-clone it? [yes]: yes
project_name [Pyramid Scaffold]: sqla_demo
repo_name [sqla_demo]: sqla_demo
Select template_language:
1 - jinja2
2 - chameleon
3 - mako
Choose from 1, 2, 3 [1]: 1
Select backend:
1 - none
2 - sqlalchemy
3 - zodb
Choose from 1, 2, 3 [1]: 2

然后我们像以前一样运行以下命令。

# Change directory into your newly created project.
cd sqla_demo
# Create a new virtual environment...
python3 -m venv env
# ...where we upgrade packaging tools...
env/bin/pip install --upgrade pip setuptools
# ...and into which we install our project and its testing requirements.
env/bin/pip install -e ".[testing]"
# Reset our environment variable for a new virtual environment.
export VENV=~/sqla_demo/env

我们现在有了一个工作示例SQLAlchemy应用程序,并安装了所有依赖项。Alembic提供了一个从现有数据库迁移模型的示例和升级方法。让我们生成第一个修订版。

$VENV/bin/alembic -c development.ini revision --autogenerate -m "init"

现在让我们升级数据库模式。

$VENV/bin/alembic -c development.ini upgrade head

示例项目还提供了一个控制台脚本,用于将数据加载到SQLite数据库中。运行它,然后启动应用程序:

$VENV/bin/initialize_sqla_demo_db development.ini
$VENV/bin/pserve development.ini

ORM简化了数据库结构到编程语言的映射。SQLAlchemy对此映射使用“模型”。CookiCutter生成了一个示例模型:

11class MyModel(Base):
12    __tablename__ = 'models'
13    id = Column(Integer, primary_key=True)
14    name = Column(Text)
15    value = Column(Integer)

视图代码在Web请求和系统其余部分之间进行逻辑中介,然后借助sqlacalchemy,可以轻松获取数据:

 8@view_config(route_name='home', renderer='sqla_demo:templates/mytemplate.jinja2')
 9def my_view(request):
10    try:
11        query = request.dbsession.query(models.MyModel)
12        one = query.filter(models.MyModel.name == 'one').one()
13    except SQLAlchemyError:
14        return Response(db_err_msg, content_type='text/plain', status=500)
15    return {'one': one, 'project': 'sqla_demo'}

形式

开发人员对Web表单有很多意见,因此有许多用于Python的表单库。Pyramid不直接捆绑表单库,但是 变形 是一个流行的形式选择,连同它的相关 * colander * 模式系统。

例如,假设我们需要一个编辑wiki页面的表单。表单上应该有两个字段,一个是必需的标题,另一个是正文的富文本编辑器。使用deform,我们可以将其表示为一个colander模式:

class WikiPage(colander.MappingSchema):
    title = colander.SchemaNode(colander.String())
    body = colander.SchemaNode(
        colander.String(),
        widget=deform.widget.RichTextWidget()
    )

有了这个功能,我们就可以呈现表单的HTML,也许可以使用现有页面的表单数据:

form = self.wiki_form.render()

我们要处理表单提交、验证和保存:

# Get the form data that was posted
controls = self.request.POST.items()
try:
    # Validate and either raise a validation error
    # or return deserialized data from widgets
    appstruct = wiki_form.validate(controls)
except deform.ValidationFailure as e:
    # Bail out and render form with errors
    return dict(title=title, page=page, form=e.render())

# Change the content and redirect to the view
page['title'] = appstruct['title']
page['body'] = appstruct['body']

deform和colander为表单、小部件、模式和验证提供了非常灵活的组合。变形的最新版本还包括 retail mode 用于获取自定义窗体上的变形特征。

deform使用来自Twitter引导程序的有吸引力的CSS,以及更强大的选择、复选框、日期和时间小部件。

参见

参见: Quick Tutorial FormsDeformColander .

结论

这个 Quick Tour 覆盖了很多。我们在Pyramid中引入了一长串概念,其中许多概念在Pyramid开发人员文档中进行了更全面的扩展。