diff options
| author | Steve Piercy <web@stevepiercy.com> | 2018-11-26 23:59:40 -0800 |
|---|---|---|
| committer | Steve Piercy <web@stevepiercy.com> | 2018-11-26 23:59:40 -0800 |
| commit | 2615104ce4ba383a46df3c27ba26cfb86654e116 (patch) | |
| tree | ad938e23efd1be67821ddfb710748e746c92c420 /docs/tutorials/wiki/basiclayout.rst | |
| parent | 28f24e7592fc5a7fd28874e9a350f80674583471 (diff) | |
| parent | 587fe72fae0efda3a860d37a1ea2449a41dab622 (diff) | |
| download | pyramid-2615104ce4ba383a46df3c27ba26cfb86654e116.tar.gz pyramid-2615104ce4ba383a46df3c27ba26cfb86654e116.tar.bz2 pyramid-2615104ce4ba383a46df3c27ba26cfb86654e116.zip | |
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'docs/tutorials/wiki/basiclayout.rst')
| -rw-r--r-- | docs/tutorials/wiki/basiclayout.rst | 355 |
1 files changed, 211 insertions, 144 deletions
diff --git a/docs/tutorials/wiki/basiclayout.rst b/docs/tutorials/wiki/basiclayout.rst index 49ee6902e..52d3f4670 100644 --- a/docs/tutorials/wiki/basiclayout.rst +++ b/docs/tutorials/wiki/basiclayout.rst @@ -4,188 +4,255 @@ Basic Layout ============ -The starter files generated by selecting the ``zodb`` backend in the -cookiecutter are very basic, but they provide a good orientation for the -high-level patterns common to most :term:`traversal`-based (and -:term:`ZODB`-based) :app:`Pyramid` projects. +The starter files generated by the cookiecutter are very basic, but they provide a good orientation for the high-level patterns common to most :term:`traversal`-based (and :term:`ZODB`-based) :app:`Pyramid` projects. Application configuration with ``__init__.py`` ---------------------------------------------- -A directory on disk can be turned into a Python :term:`package` by containing -an ``__init__.py`` file. Even if empty, this marks a directory as a Python -package. We use ``__init__.py`` both as a marker, indicating the directory in -which it's contained is a package, and to contain application configuration -code. +A directory on disk can be turned into a Python :term:`package` by containing an ``__init__.py`` file. +Even if empty, this marks a directory as a Python package. +We use ``__init__.py`` both as a marker, indicating the directory in which it is contained is a package, and to contain application configuration code. -When you run the application using the ``pserve`` command using the -``development.ini`` generated configuration file, the application -configuration points at a :term:`Setuptools` :term:`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``. +When you run the application using the ``pserve`` command using the ``development.ini`` generated configuration file, the application configuration points at a :term:`Setuptools` :term:`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``. -Open ``tutorial/__init__.py``. It should already contain the following: +Open ``tutorial/__init__.py``. +It should already contain the following: .. literalinclude:: src/basiclayout/tutorial/__init__.py - :linenos: - :language: py + :linenos: + :language: py + +Let's go over this piece-by-piece. +First we need some imports to support later code. + +.. literalinclude:: src/basiclayout/tutorial/__init__.py + :end-before: root_factory + :lineno-match: + :language: py + +Define a :term:`root factory` for our Pyramid application. +It establishes a connection to ZODB database. +It returns an ``appmaker``, which we will describe in the next section :ref:`wiki-resources-and-models`. + +.. literalinclude:: src/basiclayout/tutorial/__init__.py + :pyobject: root_factory + :lineno-match: + :language: py + +``__init__.py`` defines a function named ``main``. +Here is the entirety of the ``main`` function that we have defined in our ``__init__.py``: + +.. literalinclude:: src/basiclayout/tutorial/__init__.py + :pyobject: main + :lineno-match: + :language: py + +When you invoke the ``pserve development.ini`` command, the ``main`` function above is executed. +It accepts some settings and returns a :term:`WSGI` application. +See :ref:`startup_chapter` for more about ``pserve``. + +Next in ``main``, construct a :term:`Configurator` object using a context manager. +See also :term:`Deployment settings`. + +.. literalinclude:: src/basiclayout/tutorial/__init__.py + :lines: 14 + :lineno-match: + :language: py + +``settings`` is passed to the ``Configurator`` as a keyword argument with the dictionary values passed as the ``**settings`` argument. +This will be a dictionary of settings parsed from the ``.ini`` file, which contains +deployment-related values, such as ``pyramid.reload_templates``, ``zodbconn.uri``, and so on. + +Next include support for ``pyramid_tm``, allowing Pyramid requests to join the active transaction as provided by the `transaction <https://pypi.org/project/transaction/>`_ package. + +.. literalinclude:: src/basiclayout/tutorial/__init__.py + :lines: 15 + :lineno-match: + :language: py + +Next include support for ``pyramid_retry`` to retry a request when transient exceptions occur. + +.. literalinclude:: src/basiclayout/tutorial/__init__.py + :lines: 16 + :lineno-match: + :language: py + +Next include support for ``pyramid_zodbconn``, providing integration between :term:`ZODB` and a Pyramid application. + +.. literalinclude:: src/basiclayout/tutorial/__init__.py + :lines: 17 + :lineno-match: + :language: py + +Next set a root factory using our function named ``root_factory``. + +.. literalinclude:: src/basiclayout/tutorial/__init__.py + :lines: 18 + :lineno-match: + :language: py + +Next include support for the :term:`Chameleon` template rendering bindings, allowing us to use the ``.pt`` templates. -#. *Lines 1-3*. Perform some dependency imports. +.. literalinclude:: src/basiclayout/tutorial/__init__.py + :lines: 19 + :lineno-match: + :language: py -#. *Lines 6-8*. Define a :term:`root factory` for our Pyramid application. +Next include routes from the ``.routes`` module. -#. *Line 11*. ``__init__.py`` defines a function named ``main``. +.. literalinclude:: src/basiclayout/tutorial/__init__.py + :lines: 20 + :lineno-match: + :language: py -#. *Line 14*. Use an explicit transaction manager for apps so that they do not implicitly create new transactions when touching the manager outside of the ``pyramid_tm`` lifecycle. +This registers a "static view" using the :meth:`pyramid.config.Configurator.add_static_view` method. +This view answers requests whose URL path starts with ``/static``. +This statement registers a view that will serve up static assets, such as CSS and image files. +In this case the URL will answer requests at ``http://localhost:6543/static/`` and below. -#. *Line 15*. Construct a :term:`Configurator` as a :term:`context manager` with the settings keyword parsed by :term:`PasteDeploy`. +The first argument is the "name" ``static``, which indicates that the URL path prefix of the view will be ``/static``. -#. *Line 16*. Include support for the :term:`Chameleon` template rendering - bindings, allowing us to use the ``.pt`` templates. +The second argument of this method is the "path". +It is a relative :term:`asset specification`. +It finds the resources it should serve within the ``static`` directory inside the ``tutorial`` package. +Alternatively the cookiecutter could have used an *absolute* asset specification as the path (``tutorial:static``). -#. *Line 17*. Include support for ``pyramid_tm``, allowing Pyramid requests to join the active transaction as provided by the `transaction <https://pypi.org/project/transaction/>`_ package. +The third argument is an optional ``cache_max_age`` which specifies the number of seconds the static asset will be HTTP-cached. -#. *Line 18*. Include support for ``pyramid_retry`` to retry a request when transient exceptions occur. +Next perform a :term:`scan`. -#. *Line 19*. Include support for ``pyramid_zodbconn``, providing integration between :term:`ZODB` and a Pyramid application. +.. literalinclude:: src/basiclayout/tutorial/__init__.py + :lines: 21 + :lineno-match: + :language: py -#. *Line 20*. Set a root factory using our function named ``root_factory``. +A scan will find :term:`configuration decoration`, such as view configuration decorators (e.g., ``@view_config``) in the source code of the ``tutorial`` package. +It will take actions based on these decorators. +We don't pass any arguments to :meth:`~pyramid.config.Configurator.scan`, which implies that the scan should take place in the current package (in this case, ``tutorial``). +The cookiecutter could have equivalently said ``config.scan('tutorial')``, but it chose to omit the package name argument. -#. *Line 21*. Register a "static view", which answers requests whose URL - paths start with ``/static``, using the - :meth:`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 second argument of this tag is the "path", - which is a relative :term:`asset specification`, so it finds the resources - it should serve within the ``static`` directory inside the ``tutorial`` - package. Alternatively the cookiecutter could have used an *absolute* asset - specification as the path (``tutorial:static``). +Finally use the :meth:`pyramid.config.Configurator.make_wsgi_app` method to return a :term:`WSGI` application. -#. *Line 22*. Perform a :term:`scan`. A scan will find :term:`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 - :meth:`~pyramid.config.Configurator.scan`, which implies that the scan - should take place in the current package (in this case, ``tutorial``). - The cookiecutter could have equivalently said ``config.scan('tutorial')``, but - it chose to omit the package name argument. +.. literalinclude:: src/basiclayout/tutorial/__init__.py + :lines: 22 + :lineno-match: + :language: py -#. *Line 23*. Use the - :meth:`pyramid.config.Configurator.make_wsgi_app` method - to return a :term:`WSGI` application. -Resources and models with ``models.py`` ---------------------------------------- +.. _wiki-resources-and-models: -:app:`Pyramid` uses the word :term:`resource` to describe objects arranged -hierarchically in a :term:`resource tree`. This tree is consulted by -:term:`traversal` to map URLs to code. In this application, the resource -tree represents the site structure, but it *also* represents the -:term:`domain model` of the application, because each resource is a node -stored persistently in a :term:`ZODB` database. The ``models.py`` file is -where the ``zodb`` cookiecutter put the classes that implement our -resource objects, each of which also happens to be a domain model object. +Resources and models with ``models`` package +-------------------------------------------- + +:app:`Pyramid` uses the word :term:`resource` to describe objects arranged hierarchically in a :term:`resource tree`. +This tree is consulted by :term:`traversal` to map URLs to code. +In this application, the resource tree represents the site structure, but it *also* represents the :term:`domain model` of the application. +Each resource is a node stored persistently in a :term:`ZODB` database. +The ``models.py`` file is where the ``zodb`` cookiecutter put the classes that implement our resource objects, each of which also happens to be a domain model object. Here is the source for ``models.py``: -.. literalinclude:: src/basiclayout/tutorial/models.py +.. literalinclude:: src/basiclayout/tutorial/models/__init__.py :linenos: :language: python -#. *Lines 4-5*. The ``MyModel`` :term:`resource` class is implemented here. - Instances of this class are capable of being persisted in :term:`ZODB` - because the class inherits from the - :class:`persistent.mapping.PersistentMapping` class. The ``__parent__`` - and ``__name__`` are important parts of the :term:`traversal` protocol. - By default, set these to ``None`` to indicate that this is the - :term:`root` object. - -#. *Lines 8-12*. ``appmaker`` is used to return the *application - root* object. It is called on *every request* to the - :app:`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``. - - Bootstrapping is done 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. - -Views With ``views.py`` ------------------------ - -Our cookiecutter 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``: - -.. literalinclude:: src/basiclayout/tutorial/views.py - :linenos: - :language: python +#. *Lines 4-5*. + The ``MyModel`` :term:`resource` class is implemented here. + Instances of this class are capable of being persisted in :term:`ZODB` because the class inherits from the :class:`persistent.mapping.PersistentMapping` class. + The ``__parent__`` and ``__name__`` are important parts of the :term:`traversal` protocol. + By default, these are set to ``None`` to indicate that this is the :term:`root` object. + +#. *Lines 8-12*. + ``appmaker`` is used to return the *application root* object. + It is called on *every request* to the :app:`Pyramid` application by virtue of the ``root_factory`` defined in our ``__init__.py``. + It also performs bootstrapping by *creating* an application root (inside the ZODB root object) if one does not already exist. + + Bootstrapping is done by first seeing if the database has the persistent application root. + If not, then we make an instance, store it, and commit the transaction. + + We then return the application root object. + + +View declarations via the ``views`` package +------------------------------------------- + +Our cookiecutter generated a default ``views`` package on our behalf. +It contains a two views. + +The first view is used to render the page shown when you visit the URL ``http://localhost:6543/``. +Open ``tutorial/views/default.py`` in the ``views`` package. +It should already contain the following: + +.. literalinclude:: src/basiclayout/tutorial/views/default.py + :linenos: + :language: python Let's try to understand the components in this module: -#. *Lines 1-2*. Perform some dependency imports. - -#. *Line 5*. Use the :func:`pyramid.view.view_config` :term:`configuration - decoration` to perform a :term:`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 :term:`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 :term:`traversal` finds the ``tutorial.models.MyModel`` - :term:`resource` to be the :term:`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 :term:`asset specification` of - ``templates/mytemplate.pt``. This asset specification points at a - :term:`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). Alternatively we could have 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``. - -#. *Lines 6-7*. We define a :term:`view callable` named ``my_view``, which - we decorated in the step above. This view callable is a *function* we - write generated by the ``zodb`` cookiecutter that is given a - ``request`` and which returns a dictionary. The ``mytemplate.pt`` - :term:`renderer` named by the asset specification in the step above will - convert this dictionary to a :term:`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. +#. *Lines 1-3*. + Perform some dependency imports. + +#. *Line 6*. + Use the :func:`pyramid.view.view_config` :term:`configuration decoration` to perform a :term:`view configuration` registration. + This view configuration registration will be activated when the application is started. + Remember in our application's ``__init__.py`` when we executed the :meth:`pyramid.config.Configurator.scan` method ``config.scan()``? + By calling the scan method, Pyramid's configurator will find and process this ``@view_config`` decorator, and create a view configuration within our application. + Without being processed by ``scan``, the decorator effectively does nothing. + ``@view_config`` is inert without being detected via a :term:`scan`. + + 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 ``my_view`` should only be run when :term:`traversal` finds the ``tutorial.models.MyModel`` :term:`resource` as the :term:`context` of a request. + In English this means that when the URL ``/`` is visited, and because ``MyModel`` is the root model, this view callable will be invoked. + + The ``renderer`` argument names an :term:`asset specification` of ``templates/mytemplate.pt``. + This asset specification points at a :term:`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 will see a ``mytemplate.pt`` template file + This template renders the default home page of the generated project. + This asset specification is *relative* to the ``views`` package. + Alternatively we could have used the absolute asset specification ``tutorial:templates/mytemplate.pt``. + + 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``. + +#. *Lines 7-8*. + A :term:`view callable` named ``my_view`` is defined, which is decorated in the step above. + This view callable is a *function* generated by the cookiecutter. + It is given a single argument, ``request``. + This is the standard call signature for a Pyramid :term:`view callable`. + The function returns the dictionary ``{'project': 'myproj'}``. + This dictionary is used by the template named by the ``mytemplate.pt`` asset specification to fill in certain values on the page. + +Let us open ``tutorial/views/default.py`` in the ``views`` package to look at the second view. + +.. literalinclude:: src/basiclayout/tutorial/views/notfound.py + :linenos: + :language: python + +Without repeating ourselves, we will point out the differences between this view and the previous. + +#. *Line 4*. + The ``notfound_view`` function is decorated with ``@notfound_view_config``. + This decorator registers a :term:`Not Found View` using :meth:`pyramid.config.Configurator.add_notfound_view`. + + The ``renderer`` argument names an :term:`asset specification` of ``templates/404.pt``. + +#. *Lines 5-7*. + A :term:`view callable` named ``notfound_view`` is defined, which is decorated in the step above. + It sets the HTTP response status code to ``404``. + The function returns an empty dictionary to the template ``404.pt``, which accepts no parameters anyway. + Configuration in ``development.ini`` ------------------------------------ -The ``development.ini`` (in the ``tutorial`` :term:`project` directory, as -opposed to the ``tutorial`` :term:`package` directory) looks like this: +The ``development.ini`` (in the ``tutorial`` :term:`project` directory, as opposed to the ``tutorial`` :term:`package` directory) looks like this: .. literalinclude:: src/basiclayout/development.ini :language: ini -Note the existence of a ``[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``. +Note the existence of a ``[app:main]`` section which specifies our WSGI application. +Our ZODB database settings are specified as the ``zodbconn.uri`` setting within this section. +When the server is started via ``pserve``, the values within this section are passed as ``**settings`` to the ``main`` function defined in ``__init__.py``. |
