diff options
Diffstat (limited to 'docs/tutorials/wiki2/basiclayout.rst')
| -rw-r--r-- | docs/tutorials/wiki2/basiclayout.rst | 214 |
1 files changed, 123 insertions, 91 deletions
diff --git a/docs/tutorials/wiki2/basiclayout.rst b/docs/tutorials/wiki2/basiclayout.rst index 695d7f15b..e3d0a0a3c 100644 --- a/docs/tutorials/wiki2/basiclayout.rst +++ b/docs/tutorials/wiki2/basiclayout.rst @@ -12,12 +12,12 @@ 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 +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. -Open ``tutorial/tutorial/__init__.py``. It should already contain -the following: +Open ``tutorial/tutorial/__init__.py``. It should already contain the +following: .. literalinclude:: src/basiclayout/tutorial/__init__.py :linenos: @@ -36,6 +36,7 @@ the ``main`` function we've defined in our ``__init__.py``: .. literalinclude:: src/basiclayout/tutorial/__init__.py :pyobject: main + :lineno-start: 4 :linenos: :language: py @@ -43,58 +44,41 @@ 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``.) -The main function first creates a :term:`SQLAlchemy` database engine using -:func:`sqlalchemy.engine_from_config` from the ``sqlalchemy.`` prefixed -settings in the ``development.ini`` file's ``[app:main]`` section. -This will be a URI (something like ``sqlite://``): - - .. literalinclude:: src/basiclayout/tutorial/__init__.py - :lines: 13 - :language: py - -``main`` then initializes our SQLAlchemy session object, passing it the -engine: +Next in ``main``, construct a :term:`Configurator` object: .. literalinclude:: src/basiclayout/tutorial/__init__.py - :lines: 14 - :language: py - -``main`` subsequently initializes our SQLAlchemy declarative ``Base`` object, -assigning the engine we created to the ``bind`` attribute of it's -``metadata`` object. This allows table definitions done imperatively -(instead of declaratively, via a class statement) to work. We won't use any -such tables in our application, but if you add one later, long after you've -forgotten about this tutorial, you won't be left scratching your head when it -doesn't work. - - .. literalinclude:: src/basiclayout/tutorial/__init__.py - :lines: 15 - :language: py - -The next step of ``main`` is to construct a :term:`Configurator` object: - - .. literalinclude:: src/basiclayout/tutorial/__init__.py - :lines: 16 + :lines: 7 + :lineno-start: 7 :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``, -``db_string``, etc. +``sqlalchemy.url``, and so on. + +Next include :term:`Jinja2` templating bindings so that we can use renderers +with the ``.jinja2`` extension within our project. + + .. literalinclude:: src/basiclayout/tutorial/__init__.py + :lines: 8 + :lineno-start: 8 + :language: py -Next, include :term:`Chameleon` templating bindings so that we can use -renderers with the ``.pt`` extension within our project. +Next include the module ``meta`` from the package ``models`` using a dotted +Python path. .. literalinclude:: src/basiclayout/tutorial/__init__.py - :lines: 17 + :lines: 9 + :lineno-start: 9 :language: py ``main`` now calls :meth:`pyramid.config.Configurator.add_static_view` with two arguments: ``static`` (the name), and ``static`` (the path): .. literalinclude:: src/basiclayout/tutorial/__init__.py - :lines: 18 + :lines: 10 + :lineno-start: 10 :language: py This registers a static resource view which will match any URL that starts @@ -112,11 +96,12 @@ via the :meth:`pyramid.config.Configurator.add_route` method that will be used when the URL is ``/``: .. literalinclude:: src/basiclayout/tutorial/__init__.py - :lines: 19 + :lines: 11 + :lineno-start: 11 :language: py -Since this route has a ``pattern`` equaling ``/`` it is the route that will -be matched when the URL ``/`` is visited, e.g. ``http://localhost:6543/``. +Since this route has a ``pattern`` equaling ``/``, it is the route that will +be matched when the URL ``/`` is visited, e.g., ``http://localhost:6543/``. ``main`` next calls the ``scan`` method of the configurator (:meth:`pyramid.config.Configurator.scan`), which will recursively scan our @@ -126,28 +111,32 @@ view configuration will be registered, which will allow one of our application URLs to be mapped to some code. .. literalinclude:: src/basiclayout/tutorial/__init__.py - :lines: 20 + :lines: 12 + :lineno-start: 12 :language: py -Finally, ``main`` is finished configuring things, so it uses the +Finally ``main`` is finished configuring things, so it uses the :meth:`pyramid.config.Configurator.make_wsgi_app` method to return a :term:`WSGI` application: .. literalinclude:: src/basiclayout/tutorial/__init__.py - :lines: 21 + :lines: 13 + :lineno-start: 13 :language: py -View declarations via ``views.py`` ----------------------------------- + +View declarations via the ``views`` package +------------------------------------------- The main function of a web framework is mapping each URL pattern to code (a :term:`view callable`) that is executed when the requested URL matches the corresponding :term:`route`. Our application uses the :meth:`pyramid.view.view_config` decorator to perform this mapping. -Open ``tutorial/tutorial/views.py``. It should already contain the following: +Open ``tutorial/tutorial/views/default.py`` in the ``views`` package. It +should already contain the following: - .. literalinclude:: src/basiclayout/tutorial/views.py + .. literalinclude:: src/basiclayout/tutorial/views/default.py :linenos: :language: py @@ -156,13 +145,13 @@ function it decorates (``my_view``) with a :term:`view configuration`, consisting of: * a ``route_name`` (``home``) - * a ``renderer``, which is a template from the ``templates`` subdirectory - of the package. + * a ``renderer``, which is a template from the ``templates`` subdirectory of + the package. When the pattern associated with the ``home`` view is matched during a request, -``my_view()`` will be executed. ``my_view()`` returns a dictionary; the -renderer will use the ``templates/mytemplate.pt`` template to create a response -based on the values in the dictionary. +``my_view()`` will be executed. ``my_view()`` returns a dictionary; the +renderer will use the ``templates/mytemplate.jinja2`` template to create a +response based on the values in the dictionary. Note that ``my_view()`` accepts a single argument named ``request``. This is the standard call signature for a Pyramid :term:`view callable`. @@ -175,67 +164,110 @@ application. Without being processed by ``scan``, the decorator effectively does nothing. ``@view_config`` is inert without being detected via a :term:`scan`. -The sample ``my_view()`` created by the scaffold uses a ``try:`` and ``except:`` -clause to detect if there is a problem accessing the project database and -provide an alternate error response. That response will include the text -shown at the end of the file, which will be displayed in the browser to -inform the user about possible actions to take to solve the problem. +The sample ``my_view()`` created by the scaffold uses a ``try:`` and +``except:`` clause to detect if there is a problem accessing the project +database and provide an alternate error response. That response will include +the text shown at the end of the file, which will be displayed in the browser +to inform the user about possible actions to take to solve the problem. + -Content Models with ``models.py`` ---------------------------------- +Content models with the ``models`` package +------------------------------------------ In a SQLAlchemy-based application, a *model* object is an object composed by -querying the SQL database. The ``models.py`` file is where the ``alchemy`` +querying the SQL database. The ``models`` package is where the ``alchemy`` scaffold put the classes that implement our models. -Open ``tutorial/tutorial/models.py``. It should already contain the following: +First, open ``tutorial/tutorial/models/__init__.py``, which should already +contain the following: - .. literalinclude:: src/basiclayout/tutorial/models.py + .. literalinclude:: src/basiclayout/tutorial/models/__init__.py :linenos: :language: py -Let's examine this in detail. First, we need some imports to support later code: +Our ``__init__.py`` will perform some imports to support later code, then calls +the function :func:`sqlalchemy.orm.configure_mappers`. + +Next open ``tutorial/tutorial/models/meta.py``, which should already contain +the following: - .. literalinclude:: src/basiclayout/tutorial/models.py - :end-before: DBSession + .. literalinclude:: src/basiclayout/tutorial/models/meta.py :linenos: :language: py -Next we set up a SQLAlchemy ``DBSession`` object: +``meta.py`` contains imports that are used to support later code. We create a +dictionary ``NAMING_CONVENTION`` as well. - .. literalinclude:: src/basiclayout/tutorial/models.py - :lines: 17 + .. literalinclude:: src/basiclayout/tutorial/models/meta.py + :end-before: metadata + :linenos: + :language: py + +Next we create a ``metadata`` object from the class +:class:`sqlalchemy.schema.MetaData`, using ``NAMING_CONVENTION`` as the value +for the ``naming_convention`` argument. We also need to create a declarative +``Base`` object to use as a base class for our model. Then our model classes +will inherit from the ``Base`` class so they can be associated with our +particular database connection. + + .. literalinclude:: src/basiclayout/tutorial/models/meta.py + :lines: 18-19 + :lineno-start: 18 + :linenos: :language: py -``scoped_session`` and ``sessionmaker`` are standard SQLAlchemy helpers. -``scoped_session`` allows us to access our database connection globally. -``sessionmaker`` creates a database session object. We pass to -``sessionmaker`` the ``extension=ZopeTransactionExtension()`` extension -option in order to allow the system to automatically manage database -transactions. With ``ZopeTransactionExtension`` activated, our application -will automatically issue a transaction commit after every request unless an -exception is raised, in which case the transaction will be aborted. +Next we define several functions, the first of which is ``includeme``, which +configures various database settings by calling subsequently defined functions. + + .. literalinclude:: src/basiclayout/tutorial/models/meta.py + :pyobject: includeme + :lineno-start: 22 + :linenos: + :language: py + +The function ``get_session`` registers a database session with a transaction +manager, and returns a ``dbsession`` object. With the transaction manager, our +application will automatically issue a transaction commit after every request +unless an exception is raised, in which case the transaction will be aborted. + + .. literalinclude:: src/basiclayout/tutorial/models/meta.py + :pyobject: get_session + :lineno-start: 35 + :linenos: + :language: py -We also need to create a declarative ``Base`` object to use as a -base class for our model: +The ``get_engine`` function creates an :term:`SQLAlchemy` database engine using +:func:`sqlalchemy.engine_from_config` from the ``sqlalchemy.``-prefixed +settings in the ``development.ini`` file's ``[app:main]`` section, which is a +URI, something like ``sqlite://``. - .. literalinclude:: src/basiclayout/tutorial/models.py - :lines: 18 + .. literalinclude:: src/basiclayout/tutorial/models/meta.py + :pyobject: get_engine + :lineno-start: 42 + :linenos: :language: py -Our model classes will inherit from this ``Base`` class so they can be -associated with our particular database connection. +The function ``get_dbmaker`` accepts an :term:`SQLAlchemy` database engine, +and creates a database session object ``dbmaker`` from the :term:`SQLAlchemy` +class :class:`sqlalchemy.orm.session.sessionmaker`, which is then used for +creating a session with the database engine. + + .. literalinclude:: src/basiclayout/tutorial/models/meta.py + :pyobject: get_dbmaker + :lineno-start: 46 + :linenos: + :language: py -To give a simple example of a model class, we define one named ``MyModel``: +To give a simple example of a model class, we define one named ``MyModel``: - .. literalinclude:: src/basiclayout/tutorial/models.py + .. literalinclude:: src/basiclayout/tutorial/models/mymodel.py :pyobject: MyModel :linenos: :language: py Our example model does not require an ``__init__`` method because SQLAlchemy -supplies for us a default constructor if one is not already present, -which accepts keyword arguments of the same name as that of the mapped attributes. +supplies for us a default constructor if one is not already present, which +accepts keyword arguments of the same name as that of the mapped attributes. .. note:: Example usage of MyModel: @@ -247,8 +279,8 @@ The ``MyModel`` class has a ``__tablename__`` attribute. This informs SQLAlchemy which table to use to store the data representing instances of this class. -The Index import and the Index object creation is not required for this -tutorial, and will be removed in the next step. - That's about all there is to it regarding models, views, and initialization code in our stock application. + +The Index import and the Index object creation is not required for this +tutorial, and will be removed in the next step. |
