summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2024-02-04 16:58:08 -0700
committerMichael Merickel <michael@merickel.org>2024-02-04 16:58:08 -0700
commit3c61799661ea5c36ae97326574a3dc7a695ce6aa (patch)
treeae0ee64967811a6c453b7f8f47c0041fd2a544c8
parent286a7a3ee8705b28fb82252daa37896d63ae6741 (diff)
downloadpyramid-3c61799661ea5c36ae97326574a3dc7a695ce6aa.tar.gz
pyramid-3c61799661ea5c36ae97326574a3dc7a695ce6aa.tar.bz2
pyramid-3c61799661ea5c36ae97326574a3dc7a695ce6aa.zip
update authorization chapter
-rw-r--r--docs/tutorials/wiki2/authorization.rst16
-rw-r--r--docs/tutorials/wiki2/src/authorization/.coveragerc2
-rw-r--r--docs/tutorials/wiki2/src/authorization/CHANGES.txt4
-rw-r--r--docs/tutorials/wiki2/src/authorization/MANIFEST.in2
-rw-r--r--docs/tutorials/wiki2/src/authorization/README.md60
-rw-r--r--docs/tutorials/wiki2/src/authorization/README.txt44
-rw-r--r--docs/tutorials/wiki2/src/authorization/development.ini8
-rw-r--r--docs/tutorials/wiki2/src/authorization/production.ini8
-rw-r--r--docs/tutorials/wiki2/src/authorization/pyproject.toml60
-rw-r--r--docs/tutorials/wiki2/src/authorization/pytest.ini6
-rw-r--r--docs/tutorials/wiki2/src/authorization/setup.py63
-rw-r--r--docs/tutorials/wiki2/src/authorization/testing.ini8
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/alembic/versions/20240204_07f9d6b626b2.py52
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/alembic/versions/20240204_4b6614165904.py33
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py28
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/models/page.py20
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/models/user.py17
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/routes.py10
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/security.py2
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py9
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/views/default.py8
21 files changed, 274 insertions, 186 deletions
diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst
index e0c59a5b6..84b05b863 100644
--- a/docs/tutorials/wiki2/authorization.rst
+++ b/docs/tutorials/wiki2/authorization.rst
@@ -89,7 +89,7 @@ Open the file ``tutorial/routes.py`` and edit the following lines:
.. literalinclude:: src/authorization/tutorial/routes.py
:linenos:
- :emphasize-lines: 1-11,18-
+ :emphasize-lines: 1-11,19-
:language: python
The highlighted lines need to be edited or added.
@@ -101,7 +101,7 @@ the principals of either ``role:editor`` or ``role:basic`` to have the
``create`` permission:
.. literalinclude:: src/authorization/tutorial/routes.py
- :lines: 31-39
+ :lines: 35-43
:lineno-match:
:emphasize-lines: 5-9
:language: python
@@ -110,7 +110,7 @@ The ``NewPage`` is loaded as the :term:`context` of the ``add_page`` route by
declaring a ``factory`` on the route:
.. literalinclude:: src/authorization/tutorial/routes.py
- :lines: 19-20
+ :lines: 20-21
:lineno-match:
:emphasize-lines: 1-2
:language: python
@@ -119,7 +119,7 @@ The ``PageResource`` class defines the :term:`ACL` for a ``Page``. It uses an
actual ``Page`` object to determine *who* can do *what* to the page.
.. literalinclude:: src/authorization/tutorial/routes.py
- :lines: 48-
+ :lines: 54-
:lineno-match:
:emphasize-lines: 5-10
:language: python
@@ -128,7 +128,7 @@ The ``PageResource`` is loaded as the :term:`context` of the ``view_page`` and
``edit_page`` routes by declaring a ``factory`` on the routes:
.. literalinclude:: src/authorization/tutorial/routes.py
- :lines: 18-22
+ :lines: 19-23
:lineno-match:
:emphasize-lines: 1,4-5
:language: python
@@ -157,7 +157,7 @@ Edit the ``view_page`` view to declare the ``view`` permission, and remove the
explicit checks within the view:
.. literalinclude:: src/authorization/tutorial/views/default.py
- :lines: 18-23
+ :lines: 19-24
:lineno-match:
:emphasize-lines: 1-2,4
:language: python
@@ -171,7 +171,7 @@ the view logic.
Edit the ``edit_page`` view to declare the ``edit`` permission:
.. literalinclude:: src/authorization/tutorial/views/default.py
- :lines: 38-42
+ :lines: 41-45
:lineno-match:
:emphasize-lines: 1-2,4
:language: python
@@ -179,7 +179,7 @@ Edit the ``edit_page`` view to declare the ``edit`` permission:
Edit the ``add_page`` view to declare the ``create`` permission:
.. literalinclude:: src/authorization/tutorial/views/default.py
- :lines: 52-56
+ :lines: 55-59
:lineno-match:
:emphasize-lines: 1-2,4
:language: python
diff --git a/docs/tutorials/wiki2/src/authorization/.coveragerc b/docs/tutorials/wiki2/src/authorization/.coveragerc
deleted file mode 100644
index 5db0e79cf..000000000
--- a/docs/tutorials/wiki2/src/authorization/.coveragerc
+++ /dev/null
@@ -1,2 +0,0 @@
-[run]
-source = tutorial
diff --git a/docs/tutorials/wiki2/src/authorization/CHANGES.txt b/docs/tutorials/wiki2/src/authorization/CHANGES.txt
deleted file mode 100644
index 14b902fd1..000000000
--- a/docs/tutorials/wiki2/src/authorization/CHANGES.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-0.0
----
-
-- Initial version.
diff --git a/docs/tutorials/wiki2/src/authorization/MANIFEST.in b/docs/tutorials/wiki2/src/authorization/MANIFEST.in
index b4624fd1c..201692c1b 100644
--- a/docs/tutorials/wiki2/src/authorization/MANIFEST.in
+++ b/docs/tutorials/wiki2/src/authorization/MANIFEST.in
@@ -1,4 +1,4 @@
-include *.txt *.ini *.cfg *.rst
+include *.txt *.ini *.cfg *.rst *.toml
recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml *.jinja2
recursive-include tests *
recursive-exclude * __pycache__
diff --git a/docs/tutorials/wiki2/src/authorization/README.md b/docs/tutorials/wiki2/src/authorization/README.md
new file mode 100644
index 000000000..5ac1209d0
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/README.md
@@ -0,0 +1,60 @@
+# myproj
+
+## Getting Started
+
+- Change directory into your newly created project if not already there. Your
+ current directory should be the same as this `README.md` file and `pyproject.toml`.
+
+ ```
+ cd tutorial
+ ```
+
+- Create a Python virtual environment, if not already created.
+
+ ```
+ python3 -m venv env
+ ```
+
+- Upgrade packaging tools, if necessary.
+
+ ```
+ env/bin/pip install --upgrade pip
+ ```
+
+- Install the project in editable mode with its testing requirements.
+
+ ```
+ env/bin/pip install -e ".[testing]"
+ ```
+
+- Initialize and upgrade the database using Alembic.
+
+ - Generate your first revision.
+
+ ```
+ env/bin/alembic -c development.ini revision --autogenerate -m "init"
+ ```
+
+ - Upgrade to that revision.
+
+ ```
+ env/bin/alembic -c development.ini upgrade head
+ ```
+
+- Load default data into the database using a script.
+
+ ```
+ env/bin/initialize_tutorial_db development.ini
+ ```
+
+- Run your project's tests.
+
+ ```
+ env/bin/pytest
+ ```
+
+- Run your project.
+
+ ```
+ env/bin/pserve development.ini
+ ```
diff --git a/docs/tutorials/wiki2/src/authorization/README.txt b/docs/tutorials/wiki2/src/authorization/README.txt
deleted file mode 100644
index ed6b88b49..000000000
--- a/docs/tutorials/wiki2/src/authorization/README.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-myproj
-======
-
-Getting Started
----------------
-
-- Change directory into your newly created project if not already there. Your
- current directory should be the same as this README.txt file and setup.py.
-
- cd tutorial
-
-- Create a Python virtual environment, if not already created.
-
- python3 -m venv env
-
-- Upgrade packaging tools, if necessary.
-
- env/bin/pip install --upgrade pip setuptools
-
-- Install the project in editable mode with its testing requirements.
-
- env/bin/pip install -e ".[testing]"
-
-- Initialize and upgrade the database using Alembic.
-
- - Generate your first revision.
-
- env/bin/alembic -c development.ini revision --autogenerate -m "init"
-
- - Upgrade to that revision.
-
- env/bin/alembic -c development.ini upgrade head
-
-- Load default data into the database using a script.
-
- env/bin/initialize_tutorial_db development.ini
-
-- Run your project's tests.
-
- env/bin/pytest
-
-- Run your project.
-
- env/bin/pserve development.ini
diff --git a/docs/tutorials/wiki2/src/authorization/development.ini b/docs/tutorials/wiki2/src/authorization/development.ini
index 7fda4cb7b..7dcb83da0 100644
--- a/docs/tutorials/wiki2/src/authorization/development.ini
+++ b/docs/tutorials/wiki2/src/authorization/development.ini
@@ -27,16 +27,16 @@ auth.secret = seekrit
[pshell]
setup = tutorial.pshell.setup
-###
-# wsgi server configuration
-###
-
[alembic]
# path to migration scripts
script_location = tutorial/alembic
file_template = %%(year)d%%(month).2d%%(day).2d_%%(rev)s
# file_template = %%(rev)s_%%(slug)s
+###
+# wsgi server configuration
+###
+
[server:main]
use = egg:waitress#main
listen = localhost:6543
diff --git a/docs/tutorials/wiki2/src/authorization/production.ini b/docs/tutorials/wiki2/src/authorization/production.ini
index 8e878a707..b4c18b119 100644
--- a/docs/tutorials/wiki2/src/authorization/production.ini
+++ b/docs/tutorials/wiki2/src/authorization/production.ini
@@ -21,16 +21,16 @@ auth.secret = real-seekrit
[pshell]
setup = tutorial.pshell.setup
-###
-# wsgi server configuration
-###
-
[alembic]
# path to migration scripts
script_location = tutorial/alembic
file_template = %%(year)d%%(month).2d%%(day).2d_%%(rev)s
# file_template = %%(rev)s_%%(slug)s
+###
+# wsgi server configuration
+###
+
[server:main]
use = egg:waitress#main
listen = *:6543
diff --git a/docs/tutorials/wiki2/src/authorization/pyproject.toml b/docs/tutorials/wiki2/src/authorization/pyproject.toml
new file mode 100644
index 000000000..2b054c578
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/pyproject.toml
@@ -0,0 +1,60 @@
+[build-system]
+requires = ["setuptools"]
+build-backend = "setuptools.build_meta"
+
+[project]
+version = "0.0"
+name = "tutorial"
+authors = []
+description = "myproj"
+readme = "README.md"
+keywords = ["web", "pyramid", "pylons"]
+classifiers = [
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3",
+ "Framework :: Pyramid",
+ "Topic :: Internet :: WWW/HTTP",
+ "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
+]
+requires-python = ">=3.8"
+dependencies = [
+ "alembic",
+ "bcrypt",
+ "docutils",
+ "plaster_pastedeploy",
+ "pyramid",
+ "pyramid_debugtoolbar",
+ "pyramid_jinja2",
+ "pyramid_retry",
+ "pyramid_tm",
+ "SQLAlchemy",
+ "transaction",
+ "waitress",
+ "zope.sqlalchemy",
+]
+
+[project.optional-dependencies]
+testing = [
+ "WebTest",
+ "pytest",
+ "pytest-cov",
+]
+
+[project.scripts]
+initialize_tutorial_db = "tutorial.scripts.initialize_db:main"
+
+[project.entry-points."paste.app_factory"]
+main = "tutorial:main"
+
+[tool.setuptools.packages.find]
+exclude = ["tests"]
+
+[tool.coverage.run]
+source = "tutorial"
+
+[tool.pytest.ini_options]
+addopts = "--strict-markers"
+testpaths = [
+ "tutorial",
+ "tests",
+]
diff --git a/docs/tutorials/wiki2/src/authorization/pytest.ini b/docs/tutorials/wiki2/src/authorization/pytest.ini
deleted file mode 100644
index 3df78fe9d..000000000
--- a/docs/tutorials/wiki2/src/authorization/pytest.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[pytest]
-addopts = --strict-markers
-
-testpaths =
- tutorial
- tests
diff --git a/docs/tutorials/wiki2/src/authorization/setup.py b/docs/tutorials/wiki2/src/authorization/setup.py
deleted file mode 100644
index 12eabaff2..000000000
--- a/docs/tutorials/wiki2/src/authorization/setup.py
+++ /dev/null
@@ -1,63 +0,0 @@
-import os
-
-from setuptools import setup, find_packages
-
-here = os.path.abspath(os.path.dirname(__file__))
-with open(os.path.join(here, 'README.txt')) as f:
- README = f.read()
-with open(os.path.join(here, 'CHANGES.txt')) as f:
- CHANGES = f.read()
-
-requires = [
- 'alembic',
- 'bcrypt',
- 'docutils',
- 'plaster_pastedeploy',
- 'pyramid',
- 'pyramid_debugtoolbar',
- 'pyramid_jinja2',
- 'pyramid_retry',
- 'pyramid_tm',
- 'SQLAlchemy',
- 'transaction',
- 'waitress',
- 'zope.sqlalchemy',
-]
-
-tests_require = [
- 'WebTest',
- 'pytest',
- 'pytest-cov',
-]
-
-setup(
- name='tutorial',
- version='0.0',
- description='myproj',
- long_description=README + '\n\n' + CHANGES,
- classifiers=[
- 'Programming Language :: Python',
- 'Framework :: Pyramid',
- 'Topic :: Internet :: WWW/HTTP',
- 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application',
- ],
- author='',
- author_email='',
- url='',
- keywords='web pyramid pylons',
- packages=find_packages(exclude=['tests']),
- include_package_data=True,
- zip_safe=False,
- extras_require={
- 'testing': tests_require,
- },
- install_requires=requires,
- entry_points={
- 'paste.app_factory': [
- 'main = tutorial:main',
- ],
- 'console_scripts': [
- 'initialize_tutorial_db=tutorial.scripts.initialize_db:main',
- ],
- },
-)
diff --git a/docs/tutorials/wiki2/src/authorization/testing.ini b/docs/tutorials/wiki2/src/authorization/testing.ini
index d3c601f16..095666d67 100644
--- a/docs/tutorials/wiki2/src/authorization/testing.ini
+++ b/docs/tutorials/wiki2/src/authorization/testing.ini
@@ -21,16 +21,16 @@ auth.secret = test-seekrit
[pshell]
setup = tutorial.pshell.setup
-###
-# wsgi server configuration
-###
-
[alembic]
# path to migration scripts
script_location = tutorial/alembic
file_template = %%(year)d%%(month).2d%%(day).2d_%%(rev)s
# file_template = %%(rev)s_%%(slug)s
+###
+# wsgi server configuration
+###
+
[server:main]
use = egg:waitress#main
listen = localhost:6543
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/alembic/versions/20240204_07f9d6b626b2.py b/docs/tutorials/wiki2/src/authorization/tutorial/alembic/versions/20240204_07f9d6b626b2.py
new file mode 100644
index 000000000..329c00208
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/alembic/versions/20240204_07f9d6b626b2.py
@@ -0,0 +1,52 @@
+"""use new models Page and User
+
+Revision ID: 07f9d6b626b2
+Revises: 4b6614165904
+Create Date: 2024-02-04 14:39:12.885858
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '07f9d6b626b2'
+down_revision = '4b6614165904'
+branch_labels = None
+depends_on = None
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('users',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('name', sa.String(), nullable=False),
+ sa.Column('role', sa.String(), nullable=False),
+ sa.Column('password_hash', sa.String(), nullable=True),
+ sa.PrimaryKeyConstraint('id', name=op.f('pk_users')),
+ sa.UniqueConstraint('name', name=op.f('uq_users_name'))
+ )
+ op.create_table('pages',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('name', sa.String(), nullable=False),
+ sa.Column('data', sa.String(), nullable=False),
+ sa.Column('creator_id', sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(['creator_id'], ['users.id'], name=op.f('fk_pages_creator_id_users')),
+ sa.PrimaryKeyConstraint('id', name=op.f('pk_pages')),
+ sa.UniqueConstraint('name', name=op.f('uq_pages_name'))
+ )
+ op.drop_index('my_index', table_name='models')
+ op.drop_table('models')
+ # ### end Alembic commands ###
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('models',
+ sa.Column('id', sa.INTEGER(), nullable=False),
+ sa.Column('name', sa.VARCHAR(), nullable=True),
+ sa.Column('value', sa.INTEGER(), nullable=True),
+ sa.PrimaryKeyConstraint('id', name='pk_models')
+ )
+ op.create_index('my_index', 'models', ['name'], unique=1)
+ op.drop_table('pages')
+ op.drop_table('users')
+ # ### end Alembic commands ###
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/alembic/versions/20240204_4b6614165904.py b/docs/tutorials/wiki2/src/authorization/tutorial/alembic/versions/20240204_4b6614165904.py
new file mode 100644
index 000000000..4f7a97d30
--- /dev/null
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/alembic/versions/20240204_4b6614165904.py
@@ -0,0 +1,33 @@
+"""init
+
+Revision ID: 4b6614165904
+Revises:
+Create Date: 2024-02-04 14:32:46.784813
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '4b6614165904'
+down_revision = None
+branch_labels = None
+depends_on = None
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('models',
+ sa.Column('id', sa.Integer(), nullable=False),
+ sa.Column('name', sa.String(), nullable=True),
+ sa.Column('value', sa.Integer(), nullable=True),
+ sa.PrimaryKeyConstraint('id', name=op.f('pk_models'))
+ )
+ op.create_index('my_index', 'models', ['name'], unique=True, mysql_length=255)
+ # ### end Alembic commands ###
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_index('my_index', table_name='models', mysql_length=255)
+ op.drop_table('models')
+ # ### end Alembic commands ###
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py
index d659c7857..38712bfd2 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py
@@ -1,16 +1,16 @@
-from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.schema import MetaData
+from sqlalchemy import MetaData
+from sqlalchemy.orm import DeclarativeBase
-# Recommended naming convention used by Alembic, as various different database
-# providers will autogenerate vastly different names making migrations more
-# difficult. See: https://alembic.sqlalchemy.org/en/latest/naming.html
-NAMING_CONVENTION = {
- "ix": "ix_%(column_0_label)s",
- "uq": "uq_%(table_name)s_%(column_0_name)s",
- "ck": "ck_%(table_name)s_%(constraint_name)s",
- "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
- "pk": "pk_%(table_name)s"
-}
-metadata = MetaData(naming_convention=NAMING_CONVENTION)
-Base = declarative_base(metadata=metadata)
+class Base(DeclarativeBase):
+ # Recommended naming convention used by Alembic, as various different
+ # database providers will autogenerate vastly different names making
+ # migrations more difficult.
+ # See: https://alembic.sqlalchemy.org/en/latest/naming.html
+ metadata = MetaData(naming_convention={
+ "ix": "ix_%(column_0_label)s",
+ "uq": "uq_%(table_name)s_%(column_0_name)s",
+ "ck": "ck_%(table_name)s_`%(constraint_name)s`",
+ "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
+ "pk": "pk_%(table_name)s"
+ })
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/page.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/page.py
index 74ff1faf8..6cc9aae3c 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/models/page.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/page.py
@@ -1,10 +1,6 @@
-from sqlalchemy import (
- Column,
- ForeignKey,
- Integer,
- Text,
-)
-from sqlalchemy.orm import relationship
+import bcrypt
+from sqlalchemy import ForeignKey
+from sqlalchemy.orm import Mapped, mapped_column, relationship
from .meta import Base
@@ -12,9 +8,9 @@ from .meta import Base
class Page(Base):
""" The SQLAlchemy declarative model class for a Page object. """
__tablename__ = 'pages'
- id = Column(Integer, primary_key=True)
- name = Column(Text, nullable=False, unique=True)
- data = Column(Text, nullable=False)
+ id: Mapped[int] = mapped_column(primary_key=True)
+ name: Mapped[str] = mapped_column(unique=True)
+ data: Mapped[str]
- creator_id = Column(ForeignKey('users.id'), nullable=False)
- creator = relationship('User', backref='created_pages')
+ creator_id: Mapped[int] = mapped_column(ForeignKey('users.id'))
+ creator: Mapped['User'] = relationship(back_populates='created_pages')
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py
index 9228b48f7..926a66e64 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py
@@ -1,9 +1,6 @@
import bcrypt
-from sqlalchemy import (
- Column,
- Integer,
- Text,
-)
+from sqlalchemy.orm import Mapped, mapped_column, relationship
+from typing import List, Optional
from .meta import Base
@@ -11,11 +8,11 @@ from .meta import Base
class User(Base):
""" The SQLAlchemy declarative model class for a User object. """
__tablename__ = 'users'
- id = Column(Integer, primary_key=True)
- name = Column(Text, nullable=False, unique=True)
- role = Column(Text, nullable=False)
+ id: Mapped[int] = mapped_column(primary_key=True)
+ name: Mapped[str] = mapped_column(unique=True)
+ role: Mapped[str]
- password_hash = Column(Text)
+ password_hash: Mapped[Optional[str]]
def set_password(self, pw):
pwhash = bcrypt.hashpw(pw.encode('utf8'), bcrypt.gensalt())
@@ -26,3 +23,5 @@ class User(Base):
expected_hash = self.password_hash.encode('utf8')
return bcrypt.checkpw(pw.encode('utf8'), expected_hash)
return False
+
+ created_pages: Mapped[List['Page']] = relationship(back_populates='creator')
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/routes.py b/docs/tutorials/wiki2/src/authorization/tutorial/routes.py
index fb352604d..7ee816132 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/routes.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/routes.py
@@ -6,6 +6,7 @@ from pyramid.httpexceptions import (
HTTPNotFound,
HTTPSeeOther,
)
+import sqlalchemy as sa
from . import models
@@ -23,7 +24,10 @@ def includeme(config):
def new_page_factory(request):
pagename = request.matchdict['pagename']
- if request.dbsession.query(models.Page).filter_by(name=pagename).count() > 0:
+ exists = request.dbsession.execute(
+ sa.select(sa.exists(models.Page)).where(models.Page.name == pagename)
+ ).scalar()
+ if exists:
next_url = request.route_url('edit_page', pagename=pagename)
raise HTTPSeeOther(location=next_url)
return NewPage(pagename)
@@ -40,7 +44,9 @@ class NewPage:
def page_factory(request):
pagename = request.matchdict['pagename']
- page = request.dbsession.query(models.Page).filter_by(name=pagename).first()
+ page = request.dbsession.scalars(
+ sa.select(models.Page).where(models.Page.name == pagename)
+ ).one_or_none()
if page is None:
raise HTTPNotFound
return PageResource(page)
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/security.py b/docs/tutorials/wiki2/src/authorization/tutorial/security.py
index 18f0bd4c7..1f8fda099 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/security.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/security.py
@@ -22,7 +22,7 @@ class MySecurityPolicy:
return None
userid = identity['userid']
- user = request.dbsession.query(models.User).get(userid)
+ user = request.dbsession.get(models.User, userid)
return user
def identity(self, request):
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py
index 807ff3464..e97908b63 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py
@@ -8,6 +8,7 @@ from pyramid.view import (
forbidden_view_config,
view_config,
)
+import sqlalchemy as sa
from .. import models
@@ -22,11 +23,9 @@ def login(request):
if request.method == 'POST':
login = request.params['login']
password = request.params['password']
- user = (
- request.dbsession.query(models.User)
- .filter_by(name=login)
- .first()
- )
+ user = request.dbsession.scalars(
+ sa.select(models.User).where(models.User.name == login)
+ ).one_or_none()
if user is not None and user.check_password(password):
new_csrf_token(request)
headers = remember(request, user.id)
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py
index 4a2a66c84..50c34cbb5 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py
@@ -3,6 +3,7 @@ from html import escape
from pyramid.httpexceptions import HTTPSeeOther
from pyramid.view import view_config
import re
+import sqlalchemy as sa
from .. import models
@@ -22,7 +23,9 @@ def view_page(request):
def add_link(match):
word = match.group(1)
- exists = request.dbsession.query(models.Page).filter_by(name=word).all()
+ exists = request.dbsession.execute(
+ sa.select(sa.exists(models.Page)).where(models.Page.name == word)
+ ).scalar()
if exists:
view_url = request.route_url('view_page', pagename=word)
return '<a href="%s">%s</a>' % (view_url, escape(word))
@@ -55,8 +58,7 @@ def add_page(request):
pagename = request.context.pagename
if request.method == 'POST':
body = request.params['body']
- page = models.Page(name=pagename, data=body)
- page.creator = request.identity
+ page = models.Page(name=pagename, data=body, creator=request.identity)
request.dbsession.add(page)
next_url = request.route_url('view_page', pagename=pagename)
return HTTPSeeOther(location=next_url)