6:在ZODB中存储资源¶
在数据库中存储和检索资源树容器和项目。
背景¶
我们现在有了一个资源树,可以无限深入,一路添加项目和子容器。我们显然需要一个数据库,一个可以支持层次结构的数据库。zodb是一个基于事务的python数据库,支持透明持久性。我们将修改我们的应用程序以使用ZODB。
在此过程中,我们将增加 pyramid_tm
, a system for adding transaction awareness to our code. With this we don't need to manually manage our transaction begin/commit cycles in our application code. Instead, transactions are setup transparently on request/response boundaries, outside our application code.
目标¶
创建一个将记录添加到持久存储的CRUD应用程序。
安装程序
pyramid_tm
和pyramid_zodbconn
.使我们的“内容”类继承自
Persistent
.在应用程序中设置数据库连接字符串。
建立一个根工厂,从zodb而不是从内存为根提供服务。
步骤¶
我们将使用前面的步骤作为起点:
$ cd ..; cp -r addcontent zodb; cd zodb
在中引入一些新的依赖项
zodb/setup.py
:1from setuptools import setup 2 3requires = [ 4 'pyramid', 5 'pyramid_jinja2', 6 'ZODB3', 7 'pyramid_zodbconn', 8 'pyramid_tm', 9 'pyramid_debugtoolbar' 10] 11 12setup(name='tutorial', 13 install_requires=requires, 14 entry_points="""\ 15 [paste.app_factory] 16 main = tutorial:main 17 """, 18)
We can now install our project:
$ $VENV/bin/python setup.py develop
修改我们的
zodb/development.ini
要包括一些配置并提供数据库连接参数:1[app:main] 2use = egg:tutorial 3pyramid.reload_templates = true 4pyramid.includes = 5 pyramid_debugtoolbar 6 pyramid_zodbconn 7 pyramid_tm 8zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 9 10[server:main] 11use = egg:pyramid#wsgiref 12host = 0.0.0.0 13port = 6543 14 15# Begin logging configuration 16 17[loggers] 18keys = root, tutorial 19 20[logger_tutorial] 21level = DEBUG 22handlers = 23qualname = tutorial 24 25[handlers] 26keys = console 27 28[formatters] 29keys = generic 30 31[logger_root] 32level = INFO 33handlers = console 34 35[handler_console] 36class = StreamHandler 37args = (sys.stderr,) 38level = NOTSET 39formatter = generic 40 41[formatter_generic] 42format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s 43 44# End logging configuration
我们的启动代码
zodb/tutorial/__init__.py
获取一些引导更改:1from pyramid.config import Configurator 2from pyramid_zodbconn import get_connection 3 4from .resources import bootstrap 5 6 7def root_factory(request): 8 conn = get_connection(request) 9 return bootstrap(conn.root()) 10 11def main(global_config, **settings): 12 config = Configurator(settings=settings, 13 root_factory=root_factory) 14 config.include('pyramid_jinja2') 15 config.scan('.views') 16 return config.make_wsgi_app()
我们的观点
zodb/tutorial/views.py
在add_folder
和add_content
for how new instances are made and put into a container:1from random import randint 2 3from pyramid.httpexceptions import HTTPFound 4from pyramid.location import lineage 5from pyramid.view import view_config 6 7from .resources import ( 8 Root, 9 Folder, 10 Document 11 ) 12 13 14class TutorialViews(object): 15 def __init__(self, context, request): 16 self.context = context 17 self.request = request 18 self.parents = reversed(list(lineage(context))) 19 20 @view_config(renderer='templates/root.jinja2', 21 context=Root) 22 def root(self): 23 page_title = 'Quick Tutorial: Root' 24 return dict(page_title=page_title) 25 26 @view_config(renderer='templates/folder.jinja2', 27 context=Folder) 28 def folder(self): 29 page_title = 'Quick Tutorial: Folder' 30 return dict(page_title=page_title) 31 32 @view_config(name='add_folder', context=Folder) 33 def add_folder(self): 34 # Make a new Folder 35 title = self.request.POST['folder_title'] 36 name = str(randint(0, 999999)) 37 new_folder = Folder(title) 38 new_folder.__name__ = name 39 new_folder.__parent__ = self.context 40 self.context[name] = new_folder 41 42 # Redirect to the new folder 43 url = self.request.resource_url(new_folder) 44 return HTTPFound(location=url) 45 46 @view_config(name='add_document', context=Folder) 47 def add_document(self): 48 # Make a new Document 49 title = self.request.POST['document_title'] 50 name = str(randint(0, 999999)) 51 new_document = Document(title) 52 new_document.__name__ = name 53 new_document.__parent__ = self.context 54 self.context[name] = new_document 55 56 # Redirect to the new document 57 url = self.request.resource_url(new_document) 58 return HTTPFound(location=url) 59 60 @view_config(renderer='templates/document.jinja2', 61 context=Document) 62 def document(self): 63 page_title = 'Quick Tutorial: Document' 64 return dict(page_title=page_title)
使我们的资源在
zodb/tutorial/resources.py
:1from persistent import Persistent 2from persistent.mapping import PersistentMapping 3import transaction 4 5 6class Folder(PersistentMapping): 7 def __init__(self, title): 8 PersistentMapping.__init__(self) 9 self.title = title 10 11 12class Root(Folder): 13 __name__ = None 14 __parent__ = None 15 16 17class Document(Persistent): 18 def __init__(self, title): 19 Persistent.__init__(self) 20 self.title = title 21 22 23def bootstrap(zodb_root): 24 if not 'tutorial' in zodb_root: 25 root = Root('My Site') 26 zodb_root['tutorial'] = root 27 transaction.commit() 28 return zodb_root['tutorial']
No changes to any templates!
运行 Pyramid 应用程序时使用:
$ $VENV/bin/pserve development.ini --reload
在浏览器中打开http://localhost:6543/。
分析¶
我们安装 pyramid_zodbconn
处理与zodb的数据库连接。这也会提取zodb3包。
使能 pyramid_zodbconn
:
我们激活包配置使用
pyramid.includes
.我们定义了一个
zodbconn.uri
使用data.fs文件的路径进行设置。
在根工厂中,我们不再使用旧的根对象,而是获得到zodb的连接,并使用它创建对象。
我们的资源需要一些小的改变。文件夹现在从继承 persistent.PersistentMapping
和文档来源 persistent.Persistent
. 注意 Folder
now needs to call super()
上 __init__
方法,否则映射将无法正确初始化。
在引导程序上,注意 transaction.commit()
to commit the change. 这是因为在第一次启动时,我们希望在继续之前有一个根资源。
ZODB有多种部署模式。例如,Zeo是跨多个进程和主机的纯Python对象存储服务。relstorage允许您使用RDBMS来存储/检索您的python pickles。
Extra Credit¶
创建删除文档的视图。
删除配置行,其中包括
pyramid_tm
. 重新启动应用程序时会发生什么?重新启动时是否保留更改?如果删除名为
Data.fs*
是吗?