7:RDBMS根工厂

使用sqlAlchemy通过资源工厂提供持久的根资源。

背景

6:在ZODB中存储资源 我们使用了一个python对象数据库zodb来存储我们的资源树信息。ZODB在保持一个可以用于遍历的“位置感知”的图形结构方面非常有用。

Relational databases, though, aren't hierarchical. We can, however, use SQLAlchemy's adjacency list relationship 提供树状结构。我们将在接下来的两个步骤中进行此操作。

在第一步中,我们准备好了基础知识:sqlacalchemy、sqlite表、事务意识和根工厂,它们为我们提供了一个上下文。我们将使用 2:有立地根的基本导线测量 作为起点。

注解

此步骤假定您熟悉 19: Databases Using SQLAlchemy .

注解

遍历对sqlAlchemy邻接表关系和多态表继承的使用来自 Kotti 基于 Pyramid 的CMS,灵感来自plone。DanielNouri用各种技术和思想提出了SQL数据库中一流遍历的思想。科蒂无疑是寻找SQL中最现代的遍历层次结构方法的地方。

目标

  • 在项目中引入sqlacalchemy和sqlite,包括事务意识。
  • 提供存储在RDBMS中的根对象,并将其用作上下文。

步骤

  1. 我们将使用SiteRoot步骤作为起点:

    $ cd ..; cp -r siteroot sqlroot; cd sqlroot
    
  2. 在中引入一些新的依赖项和控制台脚本 sqlroot/setup.py

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    from setuptools import setup
    
    requires = [
        'pyramid',
        'pyramid_jinja2',
        'pyramid_tm',
        'sqlalchemy',
        'zope.sqlalchemy',
        'pyramid_debugtoolbar'
    ]
    
    setup(name='tutorial',
          install_requires=requires,
          entry_points="""\
          [paste.app_factory]
          main = tutorial:main
          [console_scripts]
          initialize_tutorial_db = tutorial.initialize_db:main
          """,
    )
    
  3. 现在我们可以初始化我们的项目:

    $ $VENV/bin/python setup.py develop
    
  4. 我们的配置文件位于 sqlroot/development.ini wires together some new pieces:

    [app:main]
    use = egg:tutorial
    pyramid.reload_templates = true
    pyramid.includes =
        pyramid_debugtoolbar
        pyramid_tm
    sqlalchemy.url = sqlite:///%(here)s/sqltutorial.sqlite
    
    [server:main]
    use = egg:pyramid#wsgiref
    host = 0.0.0.0
    port = 6543
    
    # Begin logging configuration
    
    [loggers]
    keys = root, tutorial, sqlalchemy
    
    [logger_tutorial]
    level = DEBUG
    handlers =
    qualname = tutorial
    
    [logger_sqlalchemy]
    level = INFO
    handlers =
    qualname = sqlalchemy.engine
    
    [handlers]
    keys = console
    
    [formatters]
    keys = generic
    
    [logger_root]
    level = INFO
    handlers = console
    
    [handler_console]
    class = StreamHandler
    args = (sys.stderr,)
    level = NOTSET
    formatter = generic
    
    [formatter_generic]
    format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
    
    # End logging configuration
    
  5. 这个 setup.py 在处具有控制台脚本的入口点 sqlroot/tutorial/initialize_db.py , so let's add that script:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    import os
    import sys
    import transaction
    
    from sqlalchemy import engine_from_config
    
    from pyramid.paster import (
        get_appsettings,
        setup_logging,
        )
    
    from .models import (
        DBSession,
        Root,
        Base,
        )
    
    
    def usage(argv):
        cmd = os.path.basename(argv[0])
        print('usage: %s <config_uri>\n'
              '(example: "%s development.ini")' % (cmd, cmd))
        sys.exit(1)
    
    
    def main(argv=sys.argv):
        if len(argv) != 2:
            usage(argv)
        config_uri = argv[1]
        setup_logging(config_uri)
        settings = get_appsettings(config_uri)
        engine = engine_from_config(settings, 'sqlalchemy.')
        DBSession.configure(bind=engine)
        Base.metadata.create_all(engine)
    
        with transaction.manager:
            root = Root(title='My SQLTraversal Root')
            DBSession.add(root)
    
  6. 我们的启动代码 sqlroot/tutorial/__init__.py 获取一些引导更改:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    from pyramid.config import Configurator
    
    from sqlalchemy import engine_from_config
    
    from .models import (
        DBSession,
        Base,
        root_factory
        )
    
    
    def main(global_config, **settings):
        engine = engine_from_config(settings, 'sqlalchemy.')
        DBSession.configure(bind=engine)
        Base.metadata.bind = engine
    
        config = Configurator(settings=settings,
                              root_factory=root_factory)
        config.include('pyramid_jinja2')
        config.scan('.views')
        return config.make_wsgi_app()
    
  7. 创造 sqlroot/tutorial/models.py with our SQLAlchemy model for our persistent root:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    from sqlalchemy import (
        Column,
        Integer,
        Text,
        )
    
    from sqlalchemy.ext.declarative import declarative_base
    
    from sqlalchemy.orm import (
        scoped_session,
        sessionmaker,
        )
    
    from zope.sqlalchemy import ZopeTransactionExtension
    
    DBSession = scoped_session(
        sessionmaker(extension=ZopeTransactionExtension()))
    Base = declarative_base()
    
    
    class Root(Base):
        __name__ = ''
        __parent__ = None
        __tablename__ = 'root'
        uid = Column(Integer, primary_key=True)
        title = Column(Text, unique=True)
    
    
    def root_factory(request):
        return DBSession.query(Root).one()
    
  8. 让我们运行这个控制台脚本,从而生成我们的数据库和表:

    $ $VENV/bin/initialize_tutorial_db development.ini
    2013-09-29 15:42:23,564 INFO  [sqlalchemy.engine.base.Engine][MainThread] PRAGMA table_info("root")
    2013-09-29 15:42:23,565 INFO  [sqlalchemy.engine.base.Engine][MainThread] ()
    2013-09-29 15:42:23,566 INFO  [sqlalchemy.engine.base.Engine][MainThread]
    CREATE TABLE root (
        uid INTEGER NOT NULL,
        title TEXT,
        PRIMARY KEY (uid),
        UNIQUE (title)
    )
    
    
    2013-09-29 15:42:23,566 INFO  [sqlalchemy.engine.base.Engine][MainThread] ()
    2013-09-29 15:42:23,569 INFO  [sqlalchemy.engine.base.Engine][MainThread] COMMIT
    2013-09-29 15:42:23,572 INFO  [sqlalchemy.engine.base.Engine][MainThread] BEGIN (implicit)
    2013-09-29 15:42:23,573 INFO  [sqlalchemy.engine.base.Engine][MainThread] INSERT INTO root (title) VALUES (?)
    2013-09-29 15:42:23,573 INFO  [sqlalchemy.engine.base.Engine][MainThread] ('My SQLAlchemy Root',)
    2013-09-29 15:42:23,576 INFO  [sqlalchemy.engine.base.Engine][MainThread] COMMIT
    
  9. 视图或模板中没有任何更改。

  10. 运行 Pyramid 应用程序时使用:

    $ $VENV/bin/pserve development.ini --reload
    
  11. 在浏览器中打开http://localhost:6543/。

分析

我们执行与我们在中看到的相同类型的SQLAlchemy设置工作 19: Databases Using SQLAlchemy . 在这种情况下,根工厂从数据库返回一个对象。

这个 models.Root 实例是 context context .

这一点可以通过这样一个事实来说明:我们不必更改视图逻辑或模板。他们依赖于上下文。 Pyramid 找到了上下文并将其传递到我们的视图中。

Extra Credit

  1. 如果数据库没有 Root 与sqlAlchemy查询匹配?