在比较不同 Python Web 框架的四部分系列第一篇文章中,我解释了如何在 Flask Web 框架中创建一个待办事项列表 Web 应用程序。在第二篇文章中,我将使用 Pyramid Web 框架完成相同的任务。未来的文章将着眼于 Tornado 和 Django;随着我的深入,我将探讨它们之间更多的差异。
安装、启动和进行配置
Pyramid 自称为“从小处着手、宏图大展、始终如一的框架”,它与 Flask 非常相似,启动和运行它几乎不需要任何努力。事实上,在构建此应用程序时,您会认出许多相同的模式。然而,两者之间的主要区别在于 Pyramid 自带了几个有用的实用程序,我将在稍后介绍。
要开始使用,请创建一个虚拟环境并安装软件包。
$ mkdir pyramid_todo
$ cd pyramid_todo
$ pipenv install --python 3.6
$ pipenv shell
(pyramid-someHash) $ pipenv install pyramid
与 Flask 一样,创建一个 setup.py
文件来使您构建的应用程序成为易于安装的 Python 发行版是很明智的。
# setup.py
from setuptools import setup, find_packages
requires = [
'pyramid',
'paster_pastedeploy',
'pyramid-ipython',
'waitress'
]
setup(
name='pyramid_todo',
version='0.0',
description='A To-Do List build with Pyramid',
author='<Your name>',
author_email='<Your email>',
keywords='web pyramid pylons',
packages=find_packages(),
include_package_data=True,
install_requires=requires,
entry_points={
'paste.app_factory': [
'main = todo:main',
]
}
)
末尾附近的 entry_points
部分设置了应用程序的入口点,供其他服务使用。这允许 plaster_pastedeploy
包访问应用程序中的 main
函数,以构建应用程序对象并提供服务。(稍后我将回到这一点。)
当您安装 pyramid
时,您还获得了一些 Pyramid 特定的 shell 命令;需要注意的主要命令是 pserve
和 pshell
。pserve
将接受一个指定为参数的 INI 风格的配置文件,并在本地提供应用程序服务。pshell
也将接受一个配置文件作为参数,但它不会提供应用程序服务,而是打开一个了解应用程序及其内部配置的 Python shell。
配置文件非常重要,因此值得仔细研究。Pyramid 可以从环境变量或配置文件中获取其配置。为了避免对内容位置造成太多混淆,在本教程中,您将在配置文件中编写大部分配置,只有少数几个敏感的配置参数设置在虚拟环境中。
创建一个名为 config.ini
的文件
[app:main]
use = egg:todo
pyramid.default_locale_name = en
[server:main]
use = egg:waitress#main
listen = localhost:6543
这说明了几件事
- 实际的应用程序将来自环境中安装的
todo
包中的main
函数 - 要提供此应用程序的服务,请使用环境中安装的
waitress
包,并在 localhost 端口 6543 上提供服务
在开发中提供应用程序服务和工作时,设置日志记录以查看发生了什么是很有帮助的。以下配置将处理应用程序的日志记录
# continuing on...
[loggers]
keys = root, todo
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = INFO
handlers = console
[logger_todo]
level = DEBUG
handlers =
qualname = todo
[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
简而言之,此配置要求将与应用程序相关的所有内容记录到控制台。如果您想要更少的输出,请将日志级别设置为 WARN
,以便仅在出现问题时才触发消息。
由于 Pyramid 旨在用于不断增长的应用程序,因此请规划一个可以支持这种增长的文件结构。当然,Web 应用程序可以以您想要的任何方式构建。一般来说,您想要涵盖的概念块将包含
- 模型(Models),用于包含处理数据表示的代码和逻辑
- 视图(Views),用于与请求-响应周期相关的代码和逻辑
- 路由(Routes),用于访问应用程序功能的路径
- 脚本(Scripts),用于可能在应用程序本身的配置或管理中使用的任何代码
鉴于以上内容,文件结构可以如下所示
setup.py
config.ini
todo/
__init__.py
models.py
routes.py
views.py
scripts/
与 Flask 的 app
对象非常相似,Pyramid 也有自己的中央配置。它来自其 config
模块,被称为 Configurator
对象。此对象将处理从路由配置到指向模型和视图存在位置的所有事情。所有这些都在名为 todo
的内部目录中的 __init__.py
文件中完成。
# todo/__init__.py
from pyramid.config import Configurator
def main(global_config, **settings):
"""Returns a Pyramid WSGI application."""
config = Configurator(settings=settings)
config.scan()
return config.make_wsgi_app()
main
函数查找来自您的环境的一些全局配置,以及您在运行应用程序时提供的特定配置文件中的任何设置。它获取这些设置并使用它们来构建 Configurator
对象的实例,该对象(就所有意图和目的而言)是您的应用程序的工厂。最后,config.scan()
查找您想要附加到应用程序的任何标记为 Pyramid 视图的视图。
哇,配置内容真多。
使用路由和视图
既然已经完成了一部分配置,您就可以开始向应用程序添加功能了。功能以外部客户端可以访问的 URL 路由的形式出现,然后映射到 Python 可以运行的函数。
对于 Pyramid,所有功能都必须以某种方式添加到 Configurator
中。例如,假设您想要构建与您使用 Flask 构建的相同的简单 hello_world
视图,并映射到 /
路由。使用 Pyramid,您可以使用 .add_route()
方法向 Configurator
注册 /
路由。此方法将您要添加的路由的名称以及必须匹配才能访问该路由的实际模式作为参数。对于这种情况,将以下内容添加到您的 Configurator
config.add_route('home', '/')
在您创建视图并将其附加到该路由之前,应用程序的该路径仍然开放且孤立。当您添加视图时,请确保在参数列表中包含 request
对象。每个 Pyramid 视图必须将 request
对象作为其第一个参数,因为这是在 Pyramid 调用视图时作为第一个参数传递给视图的对象。
Pyramid 视图与 Flask 的一个相似之处是,您可以使用装饰器将函数标记为视图。具体来说,是来自 pyramid.view
的 @view_config
装饰器。
在 views.py
中,构建您想要在世界中看到的视图。
from pyramid.view import view_config
@view_config(route_name="hello", renderer="string")
def hello_world(request):
"""Print 'Hello, world!' as the response body."""
return 'Hello, world!'
使用 @view_config
装饰器,您至少必须指定将映射到此特定视图的路由的名称。如果您愿意,可以将 view_config
装饰器堆叠在彼此之上以映射到多个路由,但您必须至少有一个来连接视图,并且每个都必须包含路由的名称。[注意: “to connect view the view” 的措辞是否正确?]
另一个参数 renderer
是可选的,但并非真正可选。如果您不指定渲染器,则必须使用来自 pyramid.response
的 Response
对象,有意识地构建您想要发送回客户端的 HTTP 响应。通过将 renderer
指定为字符串,Pyramid 知道获取此函数返回的任何内容,并将其包装在 MIME 类型为 text/plain
的同一 Response
对象中。默认情况下,Pyramid 允许您使用 string
和 json
作为渲染器。如果您已将模板引擎附加到您的应用程序,因为您还希望 Pyramid 生成您的 HTML,则可以将 HTML 模板直接指向为渲染器。
第一个视图已完成。这是现在带有附加路由的 __init__.py
的样子。
# in __init__.py
from pyramid.config import Configurator
def main(global_config, **settings):
"""Returns a Pyramid WSGI application."""
config = Configurator(settings=settings)
config.add_route('hello', '/')
config.scan()
return config.make_wsgi_app()
太棒了!到达这里绝非易事,但既然您已经设置好了,您就可以以明显更小的难度添加功能。
平滑粗糙的边缘
现在应用程序只有一个路由,但是很容易看出,大型应用程序可以有几十个甚至数百个路由。将它们全部包含在具有中央配置的同一 main
函数中并不是真正的好主意,因为它会变得混乱。值得庆幸的是,通过对应用程序进行一些调整,可以很容易地包含路由。
第一步:在 routes.py
文件中,创建一个名为 includeme
的函数(是的,它实际上必须这样命名),该函数将配置器对象作为参数。
# in routes.py
def includeme(config):
"""Include these routes within the application."""
第二步:将 config.add_route
方法调用从 __init__.py
移动到 includeme
函数中
def includeme(config):
"""Include these routes within the application."""
config.add_route('hello', '/')
第三步:提醒 Configurator 您需要包含此 routes.py
文件作为其配置的一部分。因为它与 __init__.py
在同一目录中,所以您可以将此文件的导入路径指定为 .routes
。
# in __init__.py
from pyramid.config import Configurator
def main(global_config, **settings):
"""Returns a Pyramid WSGI application."""
config = Configurator(settings=settings)
config.include('.routes')
config.scan()
return config.make_wsgi_app()
连接数据库
与 Flask 一样,您需要通过连接数据库来持久化数据。Pyramid 将直接利用 SQLAlchemy,而不是使用专门定制的包。
首先完成简单的部分。psycopg2
和 sqlalchemy
是与 Postgres 数据库通信和管理模型所必需的,因此将它们添加到 setup.py
中。
# in setup.py
requires = [
'pyramid',
'pyramid-ipython',
'waitress',
'sqlalchemy',
'psycopg2'
]
# blah blah other code
现在,您需要决定如何包含数据库的 URL。这里没有错误的答案;您做什么将取决于您正在构建的应用程序以及您的代码库需要公开的程度。
第一个选项是将尽可能多的配置保留在一个位置,方法是将数据库 URL 硬编码到 config.ini
文件中。一个缺点是,这会为具有公共代码库的应用程序带来安全风险。任何可以查看代码库的人都可以看到完整的数据库 URL,包括用户名、密码、数据库名称和端口。另一个是可维护性;如果您需要更改环境或应用程序的数据库位置,您必须直接修改 config.ini
文件。要么您必须为每个新环境维护一个配置文件,这会增加应用程序中不连续性和错误的潜在可能性。如果您选择此选项,请修改 [app:main]
标题下的 config.ini
文件以包含此键值对
sqlalchemy.url = postgres://127.0.0.1:5432/pyramid_todo
第二个选项在您创建 Configurator
时指定数据库 URL 的位置,指向一个环境变量,该变量的值可以根据您正在工作的环境进行设置。一个缺点是您进一步分散了配置,一部分在 config.ini
文件中,一部分直接在 Python 代码库中。另一个缺点是,当您需要在应用程序中的其他任何位置(例如,在数据库管理脚本中)使用数据库 URL 时,您必须在同一环境变量中编码第二个引用(或在一个位置设置变量并从该位置导入)。如果您选择此选项,请添加以下内容
# in __init__.py
import os
from pyramid.config import Configurator
SQLALCHEMY_URL = os.environ.get('DATABASE_URL', '')
def main(global_config, **settings):
"""Returns a Pyramid WSGI application."""
settings['sqlalchemy.url'] = SQLALCHEMY_URL # <-- important!
config = Configurator(settings=settings)
config.include('.routes')
config.scan()
return config.make_wsgi_app()
定义对象
好的,现在您有了一个数据库。现在您需要 Task
和 User
对象。
由于 Pyramid 直接使用 SQLAlchemy,因此在构建对象的方式上,Pyramid 与 Flash 有些不同。首先,您要构建的每个对象都必须继承自 SQLAlchemy 的 声明性基类。它将跟踪继承自它的所有内容,从而简化数据库的管理。
# in models.py
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Task(Base):
pass
class User(Base):
pass
列、这些列的数据类型和模型关系将以与 Flask 非常相似的方式声明,尽管它们将直接从 SQLAlchemy 而不是一些预先构建的 db
对象导入。其他一切都相同。
# in models.py
from datetime import datetime
import secrets
from sqlalchemy import (
Column, Unicode, Integer, DateTime, Boolean, relationship
)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Task(Base):
"""Tasks for the To Do list."""
id = Column(Integer, primary_key=True)
name = Column(Unicode, nullable=False)
note = Column(Unicode)
creation_date = Column(DateTime, nullable=False)
due_date = Column(DateTime)
completed = Column(Boolean, default=False)
user_id = Column(Integer, ForeignKey('user.id'), nullable=False)
user = relationship("user", back_populates="tasks")
def __init__(self, *args, **kwargs):
"""On construction, set date of creation."""
super().__init__(*args, **kwargs)
self.creation_date = datetime.now()
class User(Base):
"""The User object that owns tasks."""
id = Column(Integer, primary_key=True)
username = Column(Unicode, nullable=False)
email = Column(Unicode, nullable=False)
password = Column(Unicode, nullable=False)
date_joined = Column(DateTime, nullable=False)
token = Column(Unicode, nullable=False)
tasks = relationship("Task", back_populates="user")
def __init__(self, *args, **kwargs):
"""On construction, set date of creation."""
super().__init__(*args, **kwargs)
self.date_joined = datetime.now()
self.token = secrets.token_urlsafe(64)
请注意,models.py
的 config.include
行在任何地方都没有,因为不需要它。只有当应用程序配置的某些部分需要更改时,才需要 config.include
行。这仅创建了两个对象,它们继承自 SQLAlchemy 给我们的某个 Base
类。
初始化数据库
现在模型已完成,您可以编写一个脚本来与数据库对话并初始化数据库。在 scripts
目录中,创建两个文件:__init__.py
和 initializedb.py
。第一个只是将 scripts
目录变成一个 Python 包。第二个是数据库管理所需的脚本。
initializedb.py
需要一个函数来在数据库中设置必要的表。与 Flask 一样,此脚本必须了解 Base
对象,其元数据跟踪继承自它的每个类。数据库 URL 是指向和修改其表所必需的。
因此,此数据库初始化脚本将起作用
# initializedb.py
from sqlalchemy import engine_from_config
from todo import SQLALCHEMY_URL
from todo.models import Base
def main():
settings = {'sqlalchemy.url': SQLALCHEMY_URL}
engine = engine_from_config(settings, prefix='sqlalchemy.')
if bool(os.environ.get('DEBUG', '')):
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
重要提示: 只有当您将数据库 URL 作为环境变量包含在 todo/__init__.py
中时(上面的第二个选项),这才会起作用。如果数据库 URL 存储在配置文件中,您将必须包含几行来读取该文件。它看起来像这样
# alternate initializedb.py
from pyramid.paster import get_appsettings
from pyramid.scripts.common import parse_vars
from sqlalchemy import engine_from_config
import sys
from todo.models import Base
def main():
config_uri = sys.argv[1]
options = parse_vars(sys.argv[2:])
settings = get_appsettings(config_uri, options=options)
engine = engine_from_config(settings, prefix='sqlalchemy.')
if bool(os.environ.get('DEBUG', '')):
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
无论哪种方式,在 setup.py
中,添加一个控制台脚本,该脚本将访问并运行此函数。
# bottom of setup.py
setup(
# ... other stuff
entry_points={
'paste.app_factory': [
'main = todo:main',
],
'console_scripts': [
'initdb = todo.scripts.initializedb:main',
],
}
)
当此软件包安装后,您将可以访问名为 initdb
的新控制台脚本,该脚本将在您的数据库中构建表。如果数据库 URL 存储在配置文件中,则在调用命令时,您必须包含该文件的路径。它看起来像 $ initdb /path/to/config.ini
。
处理请求和数据库
好的,这里开始有点深入了。让我们谈谈事务(transactions)。从抽象意义上讲,“事务”是对现有数据库所做的任何更改。与 Flask 一样,事务只有在提交后才会持久化。如果已进行但尚未提交的更改,并且您不希望这些更改发生(可能在此过程中抛出了错误),则可以回滚(rollback)事务并中止这些更改。
在 Python 中,transaction 包允许你以对象的形式与事务进行交互,从而可以将多个更改合并到一个单独的提交中。transaction
提供了事务管理器,它为应用程序提供了一种直接的、线程感知的方式来处理事务,因此你只需考虑要更改什么。pyramid_tm
包将从 transaction
中获取事务管理器,并以适合 Pyramid 请求-响应周期的方式将其连接起来,为每个传入的请求附加一个事务管理器。
通常情况下,对于 Pyramid,当访问路由到视图的映射并调用视图函数时,request
对象会被填充。每个视图函数都将拥有一个 request
对象来使用。 然而,Pyramid 允许你修改其配置,以便将你可能需要的任何东西添加到 request
对象中。你可以使用你将添加到 request
的事务管理器,为每个请求创建一个会话,并将该会话添加到 request 中。
耶!那么,为什么这很重要呢?
通过将事务管理的会话附加到 request
对象,当视图完成处理请求时,对数据库会话所做的任何更改都将被提交,而无需你显式地提交。 以下是所有这些概念在代码中的样子。
# __init__.py
import os
from pyramid.config import Configurator
from sqlalchemy import engine_from_config
from sqlalchemy.orm import sessionmaker
import zope.sqlalchemy
SQLALCHEMY_URL = os.environ.get('DATABASE_URL', '')
def get_session_factory(engine):
"""Return a generator of database session objects."""
factory = sessionmaker()
factory.configure(bind=engine)
return factory
def get_tm_session(session_factory, transaction_manager):
"""Build a session and register it as a transaction-managed session."""
dbsession = session_factory()
zope.sqlalchemy.register(dbsession, transaction_manager=transaction_manager)
return dbsession
def main(global_config, **settings):
"""Returns a Pyramid WSGI application."""
settings['sqlalchemy.url'] = SQLALCHEMY_URL
settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager'
config = Configurator(settings=settings)
config.include('.routes')
config.include('pyramid_tm')
session_factory = get_session_factory(engine_from_config(settings, prefix='sqlalchemy.'))
config.registry['dbsession_factory'] = session_factory
config.add_request_method(
lambda request: get_tm_session(session_factory, request.tm),
'dbsession',
reify=True
)
config.scan()
return config.make_wsgi_app()
看起来很多,但它只是做了上面解释的事情,另外还在 request
对象中添加了一个名为 request.dbsession
的属性。
这里包含了一些新的包,所以请使用这些包更新 setup.py
。
# in setup.py
requires = [
'pyramid',
'pyramid-ipython',
'waitress',
'sqlalchemy',
'psycopg2',
'pyramid_tm',
'transaction',
'zope.sqlalchemy'
]
# blah blah other stuff
重新审视路由和视图
你需要创建一些真正的视图来处理数据库中的数据以及映射到这些视图的路由。
从路由开始。你创建了 routes.py
文件来处理你的路由,但除了基本的 /
路由之外没有做太多。让我们修复它。
# routes.py
def includeme(config):
config.add_route('info', '/api/v1/')
config.add_route('register', '/api/v1/accounts')
config.add_route('profile_detail', '/api/v1/accounts/{username}')
config.add_route('login', '/api/v1/accounts/login')
config.add_route('logout', '/api/v1/accounts/logout')
config.add_route('tasks', '/api/v1/accounts/{username}/tasks')
config.add_route('task_detail', '/api/v1/accounts/{username}/tasks/{id}')
现在,它不仅有像 /api/v1/accounts
这样的静态 URL,而且还可以处理像 /api/v1/accounts/{username}/tasks/{id}
这样的可变 URL,其中 URL 中的任何变量都将被花括号包围。
为了创建在你的应用程序中创建单个任务的视图(就像在 Flash 示例中一样),你可以使用 @view_config
装饰器来确保它只接受传入的 POST
请求,并了解 Pyramid 如何处理来自客户端的数据。
看一下代码,然后看看它与 Flask 版本的不同之处。
# in views.py
from datetime import datetime
from pyramid.view import view_config
from todo.models import Task, User
INCOMING_DATE_FMT = '%d/%m/%Y %H:%M:%S'
@view_config(route_name="tasks", request_method="POST", renderer='json')
def create_task(request):
"""Create a task for one user."""
response = request.response
response.headers.extend({'Content-Type': 'application/json'})
user = request.dbsession.query(User).filter_by(username=request.matchdict['username']).first()
if user:
due_date = request.json['due_date']
task = Task(
name=request.json['name'],
note=request.json['note'],
due_date=datetime.strptime(due_date, INCOMING_DATE_FMT) if due_date else None,
completed=bool(request.json['completed']),
user_id=user.id
)
request.dbsession.add(task)
response.status_code = 201
return {'msg': 'posted'}
首先,请注意 @view_config
装饰器,你希望此视图处理的唯一请求类型是 “POST” 请求。 如果你想指定一种类型的请求或一组请求,请提供表示请求的字符串,或此类字符串的元组/列表。
response = request.response
response.headers.extend({'Content-Type': 'application/json'})
# ...other code...
response.status_code = 201
发送到客户端的 HTTP 响应是基于 request.response
生成的。 通常,你无需担心该对象。 它只会生成格式正确的 HTTP 响应,你永远不会知道其中的区别。 但是,由于你想做一些特定的事情,例如修改响应的状态代码和标头,你需要访问该响应及其方法/属性。
与 Flask 不同,你不需要仅仅因为路由 URL 中有变量就修改视图函数参数列表。 相反,只要路由 URL 中存在变量,它就会被收集在 request
的 matchdict
属性中。 它将作为键值对存在于那里,其中键将是变量(例如,“username”),值将是在路由中指定的任何值(例如,“bobdobson”)。 无论通过路由 URL 传递什么值,它都将始终以字符串形式显示在 matchdict
中。 因此,当你想从传入的请求 URL 中提取用户名时,请使用 request.matchdict['username']
访问它。
user = request.dbsession.query(User).filter_by(username=request.matchdict['username']).first()
直接使用 sqlalchemy
查询对象与 flask-sqlalchemy
包允许的方式有很大不同。 回想一下,当你使用 flask-sqlalchemy
构建模型时,这些模型继承自 db.Model
对象。 该 db
对象已经包含到数据库的连接,因此该连接可以执行像 User.query.all()
这样的直接操作。
这里不存在这种简单的接口,因为 Pyramid 应用程序中的模型继承自 Base
,而 Base
是从直接来自 sqlalchemy
包的 declarative_base()
生成的。 它对其将要访问的数据库没有直接的感知。 这种感知是通过应用程序的中心配置作为 dbsession
属性附加到 request
对象上的。 以下是上面执行此操作的代码
config.add_request_method(
lambda request: get_tm_session(session_factory, request.tm),
'dbsession',
reify=True
)
综上所述,每当你想要查询或修改数据库时,都必须通过 request.dbsession
进行操作。 在这种情况下,你想通过使用用户名作为标识符来查询 “users” 表中特定的用户。 因此,User
对象作为参数提供给 .query
方法,然后从那里执行正常的 SQLAlchemy 操作。
关于这种查询数据库的方式,一个有趣的事情是,你可以查询的不仅仅是一个对象或一种类型的对象列表。 你可以查询:
- 对象属性本身,例如,
request.dbsession.query(User.username)
将查询用户名 - 对象属性的元组,例如,
request.dbsession.query(User.username, User.date_joined)
- 多个对象的元组,例如,
request.dbsession.query(User, Task)
随传入请求一起发送的数据将在 request.json
字典中找到。
最后一个主要区别是,由于将提交会话活动附加到 Pyramid 的请求-响应周期所需的所有机制,你无需在视图末尾调用 request.dbsession.commit()
。 这很方便,但前进的过程中需要注意一件事。 如果你想编辑数据库中已存在的对象,而不是向数据库添加新对象,则不能使用 request.dbsession.commit()
。 Pyramid 将抛出一个错误,大致意思是 “提交行为由事务管理器处理,因此你不能自己调用它。” 如果你不做一些类似于提交更改的操作,你的更改将不会生效。
这里的解决方案是使用 request.dbsession.flush()
。 .flush()
的作用是向数据库发出信号,表明已进行了一些更改,需要将其包含在下一次提交中。
未来规划
此时,你已经设置了 Pyramid 的大部分重要部分,类似于你在第一部分中使用 Flask 构建的内容。 应用程序还有更多内容,但大部分核心内容都在这里处理了。 其他视图函数将遵循类似的格式,当然,始终存在安全性问题(Pyramid 内置了安全性!)。
我看到的 Pyramid 应用程序设置中的主要区别之一是,它的配置步骤比 Flask 更为密集。 我分解了这些配置步骤,以更详细地解释构建 Pyramid 应用程序时发生的事情。 但是,如果我表现得好像自从我开始编程以来就了解所有这些,那将是不真诚的。 我第一次使用 Pyramid 框架是 Pyramid 1.7 及其 pcreate
脚手架系统,该系统构建了大部分必要的配置,因此你只需考虑要构建的功能。
从 Pyramid 1.8 开始,pcreate
已被弃用,转而使用 cookiecutter,后者实际上执行相同的操作。 区别在于它由其他人维护,并且 cookiecutter 模板不仅适用于 Pyramid 项目。 既然我们已经了解了 Pyramid 项目的组件,当cookiecutter 模板可用时,我绝不再支持从头开始构建 Pyramid 项目。 如果不必这样做,为什么要进行艰苦的工作呢? 实际上,pyramid-cookiecutter-alchemy 模板将完成我在这里写的大部分内容(甚至更多)。 它实际上类似于我第一次学习 Pyramid 时使用的 pcreate
脚手架。
在 PyCon Cleveland 2018 了解更多 Python 知识。
1 条评论