Michael Merickel
2018-10-16 8eed333343e4e9e7f11f3aee67299030d6bf2783
commit | author | age
640d77 1 .. _wiki2_installation:
SP 2
e26700 3 ============
CM 4 Installation
5 ============
6
3e6b60 7 Before you begin
8571f2 8 ----------------
07c189 9
7a8b0d 10 This tutorial assumes that you have already followed the steps in
f573cd 11 :ref:`installing_chapter`, except **do not create a virtual environment or
SP 12 install Pyramid**.  Thereby you will satisfy the following requirements.
e26700 13
a7dd05 14 * A Python interpreter is installed on your operating system.
d67566 15 * You've satisfied the :ref:`requirements-for-installing-packages`.
8571f2 16
07c189 17
b29848 18 Install SQLite3 and its development packages
SP 19 --------------------------------------------
3e6b60 20
b29848 21 If you used a package manager to install your Python or if you compiled your Python from source, then you must install SQLite3 and its development packages.  If you downloaded your Python as an installer from https://www.python.org, then you already have it installed and can skip this step.
SP 22
23 If you need to install the SQLite3 packages, then, for example, using the Debian system and ``apt-get``, the command would be the following:
24
25 .. code-block:: bash
26
dc1071 27     sudo apt-get install libsqlite3-dev
b29848 28
SP 29
30 Install cookiecutter
31 --------------------
32 We will use a :term:`cookiecutter` to create a Python package project from a Python package project template.  See `Cookiecutter Installation <https://cookiecutter.readthedocs.io/en/latest/installation.html>`_ for instructions.
33
34
35 Generate a Pyramid project from a cookiecutter
36 ----------------------------------------------
37
5af300 38 We will create a Pyramid project in your home directory for Unix or at the root for Windows. It is assumed you know the path to where you installed ``cookiecutter``. Issue the following commands and override the defaults in the prompts as follows.
3e6b60 39
5af300 40 On Unix
3e6b60 41 ^^^^^^^
SP 42
25fed6 43 .. code-block:: bash
3e6b60 44
dc1071 45     cd ~
0080e9 46     cookiecutter gh:Pylons/pyramid-cookiecutter-starter --checkout 1.10-branch
3e6b60 47
SP 48 On Windows
49 ^^^^^^^^^^
50
a651b3 51 .. code-block:: doscon
3e6b60 52
dc1071 53     cd \
0080e9 54     cookiecutter gh:Pylons/pyramid-cookiecutter-starter --checkout 1.10-branch
b29848 55
SP 56 On all operating systems
57 ^^^^^^^^^^^^^^^^^^^^^^^^
58 If prompted for the first item, accept the default ``yes`` by hitting return.
59
2cd381 60 .. code-block:: text
8571f2 61
28e688 62     You've cloned ~/.cookiecutters/pyramid-cookiecutter-starter before.
2cd381 63     Is it okay to delete and re-clone it? [yes]: yes
SP 64     project_name [Pyramid Scaffold]: myproj
6ff6fa 65     repo_name [myproj]: tutorial
6b6d0e 66     Select template_language:
SM 67     1 - jinja2
68     2 - chameleon
69     3 - mako
70     Choose from 1, 2, 3 [1]: 1
71     Select backend:
72     1 - none
73     2 - sqlalchemy
74     3 - zodb
75     Choose from 1, 2, 3 [1]: 2
76
3e6b60 77
b29848 78 Change directory into your newly created project
SP 79 ------------------------------------------------
80
5af300 81 On Unix
b29848 82 ^^^^^^^
SP 83
84 .. code-block:: bash
85
dc1071 86     cd tutorial
b29848 87
SP 88 On Windows
89 ^^^^^^^^^^
90
91 .. code-block:: doscon
92
dc1071 93     cd tutorial
b29848 94
SP 95
96 Set and use a ``VENV`` environment variable
7a8b0d 97 -------------------------------------------
e26700 98
b29848 99 We will set the ``VENV`` environment variable to the absolute path of the virtual environment, and use it going forward.
7a8b0d 100
5af300 101 On Unix
70cc73 102 ^^^^^^^
7a8b0d 103
25fed6 104 .. code-block:: bash
7a8b0d 105
dc1071 106     export VENV=~/tutorial
7a8b0d 107
70cc73 108 On Windows
SP 109 ^^^^^^^^^^
7a8b0d 110
a651b3 111 .. code-block:: doscon
7a8b0d 112
dc1071 113     set VENV=c:\tutorial
7a8b0d 114
b29848 115
SP 116 Create a virtual environment
117 ----------------------------
118
5af300 119 On Unix
b29848 120 ^^^^^^^
SP 121
122 .. code-block:: bash
123
dc1071 124     python3 -m venv $VENV
b29848 125
SP 126 On Windows
127 ^^^^^^^^^^
128
7ed8e2 129 Each version of Python uses different paths, so you will need to adjust the path to the command for your Python version. Recent versions of the Python 3 installer for Windows now install a Python launcher.
7a8b0d 130
SP 131 Python 2.7:
132
a651b3 133 .. code-block:: doscon
7a8b0d 134
dc1071 135     c:\Python27\Scripts\virtualenv %VENV%
7a8b0d 136
482075 137 Python 3.7:
7a8b0d 138
a651b3 139 .. code-block:: doscon
7a8b0d 140
dc1071 141     python -m venv %VENV%
25fed6 142
SP 143
b29848 144 Upgrade packaging tools in the virtual environment
SP 145 --------------------------------------------------
a7dd05 146
5af300 147 On Unix
a7dd05 148 ^^^^^^^
SP 149
150 .. code-block:: bash
151
dc1071 152     $VENV/bin/pip install --upgrade pip setuptools
a7dd05 153
SP 154 On Windows
155 ^^^^^^^^^^
156
a651b3 157 .. code-block:: doscon
a7dd05 158
dc1071 159     %VENV%\Scripts\pip install --upgrade pip setuptools
e26700 160
25fed6 161
af1a96 162 .. _installing_project_in_dev_mode:
TL 163
3e6b60 164 Installing the project in development mode
8571f2 165 ------------------------------------------
e26700 166
b29848 167 In order to do development on the project easily, you must "register" the project as a development egg in your workspace. We will install testing requirements at the same time. We do so with the following command.
e26700 168
5af300 169 On Unix
8571f2 170 ^^^^^^^
e26700 171
25fed6 172 .. code-block:: bash
e26700 173
dc1071 174     $VENV/bin/pip install -e ".[testing]"
e26700 175
70cc73 176 On Windows
8571f2 177 ^^^^^^^^^^
e26700 178
a651b3 179 .. code-block:: doscon
e26700 180
dc1071 181     %VENV%\Scripts\pip install -e ".[testing]"
25fed6 182
b29848 183 On all operating systems
SP 184 ^^^^^^^^^^^^^^^^^^^^^^^^
185
186 The console will show ``pip`` checking for packages and installing missing packages. Success executing this command will show a line like the following:
6772a2 187
e551a4 188 .. code-block:: bash
SP 189
b29848 190     Successfully installed Jinja2-2.8 Mako-1.0.6 MarkupSafe-0.23 \
SP 191     PasteDeploy-1.5.2 Pygments-2.1.3 SQLAlchemy-1.1.4 WebOb-1.6.3 \
192     WebTest-2.0.24 beautifulsoup4-4.5.1 coverage-4.2 py-1.4.32 pyramid-1.7.3 \
193     pyramid-debugtoolbar-3.0.5 pyramid-jinja2-2.7 pyramid-mako-1.0.2 \
2d5234 194     pyramid-tm-1.1.1 pytest-3.0.5 pytest-cov-2.4.0 repoze.lru-0.6 six-1.10.0 \
SP 195     transaction-2.0.3 translationstring-1.3 tutorial venusian-1.0 \
b29848 196     waitress-1.0.1 zope.deprecation-4.2.0 zope.interface-4.3.3 \
SP 197     zope.sqlalchemy-0.7.7
e551a4 198
b29848 199 Testing requirements are defined in our project's ``setup.py`` file, in the ``tests_require`` and ``extras_require`` stanzas.
e551a4 200
d8be5a 201 .. literalinclude:: src/installation/setup.py
dc1071 202     :language: python
SP 203     :lineno-match:
204     :lines: 24-28
e551a4 205
d8be5a 206 .. literalinclude:: src/installation/setup.py
dc1071 207     :language: python
SP 208     :lineno-match:
209     :lines: 48-50
e551a4 210
6772a2 211
49620d 212 .. _initialize_db_wiki2:
SP 213
214 Initialize and upgrade the database using Alembic
215 -------------------------------------------------
216
217 We use :term:`Alembic` to manage our database initialization and migrations.
218
219 Generate your first revision.
220
5af300 221 On Unix
49620d 222 ^^^^^^^
SP 223
224 .. code-block:: bash
225
dc1071 226     $VENV/bin/alembic -c development.ini revision --autogenerate -m "init"
49620d 227
SP 228 On Windows
229 ^^^^^^^^^^
230
231 .. code-block:: doscon
232
dc1071 233     %VENV%\Scripts\alembic -c development.ini revision --autogenerate -m "init"
49620d 234
SP 235 The output to your console should be something like this:
236
237 .. code-block:: text
238
dc1071 239     2018-06-22 17:57:31,587 INFO  [sqlalchemy.engine.base.Engine:1254][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
SP 240     2018-06-22 17:57:31,587 INFO  [sqlalchemy.engine.base.Engine:1255][MainThread] ()
241     2018-06-22 17:57:31,588 INFO  [sqlalchemy.engine.base.Engine:1254][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
242     2018-06-22 17:57:31,588 INFO  [sqlalchemy.engine.base.Engine:1255][MainThread] ()
243     2018-06-22 17:57:31,589 INFO  [sqlalchemy.engine.base.Engine:1151][MainThread] PRAGMA table_info("alembic_version")
244     2018-06-22 17:57:31,589 INFO  [sqlalchemy.engine.base.Engine:1154][MainThread] ()
245     2018-06-22 17:57:31,590 INFO  [sqlalchemy.engine.base.Engine:1151][MainThread] PRAGMA table_info("alembic_version")
246     2018-06-22 17:57:31,590 INFO  [sqlalchemy.engine.base.Engine:1154][MainThread] ()
247     2018-06-22 17:57:31,590 INFO  [sqlalchemy.engine.base.Engine:1151][MainThread]
248     CREATE TABLE alembic_version (
49620d 249            version_num VARCHAR(32) NOT NULL,
SP 250            CONSTRAINT alembic_version_pkc PRIMARY KEY (version_num)
dc1071 251     )
49620d 252
SP 253
dc1071 254     2018-06-22 17:57:31,591 INFO  [sqlalchemy.engine.base.Engine:1154][MainThread] ()
SP 255     2018-06-22 17:57:31,591 INFO  [sqlalchemy.engine.base.Engine:722][MainThread] COMMIT
256     2018-06-22 17:57:31,594 INFO  [sqlalchemy.engine.base.Engine:1151][MainThread] SELECT name FROM sqlite_master WHERE type='table' ORDER BY name
257     2018-06-22 17:57:31,594 INFO  [sqlalchemy.engine.base.Engine:1154][MainThread] ()
49620d 258      Generating /<somepath>/tutorial/alembic/versions/20180622_bab5a278ce04.py ... done
SP 259
260 Upgrade to that revision.
261
5af300 262 On Unix
49620d 263 ^^^^^^^
SP 264
265 .. code-block:: bash
266
dc1071 267     $VENV/bin/alembic -c development.ini upgrade head
49620d 268
SP 269 On Windows
270 ^^^^^^^^^^
271
272 .. code-block:: doscon
273
dc1071 274     %VENV%\Scripts\alembic -c development.ini upgrade head
49620d 275
SP 276 The output to your console should be something like this:
277
278 .. code-block:: text
279
dc1071 280     2018-06-22 17:57:37,814 INFO  [sqlalchemy.engine.base.Engine:1254][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
SP 281     2018-06-22 17:57:37,814 INFO  [sqlalchemy.engine.base.Engine:1255][MainThread] ()
282     2018-06-22 17:57:37,814 INFO  [sqlalchemy.engine.base.Engine:1254][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
283     2018-06-22 17:57:37,814 INFO  [sqlalchemy.engine.base.Engine:1255][MainThread] ()
284     2018-06-22 17:57:37,816 INFO  [sqlalchemy.engine.base.Engine:1151][MainThread] PRAGMA table_info("alembic_version")
285     2018-06-22 17:57:37,816 INFO  [sqlalchemy.engine.base.Engine:1154][MainThread] ()
286     2018-06-22 17:57:37,817 INFO  [sqlalchemy.engine.base.Engine:1151][MainThread] SELECT alembic_version.version_num
287     FROM alembic_version
288     2018-06-22 17:57:37,817 INFO  [sqlalchemy.engine.base.Engine:1154][MainThread] ()
289     2018-06-22 17:57:37,817 INFO  [sqlalchemy.engine.base.Engine:1151][MainThread] PRAGMA table_info("alembic_version")
290     2018-06-22 17:57:37,817 INFO  [sqlalchemy.engine.base.Engine:1154][MainThread] ()
291     2018-06-22 17:57:37,819 INFO  [sqlalchemy.engine.base.Engine:1151][MainThread]
292     CREATE TABLE models (
49620d 293            id INTEGER NOT NULL,
SP 294            name TEXT,
295            value INTEGER,
296            CONSTRAINT pk_models PRIMARY KEY (id)
dc1071 297     )
49620d 298
SP 299
dc1071 300     2018-06-22 17:57:37,820 INFO  [sqlalchemy.engine.base.Engine:1154][MainThread] ()
SP 301     2018-06-22 17:57:37,822 INFO  [sqlalchemy.engine.base.Engine:722][MainThread] COMMIT
302     2018-06-22 17:57:37,824 INFO  [sqlalchemy.engine.base.Engine:1151][MainThread] CREATE UNIQUE INDEX my_index ON models (name)
303     2018-06-22 17:57:37,824 INFO  [sqlalchemy.engine.base.Engine:1154][MainThread] ()
304     2018-06-22 17:57:37,825 INFO  [sqlalchemy.engine.base.Engine:722][MainThread] COMMIT
305     2018-06-22 17:57:37,825 INFO  [sqlalchemy.engine.base.Engine:1151][MainThread] INSERT INTO alembic_version (version_num) VALUES ('bab5a278ce04')
306     2018-06-22 17:57:37,825 INFO  [sqlalchemy.engine.base.Engine:1154][MainThread] ()
307     2018-06-22 17:57:37,825 INFO  [sqlalchemy.engine.base.Engine:722][MainThread] COMMIT
49620d 308
SP 309
310 .. _load_data_wiki2:
311
312 Load default data
313 -----------------
314
315 Load default data into the database using a :term:`console script`. Type the following command, making sure you are still in the ``tutorial`` directory (the directory with a ``development.ini`` in it):
316
5af300 317 On Unix
49620d 318 ^^^^^^^
SP 319
320 .. code-block:: bash
321
dc1071 322    $VENV/bin/initialize_tutorial_db development.ini
49620d 323
SP 324 On Windows
325 ^^^^^^^^^^
326
327 .. code-block:: doscon
328
dc1071 329    %VENV%\Scripts\initialize_tutorial_db development.ini
49620d 330
SP 331 The output to your console should be something like this:
332
333 .. code-block:: bash
334
dc1071 335     2018-06-22 17:57:46,241 INFO  [sqlalchemy.engine.base.Engine:1254][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
SP 336     2018-06-22 17:57:46,241 INFO  [sqlalchemy.engine.base.Engine:1255][MainThread] ()
337     2018-06-22 17:57:46,242 INFO  [sqlalchemy.engine.base.Engine:1254][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
338     2018-06-22 17:57:46,242 INFO  [sqlalchemy.engine.base.Engine:1255][MainThread] ()
339     2018-06-22 17:57:46,243 INFO  [sqlalchemy.engine.base.Engine:682][MainThread] BEGIN (implicit)
340     2018-06-22 17:57:46,244 INFO  [sqlalchemy.engine.base.Engine:1151][MainThread] INSERT INTO models (name, value) VALUES (?, ?)
341     2018-06-22 17:57:46,245 INFO  [sqlalchemy.engine.base.Engine:1154][MainThread] ('one', 1)
342     2018-06-22 17:57:46,246 INFO  [sqlalchemy.engine.base.Engine:722][MainThread] COMMIT
49620d 343
SP 344 Success!  You should now have a ``tutorial.sqlite`` file in your current
345 working directory. This is an SQLite database with a single table defined in it
346 (``models``) and single record inside of that.
347
348
e26700 349 .. _sql_running_tests:
CM 350
3e6b60 351 Run the tests
8571f2 352 -------------
e26700 353
e551a4 354 After you've installed the project in development mode as well as the testing
9591e9 355 requirements, you may run the tests for the project. The following commands
5cc53d 356 provide options to ``pytest`` that specify the module for which its tests shall be
PC 357 run, and to run ``pytest`` in quiet mode.
e26700 358
5af300 359 On Unix
8571f2 360 ^^^^^^^
e26700 361
25fed6 362 .. code-block:: bash
e26700 363
5cc53d 364     $VENV/bin/pytest -q
e551a4 365
70cc73 366 On Windows
8571f2 367 ^^^^^^^^^^
e26700 368
a651b3 369 .. code-block:: doscon
e26700 370
5cc53d 371     %VENV%\Scripts\pytest -q
6772a2 372
e551a4 373 For a successful test run, you should see output that ends like this:
SP 374
375 .. code-block:: bash
6772a2 376
dc1071 377     ..
SP 378     2 passed in 0.44 seconds
13846e 379
e26700 380
3e6b60 381 Expose test coverage information
8571f2 382 --------------------------------
e26700 383
5cc53d 384 You can run the ``pytest`` command to see test coverage information. This
PC 385 runs the tests in the same way that ``pytest`` does, but provides additional
b29848 386 :term:`coverage` information, exposing which lines of your project are covered by the
e551a4 387 tests.
e26700 388
d67566 389 We've already installed the ``pytest-cov`` package into our virtual
SP 390 environment, so we can run the tests with coverage.
e26700 391
5af300 392 On Unix
8571f2 393 ^^^^^^^
e26700 394
25fed6 395 .. code-block:: bash
e26700 396
5cc53d 397     $VENV/bin/pytest --cov --cov-report=term-missing
e26700 398
70cc73 399 On Windows
8571f2 400 ^^^^^^^^^^
e26700 401
a651b3 402 .. code-block:: doscon
e26700 403
5cc53d 404     c:\tutorial> %VENV%\Scripts\pytest --cov --cov-report=term-missing
e26700 405
25c809 406 If successful, you will see output something like this:
6772a2 407
25c809 408 .. code-block:: bash
6772a2 409
b29848 410     ======================== test session starts ========================
49620d 411     platform Python 3.6.5, pytest-3.6.2, py-1.5.3, pluggy-0.6.0
SP 412     rootdir: /<somepath>/tutorial, inifile: pytest.ini
413     plugins: cov-2.5.1
b29848 414     collected 2 items
e551a4 415
b29848 416     tutorial/tests.py ..
49620d 417     ------------------ coverage: platform Python 3.6.5 ------------------
dc1071 418     Name                                Stmts   Miss  Cover   Missing
SP 419     -----------------------------------------------------------------
420     tutorial/__init__.py                    8      6    25%   7-12
421     tutorial/models/__init__.py            24      0   100%
422     tutorial/models/meta.py                 5      0   100%
423     tutorial/models/mymodel.py              8      0   100%
424     tutorial/routes.py                      3      3     0%   1-3
425     tutorial/scripts/__init__.py            0      0   100%
426     tutorial/scripts/initialize_db.py      24     24     0%   1-34
427     tutorial/views/__init__.py              0      0   100%
428     tutorial/views/default.py              12      0   100%
429     tutorial/views/notfound.py              4      4     0%   1-7
430     -----------------------------------------------------------------
431     TOTAL                                  88     37    58%
49620d 432
b29848 433     ===================== 2 passed in 0.57 seconds ======================
6772a2 434
8571f2 435 Our package doesn't quite have 100% test coverage.
SP 436
e26700 437
b29848 438 .. _test_and_coverage_cookiecutter_defaults_sql:
5e3eeb 439
b29848 440 Test and coverage cookiecutter defaults
SP 441 ---------------------------------------
5e3eeb 442
5cc53d 443 Cookiecutters include configuration defaults for ``pytest`` and test coverage.
5e3eeb 444 These configuration files are ``pytest.ini`` and ``.coveragerc``, located at
SP 445 the root of your package. Without these defaults, we would need to specify the
446 path to the module on which we want to run tests and coverage.
447
5af300 448 On Unix
5e3eeb 449 ^^^^^^^
SP 450
451 .. code-block:: bash
452
5cc53d 453     $VENV/bin/pytest --cov=tutorial tutorial/tests.py -q
5e3eeb 454
SP 455 On Windows
456 ^^^^^^^^^^
457
458 .. code-block:: doscon
459
5cc53d 460     %VENV%\Scripts\pytest --cov=tutorial tutorial\tests.py -q
5e3eeb 461
5cc53d 462 pytest follows :ref:`conventions for Python test discovery
b29848 463 <pytest:test discovery>`, and the configuration defaults from the cookiecutter
5cc53d 464 tell ``pytest`` where to find the module on which we want to run tests and
5e3eeb 465 coverage.
SP 466
9c39f6 467 .. seealso:: See ``pytest``'s documentation for :ref:`pytest:usage` or invoke
5cc53d 468    ``pytest -h`` to see its full set of options.
5e3eeb 469
6772a2 470
b284c6 471 .. _wiki2-start-the-application:
6772a2 472
3e6b60 473 Start the application
8571f2 474 ---------------------
b284c6 475
1644ef 476 Start the application. See :ref:`what_is_this_pserve_thing` for more
MM 477 information on ``pserve``.
6772a2 478
5af300 479 On Unix
8571f2 480 ^^^^^^^
6772a2 481
25fed6 482 .. code-block:: bash
6772a2 483
dc1071 484     $VENV/bin/pserve development.ini --reload
6772a2 485
70cc73 486 On Windows
8571f2 487 ^^^^^^^^^^
6772a2 488
a651b3 489 .. code-block:: doscon
6772a2 490
dc1071 491     %VENV%\Scripts\pserve development.ini --reload
6772a2 492
3e6b60 493 .. note::
SP 494
495    Your OS firewall, if any, may pop up a dialog asking for authorization
496    to allow python to accept incoming network connections.
497
9591e9 498 If successful, you will see something like this on your console:
SP 499
500 .. code-block:: text
b284c6 501
b29848 502     Starting subprocess with file monitor
SP 503     Starting server in PID 44078.
504     Serving on http://localhost:6543
505     Serving on http://localhost:6543
b284c6 506
PP 507 This means the server is ready to accept requests.
508
8571f2 509
3e6b60 510 Visit the application in a browser
8571f2 511 ----------------------------------
3e6b60 512
9591e9 513 In a browser, visit http://localhost:6543/. You will see the generated
13846e 514 application's default page.
e26700 515
2c9f3c 516 One thing you'll notice is the "debug toolbar" icon on right hand side of the
CM 517 page.  You can read more about the purpose of the icon at
518 :ref:`debug_toolbar`.  It allows you to get information about your
519 application while you develop.
520
8571f2 521
f2520e 522 Decisions the cookiecutter backend option ``sqlalchemy`` has made for you
SM 523 -------------------------------------------------------------------------
e26700 524
f2520e 525 When creating a project and selecting the backend option of ``sqlalchemy``, the
SM 526 cookiecutter makes the following assumptions:
b2adfe 527
b29848 528 - You are willing to use SQLite for persistent storage, although almost any SQL database could be used with SQLAlchemy.
SP 529
530 - You are willing to use :term:`SQLAlchemy` for a database access tool.
b2adfe 531
49620d 532 - You are willing to use :term:`Alembic` for a database migrations tool.
SP 533
534 - You are willing to use a :term:`console script` for a data loading tool.
535
8d4571 536 - You are willing to use :term:`URL dispatch` to map URLs to code.
b2adfe 537
d8be5a 538 - You want to use zope.sqlalchemy_, pyramid_tm_, and the transaction_ packages
a7dd05 539   to scope sessions to requests.
e91085 540
b2adfe 541 .. note::
CM 542
ee9676 543    :app:`Pyramid` supports any persistent storage mechanism (e.g., object
8571f2 544    database or filesystem files). It also supports an additional mechanism to
SP 545    map URLs to code (:term:`traversal`). However, for the purposes of this
9591e9 546    tutorial, we'll only be using :term:`URL dispatch` and :term:`SQLAlchemy`.
8d4571 547
MM 548 .. _pyramid_jinja2:
19d341 549    https://docs.pylonsproject.org/projects/pyramid-jinja2/en/latest/
8d4571 550
MM 551 .. _pyramid_tm:
19d341 552    https://docs.pylonsproject.org/projects/pyramid-tm/en/latest/
8d4571 553
MM 554 .. _zope.sqlalchemy:
8bd6f7 555    https://pypi.org/project/zope.sqlalchemy/
8d4571 556
MM 557 .. _transaction:
a816a8 558    https://zodb.readthedocs.io/en/latest/transactions.html