diff options
Diffstat (limited to 'docs/tutorials/wiki2/definingmodels.rst')
| -rw-r--r-- | docs/tutorials/wiki2/definingmodels.rst | 208 |
1 files changed, 152 insertions, 56 deletions
diff --git a/docs/tutorials/wiki2/definingmodels.rst b/docs/tutorials/wiki2/definingmodels.rst index fd1967d99..c22fd2b61 100644 --- a/docs/tutorials/wiki2/definingmodels.rst +++ b/docs/tutorials/wiki2/definingmodels.rst @@ -33,7 +33,7 @@ Open ``tutorial/setup.py`` and edit it to look like the following: .. literalinclude:: src/models/setup.py :linenos: - :emphasize-lines: 12 + :emphasize-lines: 13 :language: python Only the highlighted line needs to be added. @@ -70,7 +70,7 @@ like the following. .. code-block:: text - Successfully installed bcrypt-3.1.2 cffi-1.9.1 pycparser-2.17 tutorial + Successfully installed bcrypt-3.1.4 cffi-1.11.5 pycparser-2.18 tutorial Remove ``mymodel.py`` @@ -158,56 +158,76 @@ the following: Here we align our imports with the names of the models, ``Page`` and ``User``. -Edit ``scripts/initializedb.py`` -================================ +.. _wiki2_migrate_database_alembic: -We haven't looked at the details of this file yet, but within the ``scripts`` -directory of your ``tutorial`` package is a file named ``initializedb.py``. -Code in this file is executed whenever we run the ``initialize_tutorial_db`` -command, as we did in the installation step of this tutorial. +Migrate the database with Alembic +================================= -.. note:: +Now that we have written our models, we need to modify the database schema to reflect the changes to our code. Let's generate a new revision, then upgrade the database to the latest revision (head). - The command is named ``initialize_tutorial_db`` because of the mapping defined in the ``[console_scripts]`` entry point of our project's ``setup.py`` file. +On UNIX: -Since we've changed our model, we need to make changes to our -``initializedb.py`` script. In particular, we'll replace our import of -``MyModel`` with those of ``User`` and ``Page``. We'll also change the very end -of the script to create two ``User`` objects (``basic`` and ``editor``) as well -as a ``Page``, rather than a ``MyModel``, and add them to our ``dbsession``. +.. code-block:: bash -Open ``tutorial/scripts/initializedb.py`` and edit it to look like the -following: + $ $VENV/bin/alembic -c development.ini revision --autogenerate \ + -m "use new models Page and User" + $ $VENV/bin/alembic -c development.ini upgrade head -.. literalinclude:: src/models/tutorial/scripts/initializedb.py - :linenos: - :language: python - :emphasize-lines: 18,44-57 +On Windows: -Only the highlighted lines need to be changed. +.. code-block:: doscon + c:\tutorial> %VENV%\Scripts\alembic -c development.ini revision \ + --autogenerate -m "use new models Page and User" + c:\tutorial> %VENV%\Scripts\alembic -c development.ini upgrade head -Installing the project and re-initializing the database -======================================================= +Success executing these commands will generate output similar to the following. -Because our model has changed, and in order to reinitialize the database, we -need to rerun the ``initialize_tutorial_db`` command to pick up the changes -we've made to both the models.py file and to the initializedb.py file. See -:ref:`initialize_db_wiki2` for instructions. +.. code-block:: text -Success will look something like this: + 2018-06-29 01:28:42,407 INFO [sqlalchemy.engine.base.Engine:1254][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1 + 2018-06-29 01:28:42,407 INFO [sqlalchemy.engine.base.Engine:1255][MainThread] () + 2018-06-29 01:28:42,408 INFO [sqlalchemy.engine.base.Engine:1254][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1 + 2018-06-29 01:28:42,408 INFO [sqlalchemy.engine.base.Engine:1255][MainThread] () + 2018-06-29 01:28:42,409 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] PRAGMA table_info("alembic_version") + 2018-06-29 01:28:42,409 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + 2018-06-29 01:28:42,410 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] SELECT alembic_version.version_num + FROM alembic_version + 2018-06-29 01:28:42,410 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + 2018-06-29 01:28:42,411 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] SELECT name FROM sqlite_master WHERE type='table' ORDER BY name + 2018-06-29 01:28:42,412 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + 2018-06-29 01:28:42,413 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] PRAGMA table_info("models") + 2018-06-29 01:28:42,413 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + 2018-06-29 01:28:42,414 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE name = 'models' AND type = 'table' + 2018-06-29 01:28:42,414 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + 2018-06-29 01:28:42,414 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] PRAGMA foreign_key_list("models") + 2018-06-29 01:28:42,414 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + 2018-06-29 01:28:42,414 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE name = 'models' AND type = 'table' + 2018-06-29 01:28:42,415 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + 2018-06-29 01:28:42,416 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] PRAGMA index_list("models") + 2018-06-29 01:28:42,416 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + 2018-06-29 01:28:42,416 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] PRAGMA index_info("my_index") + 2018-06-29 01:28:42,416 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + 2018-06-29 01:28:42,417 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] PRAGMA index_list("models") + 2018-06-29 01:28:42,417 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + 2018-06-29 01:28:42,417 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] PRAGMA index_info("my_index") + 2018-06-29 01:28:42,417 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + 2018-06-29 01:28:42,417 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] SELECT sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE name = 'models' AND type = 'table' + 2018-06-29 01:28:42,417 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + Generating /<somepath>/tutorial/tutorial/alembic/versions/20180629_23e9f8eb6c28.py ... done -.. code-block:: bash +.. code-block:: text - 2016-12-20 02:51:11,195 INFO [sqlalchemy.engine.base.Engine:1235][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1 - 2016-12-20 02:51:11,195 INFO [sqlalchemy.engine.base.Engine:1236][MainThread] () - 2016-12-20 02:51:11,195 INFO [sqlalchemy.engine.base.Engine:1235][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1 - 2016-12-20 02:51:11,195 INFO [sqlalchemy.engine.base.Engine:1236][MainThread] () - 2016-12-20 02:51:11,196 INFO [sqlalchemy.engine.base.Engine:1140][MainThread] PRAGMA table_info("pages") - 2016-12-20 02:51:11,196 INFO [sqlalchemy.engine.base.Engine:1143][MainThread] () - 2016-12-20 02:51:11,196 INFO [sqlalchemy.engine.base.Engine:1140][MainThread] PRAGMA table_info("users") - 2016-12-20 02:51:11,197 INFO [sqlalchemy.engine.base.Engine:1143][MainThread] () - 2016-12-20 02:51:11,197 INFO [sqlalchemy.engine.base.Engine:1140][MainThread] + 2018-06-29 01:29:37,957 INFO [sqlalchemy.engine.base.Engine:1254][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1 + 2018-06-29 01:29:37,958 INFO [sqlalchemy.engine.base.Engine:1255][MainThread] () + 2018-06-29 01:29:37,958 INFO [sqlalchemy.engine.base.Engine:1254][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1 + 2018-06-29 01:29:37,958 INFO [sqlalchemy.engine.base.Engine:1255][MainThread] () + 2018-06-29 01:29:37,960 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] PRAGMA table_info("alembic_version") + 2018-06-29 01:29:37,960 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + 2018-06-29 01:29:37,960 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] SELECT alembic_version.version_num + FROM alembic_version + 2018-06-29 01:29:37,960 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + 2018-06-29 01:29:37,963 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] CREATE TABLE users ( id INTEGER NOT NULL, name TEXT NOT NULL, @@ -218,30 +238,106 @@ Success will look something like this: ) - 2016-12-20 02:51:11,197 INFO [sqlalchemy.engine.base.Engine:1143][MainThread] () - 2016-12-20 02:51:11,198 INFO [sqlalchemy.engine.base.Engine:719][MainThread] COMMIT - 2016-12-20 02:51:11,199 INFO [sqlalchemy.engine.base.Engine:1140][MainThread] + 2018-06-29 01:29:37,963 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + 2018-06-29 01:29:37,966 INFO [sqlalchemy.engine.base.Engine:722][MainThread] COMMIT + 2018-06-29 01:29:37,968 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] CREATE TABLE pages ( id INTEGER NOT NULL, name TEXT NOT NULL, data TEXT NOT NULL, creator_id INTEGER NOT NULL, CONSTRAINT pk_pages PRIMARY KEY (id), - CONSTRAINT uq_pages_name UNIQUE (name), - CONSTRAINT fk_pages_creator_id_users FOREIGN KEY(creator_id) REFERENCES users (id) + CONSTRAINT fk_pages_creator_id_users FOREIGN KEY(creator_id) REFERENCES users (id), + CONSTRAINT uq_pages_name UNIQUE (name) ) - 2016-12-20 02:51:11,199 INFO [sqlalchemy.engine.base.Engine:1143][MainThread] () - 2016-12-20 02:51:11,200 INFO [sqlalchemy.engine.base.Engine:719][MainThread] COMMIT - 2016-12-20 02:51:11,755 INFO [sqlalchemy.engine.base.Engine:679][MainThread] BEGIN (implicit) - 2016-12-20 02:51:11,755 INFO [sqlalchemy.engine.base.Engine:1140][MainThread] INSERT INTO users (name, role, password_hash) VALUES (?, ?, ?) - 2016-12-20 02:51:11,755 INFO [sqlalchemy.engine.base.Engine:1143][MainThread] ('editor', 'editor', '$2b$12$ds7h2Zb7.l6TEFup5h8f4ekA9GRfEpE1yQGDRvT9PConw73kKuupG') - 2016-12-20 02:51:11,756 INFO [sqlalchemy.engine.base.Engine:1140][MainThread] INSERT INTO users (name, role, password_hash) VALUES (?, ?, ?) - 2016-12-20 02:51:11,756 INFO [sqlalchemy.engine.base.Engine:1143][MainThread] ('basic', 'basic', '$2b$12$KgruXP5Vv7rikr6dGB3TF.flGXYpiE0Li9K583EVomjY.SYmQOsyi') - 2016-12-20 02:51:11,757 INFO [sqlalchemy.engine.base.Engine:1140][MainThread] INSERT INTO pages (name, data, creator_id) VALUES (?, ?, ?) - 2016-12-20 02:51:11,757 INFO [sqlalchemy.engine.base.Engine:1143][MainThread] ('FrontPage', 'This is the front page', 1) - 2016-12-20 02:51:11,757 INFO [sqlalchemy.engine.base.Engine:719][MainThread] COMMIT + 2018-06-29 01:29:37,968 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + 2018-06-29 01:29:37,969 INFO [sqlalchemy.engine.base.Engine:722][MainThread] COMMIT + 2018-06-29 01:29:37,969 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] + DROP INDEX my_index + 2018-06-29 01:29:37,969 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + 2018-06-29 01:29:37,970 INFO [sqlalchemy.engine.base.Engine:722][MainThread] COMMIT + 2018-06-29 01:29:37,970 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] + DROP TABLE models + 2018-06-29 01:29:37,970 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + 2018-06-29 01:29:37,971 INFO [sqlalchemy.engine.base.Engine:722][MainThread] COMMIT + 2018-06-29 01:29:37,972 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] UPDATE alembic_version SET version_num='23e9f8eb6c28' WHERE alembic_version.version_num = 'b6b22ae3e628' + 2018-06-29 01:29:37,972 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] () + 2018-06-29 01:29:37,972 INFO [sqlalchemy.engine.base.Engine:722][MainThread] COMMIT + + +.. _wiki2_alembic_overview: + +Alembic overview +---------------- + +Let's briefly discuss our configuration for Alembic. + +In the alchemy cookiecutter's ``development.ini`` file, the setting for ``script_location`` configures Alembic to look for the migration script in the directory ``tutorial/alembic``. +By default Alembic stores the migration files one level deeper in ``tutorial/alembic/versions``. +These files are generated by Alembic, then executed when we run upgrade or downgrade migrations. +The setting ``file_template`` provides the format for each migration's file name. +We've configured the ``file_template`` setting to make it somewhat easy to find migrations by file name. + +At this point in this tutorial, we have two migration files. +Examine them to see what Alembic will do when you upgrade or downgrade the database to a specific revision. +Notice the revision identifiers and how they relate to one another in a chained sequence. + +.. seealso:: For further information, see the `Alembic documentation <http://alembic.zzzcomputing.com/en/latest/>`_. + + +Edit ``scripts/initialize_db.py`` +================================= + +We haven't looked at the details of this file yet, but within the ``scripts`` +directory of your ``tutorial`` package is a file named ``initialize_db.py``. +Code in this file is executed whenever we run the ``initialize_tutorial_db`` +command, as we did in the installation step of this tutorial. + +.. note:: + + The command is named ``initialize_tutorial_db`` because of the mapping defined in the ``[console_scripts]`` entry point of our project's ``setup.py`` file. + +Since we've changed our model, we need to make changes to our +``initialize_db.py`` script. In particular, we'll replace our import of +``MyModel`` with those of ``User`` and ``Page``. We'll also change the the script to create two ``User`` objects (``basic`` and ``editor``) as well +as a ``Page``, rather than a ``MyModel``, and add them to our ``dbsession``. + +Open ``tutorial/scripts/initialize_db.py`` and edit it to look like the +following: + +.. literalinclude:: src/models/tutorial/scripts/initialize_db.py + :linenos: + :language: python + :emphasize-lines: 11-24 + +Only the highlighted lines need to be changed. + + +Populating the database +======================= + +Because our model has changed, and to repopulate the database, we +need to rerun the ``initialize_tutorial_db`` command to pick up the changes +we've made to the initialize_db.py file. See :ref:`initialize_db_wiki2` for instructions. + +Success will look something like this: + +.. code-block:: text + + 2018-06-29 01:30:39,326 INFO [sqlalchemy.engine.base.Engine:1254][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1 + 2018-06-29 01:30:39,326 INFO [sqlalchemy.engine.base.Engine:1255][MainThread] () + 2018-06-29 01:30:39,327 INFO [sqlalchemy.engine.base.Engine:1254][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1 + 2018-06-29 01:30:39,327 INFO [sqlalchemy.engine.base.Engine:1255][MainThread] () + 2018-06-29 01:30:39,328 INFO [sqlalchemy.engine.base.Engine:682][MainThread] BEGIN (implicit) + 2018-06-29 01:30:39,329 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] INSERT INTO users (name, role, password_hash) VALUES (?, ?, ?) + 2018-06-29 01:30:39,329 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] ('editor', 'editor', '$2b$12$PlaJSN7goVbyx8OFs8yAju9n5gHGdI6PZ2QRJGM2jDCiEU4ItUNxy') + 2018-06-29 01:30:39,330 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] INSERT INTO users (name, role, password_hash) VALUES (?, ?, ?) + 2018-06-29 01:30:39,330 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] ('basic', 'basic', '$2b$12$MvXdM8jlkbjEyPZ6uXzRg.yatZZK8jCwfPaM7kFkmVJiJjRoCCvmW') + 2018-06-29 01:30:39,331 INFO [sqlalchemy.engine.base.Engine:1151][MainThread] INSERT INTO pages (name, data, creator_id) VALUES (?, ?, ?) + 2018-06-29 01:30:39,331 INFO [sqlalchemy.engine.base.Engine:1154][MainThread] ('FrontPage', 'This is the front page', 1) + 2018-06-29 01:30:39,332 INFO [sqlalchemy.engine.base.Engine:722][MainThread] COMMIT View the application in a browser @@ -250,11 +346,11 @@ View the application in a browser We can't. At this point, our system is in a "non-runnable" state; we'll need to change view-related files in the next chapter to be able to start the application successfully. If you try to start the application (see -:ref:`wiki2-start-the-application`), you'll wind up with a Python traceback on +:ref:`wiki2-start-the-application`) and visit http://localhost:6543, you'll wind up with a Python traceback on your console that ends with this exception: .. code-block:: text - ImportError: cannot import name MyModel + AttributeError: module 'tutorial.models' has no attribute 'MyModel' This will also happen if you attempt to run the tests. |
