From b29848f9d7b49715c1027c7361bb68e03707deae Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Fri, 23 Dec 2016 13:50:10 -0800 Subject: wiki2/*.rst first cut from comparing across branches --- docs/tutorials/wiki2/definingmodels.rst | 143 ++++++++++++++++---------------- 1 file changed, 70 insertions(+), 73 deletions(-) (limited to 'docs/tutorials/wiki2/definingmodels.rst') diff --git a/docs/tutorials/wiki2/definingmodels.rst b/docs/tutorials/wiki2/definingmodels.rst index 9f7b82d1d..7d4a7baea 100644 --- a/docs/tutorials/wiki2/definingmodels.rst +++ b/docs/tutorials/wiki2/definingmodels.rst @@ -4,7 +4,7 @@ Defining the Domain Model ========================= -The first change we'll make to our stock ``pcreate``-generated application will +The first change we'll make to our stock cookiecutter-generated application will be to define a wiki page :term:`domain model`. .. note:: @@ -22,10 +22,10 @@ Declaring dependencies in our ``setup.py`` file The models code in our application will depend on a package which is not a dependency of the original "tutorial" application. The original "tutorial" -application was generated by the ``pcreate`` command; it doesn't know about our +application was generated by the cookiecutter; it doesn't know about our custom application requirements. -We need to add a dependency, the ``bcrypt`` package, to our ``tutorial`` +We need to add a dependency, the `bcrypt `_ package, to our ``tutorial`` package's ``setup.py`` file by assigning this dependency to the ``requires`` parameter in the ``setup()`` function. @@ -38,6 +38,10 @@ Open ``tutorial/setup.py`` and edit it to look like the following: Only the highlighted line needs to be added. +.. note:: + + We are using the ``bcrypt`` package from PyPI to hash our passwords securely. There are other one-way hash algorithms for passwords if bcrypt is an issue on your system. Just make sure that it's an algorithm approved for storing passwords versus a generic one-way hash. + Running ``pip install -e .`` ============================ @@ -53,31 +57,31 @@ On UNIX: .. code-block:: bash - $ cd tutorial - $ $VENV/bin/pip install -e . + $ $VENV/bin/pip install -e . On Windows: .. code-block:: doscon - c:\pyramidtut> cd tutorial - c:\pyramidtut\tutorial> %VENV%\Scripts\pip install -e . + c:\tutorial> %VENV%\Scripts\pip install -e . Success executing this command will end with a line to the console something -like this:: +like the following. - Successfully installed bcrypt-2.0.0 cffi-1.5.2 pycparser-2.14 tutorial-0.0 +.. code-block:: text + + Successfully installed bcrypt-3.1.2 cffi-1.9.1 pycparser-2.17 tutorial Remove ``mymodel.py`` ---------------------- +===================== Let's delete the file ``tutorial/models/mymodel.py``. The ``MyModel`` class is only a sample and we're not going to use it. Add ``user.py`` ---------------- +=============== Create a new file ``tutorial/models/user.py`` with the following contents: @@ -98,12 +102,12 @@ and ``role`` (all instances of :class:`sqlalchemy.schema.Column`). These will map to columns in the ``users`` table. The ``id`` attribute will be the primary key in the table. The ``name`` attribute will be a text column, each value of which needs to be unique within the column. The ``password_hash`` is a nullable -text attribute that will contain a securely hashed password [1]_. Finally, the +text attribute that will contain a securely hashed password. Finally, the ``role`` text attribute will hold the role of the user. There are two helper methods that will help us later when using the user objects. The first is ``set_password`` which will take a raw password and -transform it using bcrypt_ into an irreversible representation, a process known +transform it using ``bcrypt`` into an irreversible representation, a process known as "hashing". The second method, ``check_password``, will allow us to compare the hashed value of the submitted password against the hashed value of the password stored in the user's record in the database. If the two hashed values @@ -116,7 +120,7 @@ authenticate as any user. Add ``page.py`` ---------------- +=============== Create a new file ``tutorial/models/page.py`` with the following contents: @@ -138,7 +142,7 @@ guaranteed that an instance of ``page`` will have a corresponding Edit ``models/__init__.py`` ---------------------------- +=========================== Since we are using a package for our models, we also need to update our ``__init__.py`` file to ensure that the models are attached to the metadata. @@ -155,12 +159,16 @@ Here we align our imports with the names of the models, ``Page`` and ``User``. Edit ``scripts/initializedb.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 ``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 [2]_. +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 ``initializedb.py`` script. In particular, we'll replace our import of @@ -180,7 +188,7 @@ Only the highlighted lines need to be changed. Installing the project and re-initializing the database -------------------------------------------------------- +======================================================= 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 @@ -191,53 +199,53 @@ Success will look something like this: .. code-block:: bash - 2016-05-22 04:12:09,226 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1 - 2016-05-22 04:12:09,226 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] () - 2016-05-22 04:12:09,226 INFO [sqlalchemy.engine.base.Engine:1192][MainThread] SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1 - 2016-05-22 04:12:09,227 INFO [sqlalchemy.engine.base.Engine:1193][MainThread] () - 2016-05-22 04:12:09,227 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] PRAGMA table_info("users") - 2016-05-22 04:12:09,227 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () - 2016-05-22 04:12:09,228 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] PRAGMA table_info("pages") - 2016-05-22 04:12:09,228 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () - 2016-05-22 04:12:09,229 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] - CREATE TABLE users ( - id INTEGER NOT NULL, - name TEXT NOT NULL, - role TEXT NOT NULL, - password_hash TEXT, - CONSTRAINT pk_users PRIMARY KEY (id), - CONSTRAINT uq_users_name UNIQUE (name) - ) - - - 2016-05-22 04:12:09,229 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () - 2016-05-22 04:12:09,230 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT - 2016-05-22 04:12:09,230 INFO [sqlalchemy.engine.base.Engine:1097][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) - ) - - - 2016-05-22 04:12:09,231 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] () - 2016-05-22 04:12:09,231 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT - 2016-05-22 04:12:09,782 INFO [sqlalchemy.engine.base.Engine:646][MainThread] BEGIN (implicit) - 2016-05-22 04:12:09,783 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] INSERT INTO users (name, role, password_hash) VALUES (?, ?, ?) - 2016-05-22 04:12:09,784 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ('editor', 'editor', b'$2b$12$K/WLVKRl5fMAb6UM58ueTetXlE3rlc5cRK5zFPimK598scXBR/xWC') - 2016-05-22 04:12:09,784 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] INSERT INTO users (name, role, password_hash) VALUES (?, ?, ?) - 2016-05-22 04:12:09,784 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ('basic', 'basic', b'$2b$12$JfwLyCJGv3t.RTSmIrh3B.FKXRT9FevkAqafWdK5oq7Hl4mgAQORe') - 2016-05-22 04:12:09,785 INFO [sqlalchemy.engine.base.Engine:1097][MainThread] INSERT INTO pages (name, data, creator_id) VALUES (?, ?, ?) - 2016-05-22 04:12:09,785 INFO [sqlalchemy.engine.base.Engine:1100][MainThread] ('FrontPage', 'This is the front page', 1) - 2016-05-22 04:12:09,786 INFO [sqlalchemy.engine.base.Engine:686][MainThread] COMMIT + 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] + CREATE TABLE users ( + id INTEGER NOT NULL, + name TEXT NOT NULL, + role TEXT NOT NULL, + password_hash TEXT, + CONSTRAINT pk_users PRIMARY KEY (id), + CONSTRAINT uq_users_name UNIQUE (name) + ) + + + 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] + 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) + ) + + + 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 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 @@ -250,14 +258,3 @@ your console that ends with this exception: ImportError: cannot import name MyModel This will also happen if you attempt to run the tests. - -.. _bcrypt: https://pypi.python.org/pypi/bcrypt - -.. [1] We are using the bcrypt_ package from PyPI to hash our passwords - securely. There are other one-way hash algorithms for passwords if - bcrypt is an issue on your system. Just make sure that it's an - algorithm approved for storing passwords versus a generic one-way hash. - -.. [2] 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. -- cgit v1.2.3