ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 02.01.2026

Просмотров: 2694

Скачиваний: 0

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

36. ZODB + TRAVERSAL WIKI TUTORIAL

36.3.7 Visit the Application in a Browser

In a browser, visit http://localhost:6543/. You will see the generated application’s default page.

One thing you’ll notice is the “debug toolbar” icon on right hand side of the page. You can read more about the purpose of the icon at The Debug Toolbar. It allows you to get information about your application while you develop.

36.3.8 Decisions the zodb Scaffold Has Made For You

Creating a project using the zodb scaffold makes the following assumptions:

you are willing to use ZODB as persistent storage

you are willing to use traversal to map URLs to code.

latex-note.png

Pyramid supports any persistent storage mechanism (e.g. a SQL database or filesystem files, etc). Pyramid also supports an additional mechanism to map URLs to code (URL dispatch). However, for the purposes of this tutorial, we’ll only be using traversal and ZODB.

36.4 Basic Layout

The starter files generated by the zodb scaffold are basic, but they provide a good orientation for the high-level patterns common to most traversal -based Pyramid (and ZODB based) projects.

The source code for this tutorial stage can be browsed via http://github.com/Pylons/pyramid/tree/1.3- branch/docs/tutorials/wiki/src/basiclayout/.

398

36.4. BASIC LAYOUT

36.4.1 Application Configuration with __init__.py

A directory on disk can be turned into a Python package by containing an __init__.py file. Even if empty, this marks a directory as a Python package. Our application uses __init__.py as both a package marker, as well as to contain application configuration code.

When you run the application using the pserve command using the development.ini generated config file, the application configuration points at a Setuptools entry point described as egg:tutorial. In our application, because the application’s setup.py file says so, this entry point happens to be the main function within the file named __init__.py:

1

from pyramid.config import Configurator

2

from pyramid_zodbconn import get_connection

3

from .models import appmaker

4

 

5

def root_factory(request):

6

conn = get_connection(request)

7return appmaker(conn.root())

8

9 def main(global_config, **settings):

10""" This function returns a Pyramid WSGI application.

11"""

12config = Configurator(root_factory=root_factory, settings=settings)

13config.add_static_view(’static’, ’static’, cache_max_age=3600)

14config.scan()

15return config.make_wsgi_app()

1.Lines 1-3. Perform some dependency imports.

2.Lines 5-7 Define a root factory for our Pyramid application.

3.Line 12. We construct a Configurator with a root factory and the settings keywords parsed by PasteDeploy. The root factory is named root_factory.

4.Line 13. Register a ‘static view’ which answers requests which start with with URL path /static using the pyramid.config.Configurator.add_static_view method(). This statement registers a view that will serve up static assets, such as CSS and image files, for us, in this case, at http://localhost:6543/static/ and below. The first argument is the “name” static, which indicates that the URL path prefix of the view will be /static. the The second argument of this tag is the “path”, which is a relative asset specification, so it finds the resources it should serve within the static directory inside the tutorial package. The scaffold could have alternately used an absolute asset specification as the path (tutorial:static) but it does not.

399


36.ZODB + TRAVERSAL WIKI TUTORIAL

5.Line 14. Perform a scan. A scan will find configuration decoration, such as view configuration decorators (e.g. @view_config) in the source code of the tutorial package and will take actions based on these decorators. We don’t pass any arguments to scan(), which implies that the scan should take place in the current package (in this case, tutorial). The scaffold could have equivalently said config.scan(’tutorial’) but it chose to omit the package name argument.

6.Line 15. Use the pyramid.config.Configurator.make_wsgi_app() method to return a WSGI application.

36.4.2 Resources and Models with models.py

Pyramid uses the word resource to describe objects arranged hierarchically in a resource tree. This tree is consulted by traversal to map URLs to code. In this application, the resource tree represents the site structure, but it also represents the domain model of the application, because each resource is a node stored persistently in a ZODB database. The models.py file is where the zodb scaffold put the classes that implement our resource objects, each of which happens also to be a domain model object.

Here is the source for models.py:

1

from persistent.mapping import PersistentMapping

2

 

3

class MyModel(PersistentMapping):

4

__parent__ = __name__ = None

5

 

6

def appmaker(zodb_root):

7

if not ’app_root’ in zodb_root:

8app_root = MyModel()

9 zodb_root[’app_root’] = app_root

10import transaction

11transaction.commit()

12return zodb_root[’app_root’]

1. Lines 3-4. The MyModel resource class is implemented here. Instances of this class will be capable of being persisted in ZODB because the class inherits from the persistent.mapping.PersistentMapping class. The __parent__ and __name__ are important parts of the traversal protocol. By default, have these as None indicating that this is the root object.

2.Lines 6-12. appmaker is used to return the application root object. It is called on every request to the Pyramid application. It also performs bootstrapping by creating an application root (inside the ZODB root object) if one does not already exist. It is used by the “root_factory” we’ve defined in our __init__.py.

We do so by first seeing if the database has the persistent application root. If not, we make an instance, store it, and commit the transaction. We then return the application root object.

400


36.4. BASIC LAYOUT

36.4.3 Views With views.py

Our scaffold generated a default views.py on our behalf. It contains a single view, which is used to render the page shown when you visit the URL http://localhost:6543/.

Here is the source for views.py:

1

2

3

4

5

6

from pyramid.view import view_config from .models import MyModel

@view_config(context=MyModel, renderer=’templates/mytemplate.pt’) def my_view(request):

return {’project’:’tutorial’}

Let’s try to understand the components in this module:

1.Lines 1-2. Perform some dependency imports.

2.Line 4. Use the pyramid.view.view_config() configuration decoration to perform a view configuration registration. This view configuration registration will be activated when the application is started. It will be activated by virtue of it being found as the result of a scan (when Line 14 of __init__.py is run).

The @view_config decorator accepts a number of keyword arguments. We use two keyword arguments here: context and renderer.

The context argument signifies that the decorated view callable should only be run when traversal finds the tutorial.models.MyModel resource to be the context of a request. In English, this means that when the URL / is visited, because MyModel is the root model, this view callable will be invoked.

The renderer argument names an asset specification of templates/mytemplate.pt. This asset specification points at a Chameleon template which lives in the mytemplate.pt file within the templates directory of the tutorial package. And indeed if you look in the templates directory of this package, you’ll see a mytemplate.pt template file, which renders the default home page of the generated project. This asset specification is relative (to the view.py’s current package). We could have alternately an used the absolute asset specification tutorial:templates/mytemplate.pt, but chose to use the relative version.

Since this call to @view_config doesn’t pass a name argument, the my_view function which it decorates represents the “default” view callable used when the context is of the type MyModel.

3.Lines 5-6. We define a view callable named my_view, which we decorated in the step above. This view callable is a function we write generated by the zodb scaffold that is given a request and which returns a dictionary. The mytemplate.pt renderer named by the asset specification in the step above will convert this dictionary to a response on our behalf.

The function returns the dictionary {’project’:’tutorial’}. This dictionary is used by the template named by the mytemplate.pt asset specification to fill in certain values on the page.

401


36. ZODB + TRAVERSAL WIKI TUTORIAL

36.4.4 Configuration in development.ini

The development.ini (in the tutorial project directory, as opposed to the tutorial package directory) looks like this:

[app:main]

use = egg:tutorial 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 pyramid_zodbconn pyramid_tm

tm.attempts = 3

zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000

[server:main]

use = egg:waitress#main host = 0.0.0.0

port = 6543

# Begin logging configuration

[loggers] keys = root

[handlers] keys = console

[formatters] keys = generic

[logger_root] level = INFO handlers = console

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

402


36.5. DEFINING THE DOMAIN MODEL

[formatter_generic]

format = %(asctime)s %(levelname)-5.5s [%(name)s] %(message)s

# End logging configuration

Note the existence of an [app:main] section which specifies our WSGI application. Our ZODB database settings are specified as the zodbconn.uri setting within this section. This value, and the other values within this section are passed as **settings to the main function we defined in __init__.py when the server is started via pserve.

36.5 Defining the Domain Model

The first change we’ll make to our stock pcreate-generated application will be to define two resource constructors, one representing a wiki page, and another representing the wiki as a mapping of wiki page names to page objects. We’ll do this inside our models.py file.

Because we’re using ZODB to represent our resource tree, each of these resource constructors represents a domain model object, so we’ll call these constructors “model constructors”. Both our Page and Wiki constructors will be class objects. A single instance of the “Wiki” class will serve as a container for “Page” objects, which will be instances of the “Page” class.

The source code for this tutorial stage can be browsed via http://github.com/Pylons/pyramid/tree/1.3- branch/docs/tutorials/wiki/src/models/.

36.5.1 Delete the Database

In the next step, we’re going to remove the MyModel Python model class from our models.py file. Since this class is referred to within our persistent storage (represented on disk as a file named Data.fs), we’ll have strange things happen the next time we want to visit the application in a browser. Remove the Data.fs from the tutorial directory before proceeding any further. It’s always fine to do this as long as you don’t care about the content of the database; the database itself will be recreated as necessary.

403

36. ZODB + TRAVERSAL WIKI TUTORIAL

36.5.2 Edit models.py

latex-note.png

There is nothing automagically special about the filename models.py. A project may have many models throughout its codebase in arbitrarily-named files. Files implementing models often have model in their filenames, or they may live in a Python subpackage of your application package named models, but this is only by convention.

The first thing we want to do is remove the MyModel class from the generated models.py file. The MyModel class is only a sample and we’re not going to use it.

Then, we’ll add a Wiki class. We want it to inherit from the persistent.mapping.PersistentMapping class because it provides mapping behavior, and it makes sure that our Wiki page is stored as a “first-class” persistent object in our ZODB database.

Our Wiki class should have two attributes set to None at class scope: __parent__ and __name__. If a model has a __parent__ attribute of None in a traversal-based Pyramid application, it means that it’s the root model. The __name__ of the root model is also always None.

Then we’ll add a Page class. This class should inherit from the persistent.Persistent class. We’ll also give it an __init__ method that accepts a single parameter named data. This parameter will contain the ReStructuredText body representing the wiki page content. Note that Page objects don’t have an initial __name__ or __parent__ attribute. All objects in a traversal graph must have a __name__ and a __parent__ attribute. We don’t specify these here because both __name__ and __parent__ will be set by by a view function when a Page is added to our Wiki mapping.

As a last step, we want to change the appmaker function in our models.py file so that the root resource of our application is a Wiki instance. We’ll also slot a single page object (the front page) into the Wiki within the appmaker. This will provide traversal a resource tree to work against when it attempts to resolve URLs to resources.

36.5.3 Look at the Result of Our Edits to models.py

The result of all of our edits to models.py will end up looking something like this:

404