From 7a68954f6edab387f01897867f6792fa18d9229d Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 4 Feb 2024 12:11:44 -0700 Subject: upgrade the wiki2/installation chapter --- docs/tutorials/wiki2/src/installation/.coveragerc | 2 - docs/tutorials/wiki2/src/installation/CHANGES.txt | 4 -- docs/tutorials/wiki2/src/installation/MANIFEST.in | 2 +- docs/tutorials/wiki2/src/installation/README.md | 60 +++++++++++++++++++++ docs/tutorials/wiki2/src/installation/README.txt | 44 ---------------- .../wiki2/src/installation/development.ini | 8 +-- .../wiki2/src/installation/production.ini | 8 +-- .../wiki2/src/installation/pyproject.toml | 58 ++++++++++++++++++++ docs/tutorials/wiki2/src/installation/pytest.ini | 6 --- docs/tutorials/wiki2/src/installation/setup.py | 61 ---------------------- docs/tutorials/wiki2/src/installation/testing.ini | 8 +-- .../wiki2/src/installation/tutorial/models/meta.py | 24 ++++----- .../src/installation/tutorial/models/mymodel.py | 15 +++--- .../src/installation/tutorial/views/default.py | 8 +-- 14 files changed, 151 insertions(+), 157 deletions(-) delete mode 100644 docs/tutorials/wiki2/src/installation/.coveragerc delete mode 100644 docs/tutorials/wiki2/src/installation/CHANGES.txt create mode 100644 docs/tutorials/wiki2/src/installation/README.md delete mode 100644 docs/tutorials/wiki2/src/installation/README.txt create mode 100644 docs/tutorials/wiki2/src/installation/pyproject.toml delete mode 100644 docs/tutorials/wiki2/src/installation/pytest.ini delete mode 100644 docs/tutorials/wiki2/src/installation/setup.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/installation/.coveragerc b/docs/tutorials/wiki2/src/installation/.coveragerc deleted file mode 100644 index 5db0e79cf..000000000 --- a/docs/tutorials/wiki2/src/installation/.coveragerc +++ /dev/null @@ -1,2 +0,0 @@ -[run] -source = tutorial diff --git a/docs/tutorials/wiki2/src/installation/CHANGES.txt b/docs/tutorials/wiki2/src/installation/CHANGES.txt deleted file mode 100644 index 14b902fd1..000000000 --- a/docs/tutorials/wiki2/src/installation/CHANGES.txt +++ /dev/null @@ -1,4 +0,0 @@ -0.0 ---- - -- Initial version. diff --git a/docs/tutorials/wiki2/src/installation/MANIFEST.in b/docs/tutorials/wiki2/src/installation/MANIFEST.in index b4624fd1c..201692c1b 100644 --- a/docs/tutorials/wiki2/src/installation/MANIFEST.in +++ b/docs/tutorials/wiki2/src/installation/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/installation/README.md b/docs/tutorials/wiki2/src/installation/README.md new file mode 100644 index 000000000..5ac1209d0 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/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/installation/README.txt b/docs/tutorials/wiki2/src/installation/README.txt deleted file mode 100644 index ed6b88b49..000000000 --- a/docs/tutorials/wiki2/src/installation/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/installation/development.ini b/docs/tutorials/wiki2/src/installation/development.ini index f02c4b1b6..e7baeed37 100644 --- a/docs/tutorials/wiki2/src/installation/development.ini +++ b/docs/tutorials/wiki2/src/installation/development.ini @@ -25,16 +25,16 @@ retry.attempts = 3 [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/installation/production.ini b/docs/tutorials/wiki2/src/installation/production.ini index f8e83f21f..f636aaba3 100644 --- a/docs/tutorials/wiki2/src/installation/production.ini +++ b/docs/tutorials/wiki2/src/installation/production.ini @@ -19,16 +19,16 @@ retry.attempts = 3 [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/installation/pyproject.toml b/docs/tutorials/wiki2/src/installation/pyproject.toml new file mode 100644 index 000000000..d03ba4882 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/pyproject.toml @@ -0,0 +1,58 @@ +[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 = [ + "plaster_pastedeploy", + "pyramid", + "pyramid_jinja2", + "pyramid_debugtoolbar", + "waitress", + "alembic", + "pyramid_retry", + "pyramid_tm", + "SQLAlchemy", + "transaction", + "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/installation/pytest.ini b/docs/tutorials/wiki2/src/installation/pytest.ini deleted file mode 100644 index 3df78fe9d..000000000 --- a/docs/tutorials/wiki2/src/installation/pytest.ini +++ /dev/null @@ -1,6 +0,0 @@ -[pytest] -addopts = --strict-markers - -testpaths = - tutorial - tests diff --git a/docs/tutorials/wiki2/src/installation/setup.py b/docs/tutorials/wiki2/src/installation/setup.py deleted file mode 100644 index 5e7a0111d..000000000 --- a/docs/tutorials/wiki2/src/installation/setup.py +++ /dev/null @@ -1,61 +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 = [ - 'plaster_pastedeploy', - 'pyramid', - 'pyramid_jinja2', - 'pyramid_debugtoolbar', - 'waitress', - 'alembic', - 'pyramid_retry', - 'pyramid_tm', - 'SQLAlchemy', - 'transaction', - '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/installation/testing.ini b/docs/tutorials/wiki2/src/installation/testing.ini index 5caa1a8dc..503cf3018 100644 --- a/docs/tutorials/wiki2/src/installation/testing.ini +++ b/docs/tutorials/wiki2/src/installation/testing.ini @@ -19,16 +19,16 @@ retry.attempts = 3 [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/installation/tutorial/models/meta.py b/docs/tutorials/wiki2/src/installation/tutorial/models/meta.py index d659c7857..62d52fcf3 100644 --- a/docs/tutorials/wiki2/src/installation/tutorial/models/meta.py +++ b/docs/tutorials/wiki2/src/installation/tutorial/models/meta.py @@ -1,16 +1,12 @@ -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): + 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/installation/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/installation/tutorial/models/mymodel.py index d65a01a42..947c7b65f 100644 --- a/docs/tutorials/wiki2/src/installation/tutorial/models/mymodel.py +++ b/docs/tutorials/wiki2/src/installation/tutorial/models/mymodel.py @@ -1,18 +1,15 @@ -from sqlalchemy import ( - Column, - Index, - Integer, - Text, -) +from sqlalchemy import Index, Integer, Text +from sqlalchemy.orm import Mapped, mapped_column +from typing import Optional from .meta import Base class MyModel(Base): __tablename__ = 'models' - id = Column(Integer, primary_key=True) - name = Column(Text) - value = Column(Integer) + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[Optional[str]] + value: Mapped[Optional[int]] Index('my_index', MyModel.name, unique=True, mysql_length=255) diff --git a/docs/tutorials/wiki2/src/installation/tutorial/views/default.py b/docs/tutorials/wiki2/src/installation/tutorial/views/default.py index a0f654d38..3eeb7f26d 100644 --- a/docs/tutorials/wiki2/src/installation/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/installation/tutorial/views/default.py @@ -1,6 +1,6 @@ from pyramid.view import view_config from pyramid.response import Response -from sqlalchemy.exc import SQLAlchemyError +import sqlalchemy as sa from .. import models @@ -8,9 +8,9 @@ from .. import models @view_config(route_name='home', renderer='tutorial:templates/mytemplate.jinja2') def my_view(request): try: - query = request.dbsession.query(models.MyModel) - one = query.filter(models.MyModel.name == 'one').one() - except SQLAlchemyError: + query = sa.select(models.MyModel).where(models.MyModel.name == 'one') + one = request.dbsession.execute(query).scalar_one() + except sa.exc.SQLAlchemyError: return Response(db_err_msg, content_type='text/plain', status=500) return {'one': one, 'project': 'myproj'} -- cgit v1.2.3 From 3a1d24397616a47f75b63b6367a78975dc546526 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 4 Feb 2024 13:10:13 -0700 Subject: upgrade basiclayout chapter --- docs/tutorials/wiki2/src/basiclayout/.coveragerc | 2 - docs/tutorials/wiki2/src/basiclayout/CHANGES.txt | 4 -- docs/tutorials/wiki2/src/basiclayout/MANIFEST.in | 2 +- docs/tutorials/wiki2/src/basiclayout/README.md | 60 +++++++++++++++++++++ docs/tutorials/wiki2/src/basiclayout/README.txt | 44 ---------------- .../wiki2/src/basiclayout/development.ini | 8 +-- .../tutorials/wiki2/src/basiclayout/production.ini | 8 +-- .../tutorials/wiki2/src/basiclayout/pyproject.toml | 58 ++++++++++++++++++++ docs/tutorials/wiki2/src/basiclayout/pytest.ini | 6 --- docs/tutorials/wiki2/src/basiclayout/setup.py | 61 ---------------------- docs/tutorials/wiki2/src/basiclayout/testing.ini | 8 +-- .../wiki2/src/basiclayout/tutorial/models/meta.py | 28 +++++----- .../src/basiclayout/tutorial/models/mymodel.py | 15 +++--- .../src/basiclayout/tutorial/views/default.py | 8 +-- .../wiki2/src/installation/tutorial/models/meta.py | 4 ++ 15 files changed, 159 insertions(+), 157 deletions(-) delete mode 100644 docs/tutorials/wiki2/src/basiclayout/.coveragerc delete mode 100644 docs/tutorials/wiki2/src/basiclayout/CHANGES.txt create mode 100644 docs/tutorials/wiki2/src/basiclayout/README.md delete mode 100644 docs/tutorials/wiki2/src/basiclayout/README.txt create mode 100644 docs/tutorials/wiki2/src/basiclayout/pyproject.toml delete mode 100644 docs/tutorials/wiki2/src/basiclayout/pytest.ini delete mode 100644 docs/tutorials/wiki2/src/basiclayout/setup.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/basiclayout/.coveragerc b/docs/tutorials/wiki2/src/basiclayout/.coveragerc deleted file mode 100644 index 5db0e79cf..000000000 --- a/docs/tutorials/wiki2/src/basiclayout/.coveragerc +++ /dev/null @@ -1,2 +0,0 @@ -[run] -source = tutorial diff --git a/docs/tutorials/wiki2/src/basiclayout/CHANGES.txt b/docs/tutorials/wiki2/src/basiclayout/CHANGES.txt deleted file mode 100644 index 14b902fd1..000000000 --- a/docs/tutorials/wiki2/src/basiclayout/CHANGES.txt +++ /dev/null @@ -1,4 +0,0 @@ -0.0 ---- - -- Initial version. diff --git a/docs/tutorials/wiki2/src/basiclayout/MANIFEST.in b/docs/tutorials/wiki2/src/basiclayout/MANIFEST.in index b4624fd1c..201692c1b 100644 --- a/docs/tutorials/wiki2/src/basiclayout/MANIFEST.in +++ b/docs/tutorials/wiki2/src/basiclayout/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/basiclayout/README.md b/docs/tutorials/wiki2/src/basiclayout/README.md new file mode 100644 index 000000000..5ac1209d0 --- /dev/null +++ b/docs/tutorials/wiki2/src/basiclayout/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/basiclayout/README.txt b/docs/tutorials/wiki2/src/basiclayout/README.txt deleted file mode 100644 index ed6b88b49..000000000 --- a/docs/tutorials/wiki2/src/basiclayout/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/basiclayout/development.ini b/docs/tutorials/wiki2/src/basiclayout/development.ini index f02c4b1b6..e7baeed37 100644 --- a/docs/tutorials/wiki2/src/basiclayout/development.ini +++ b/docs/tutorials/wiki2/src/basiclayout/development.ini @@ -25,16 +25,16 @@ retry.attempts = 3 [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/basiclayout/production.ini b/docs/tutorials/wiki2/src/basiclayout/production.ini index f8e83f21f..f636aaba3 100644 --- a/docs/tutorials/wiki2/src/basiclayout/production.ini +++ b/docs/tutorials/wiki2/src/basiclayout/production.ini @@ -19,16 +19,16 @@ retry.attempts = 3 [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/basiclayout/pyproject.toml b/docs/tutorials/wiki2/src/basiclayout/pyproject.toml new file mode 100644 index 000000000..d03ba4882 --- /dev/null +++ b/docs/tutorials/wiki2/src/basiclayout/pyproject.toml @@ -0,0 +1,58 @@ +[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 = [ + "plaster_pastedeploy", + "pyramid", + "pyramid_jinja2", + "pyramid_debugtoolbar", + "waitress", + "alembic", + "pyramid_retry", + "pyramid_tm", + "SQLAlchemy", + "transaction", + "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/basiclayout/pytest.ini b/docs/tutorials/wiki2/src/basiclayout/pytest.ini deleted file mode 100644 index 3df78fe9d..000000000 --- a/docs/tutorials/wiki2/src/basiclayout/pytest.ini +++ /dev/null @@ -1,6 +0,0 @@ -[pytest] -addopts = --strict-markers - -testpaths = - tutorial - tests diff --git a/docs/tutorials/wiki2/src/basiclayout/setup.py b/docs/tutorials/wiki2/src/basiclayout/setup.py deleted file mode 100644 index 5e7a0111d..000000000 --- a/docs/tutorials/wiki2/src/basiclayout/setup.py +++ /dev/null @@ -1,61 +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 = [ - 'plaster_pastedeploy', - 'pyramid', - 'pyramid_jinja2', - 'pyramid_debugtoolbar', - 'waitress', - 'alembic', - 'pyramid_retry', - 'pyramid_tm', - 'SQLAlchemy', - 'transaction', - '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/basiclayout/testing.ini b/docs/tutorials/wiki2/src/basiclayout/testing.ini index 5caa1a8dc..503cf3018 100644 --- a/docs/tutorials/wiki2/src/basiclayout/testing.ini +++ b/docs/tutorials/wiki2/src/basiclayout/testing.ini @@ -19,16 +19,16 @@ retry.attempts = 3 [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/basiclayout/tutorial/models/meta.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/meta.py index d659c7857..38712bfd2 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/meta.py +++ b/docs/tutorials/wiki2/src/basiclayout/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/basiclayout/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/mymodel.py index d65a01a42..947c7b65f 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/mymodel.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/mymodel.py @@ -1,18 +1,15 @@ -from sqlalchemy import ( - Column, - Index, - Integer, - Text, -) +from sqlalchemy import Index, Integer, Text +from sqlalchemy.orm import Mapped, mapped_column +from typing import Optional from .meta import Base class MyModel(Base): __tablename__ = 'models' - id = Column(Integer, primary_key=True) - name = Column(Text) - value = Column(Integer) + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[Optional[str]] + value: Mapped[Optional[int]] Index('my_index', MyModel.name, unique=True, mysql_length=255) diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/views/default.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/views/default.py index a0f654d38..3eeb7f26d 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/views/default.py @@ -1,6 +1,6 @@ from pyramid.view import view_config from pyramid.response import Response -from sqlalchemy.exc import SQLAlchemyError +import sqlalchemy as sa from .. import models @@ -8,9 +8,9 @@ from .. import models @view_config(route_name='home', renderer='tutorial:templates/mytemplate.jinja2') def my_view(request): try: - query = request.dbsession.query(models.MyModel) - one = query.filter(models.MyModel.name == 'one').one() - except SQLAlchemyError: + query = sa.select(models.MyModel).where(models.MyModel.name == 'one') + one = request.dbsession.execute(query).scalar_one() + except sa.exc.SQLAlchemyError: return Response(db_err_msg, content_type='text/plain', status=500) return {'one': one, 'project': 'myproj'} diff --git a/docs/tutorials/wiki2/src/installation/tutorial/models/meta.py b/docs/tutorials/wiki2/src/installation/tutorial/models/meta.py index 62d52fcf3..38712bfd2 100644 --- a/docs/tutorials/wiki2/src/installation/tutorial/models/meta.py +++ b/docs/tutorials/wiki2/src/installation/tutorial/models/meta.py @@ -3,6 +3,10 @@ from sqlalchemy.orm import DeclarativeBase 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", -- cgit v1.2.3 From 28afd5d1e79d618ed0522029e926fd9219d31c0d Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 4 Feb 2024 13:51:57 -0700 Subject: upgrade definingmodels chapter --- .../src/basiclayout/tutorial/models/mymodel.py | 2 +- .../src/installation/tutorial/models/mymodel.py | 2 +- docs/tutorials/wiki2/src/models/.coveragerc | 2 - docs/tutorials/wiki2/src/models/CHANGES.txt | 4 -- docs/tutorials/wiki2/src/models/MANIFEST.in | 2 +- docs/tutorials/wiki2/src/models/README.md | 60 +++++++++++++++++++++ docs/tutorials/wiki2/src/models/README.txt | 44 --------------- docs/tutorials/wiki2/src/models/development.ini | 8 +-- docs/tutorials/wiki2/src/models/production.ini | 8 +-- docs/tutorials/wiki2/src/models/pyproject.toml | 59 ++++++++++++++++++++ docs/tutorials/wiki2/src/models/pytest.ini | 6 --- docs/tutorials/wiki2/src/models/setup.py | 62 ---------------------- docs/tutorials/wiki2/src/models/testing.ini | 8 +-- .../wiki2/src/models/tutorial/models/meta.py | 28 +++++----- .../wiki2/src/models/tutorial/models/page.py | 20 +++---- .../wiki2/src/models/tutorial/models/user.py | 17 +++--- .../wiki2/src/models/tutorial/views/default.py | 8 +-- 17 files changed, 168 insertions(+), 172 deletions(-) delete mode 100644 docs/tutorials/wiki2/src/models/.coveragerc delete mode 100644 docs/tutorials/wiki2/src/models/CHANGES.txt create mode 100644 docs/tutorials/wiki2/src/models/README.md delete mode 100644 docs/tutorials/wiki2/src/models/README.txt create mode 100644 docs/tutorials/wiki2/src/models/pyproject.toml delete mode 100644 docs/tutorials/wiki2/src/models/pytest.ini delete mode 100644 docs/tutorials/wiki2/src/models/setup.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/mymodel.py index 947c7b65f..2b9c7da11 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/mymodel.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/mymodel.py @@ -1,4 +1,4 @@ -from sqlalchemy import Index, Integer, Text +from sqlalchemy import Index from sqlalchemy.orm import Mapped, mapped_column from typing import Optional diff --git a/docs/tutorials/wiki2/src/installation/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/installation/tutorial/models/mymodel.py index 947c7b65f..2b9c7da11 100644 --- a/docs/tutorials/wiki2/src/installation/tutorial/models/mymodel.py +++ b/docs/tutorials/wiki2/src/installation/tutorial/models/mymodel.py @@ -1,4 +1,4 @@ -from sqlalchemy import Index, Integer, Text +from sqlalchemy import Index from sqlalchemy.orm import Mapped, mapped_column from typing import Optional diff --git a/docs/tutorials/wiki2/src/models/.coveragerc b/docs/tutorials/wiki2/src/models/.coveragerc deleted file mode 100644 index 5db0e79cf..000000000 --- a/docs/tutorials/wiki2/src/models/.coveragerc +++ /dev/null @@ -1,2 +0,0 @@ -[run] -source = tutorial diff --git a/docs/tutorials/wiki2/src/models/CHANGES.txt b/docs/tutorials/wiki2/src/models/CHANGES.txt deleted file mode 100644 index 14b902fd1..000000000 --- a/docs/tutorials/wiki2/src/models/CHANGES.txt +++ /dev/null @@ -1,4 +0,0 @@ -0.0 ---- - -- Initial version. diff --git a/docs/tutorials/wiki2/src/models/MANIFEST.in b/docs/tutorials/wiki2/src/models/MANIFEST.in index b4624fd1c..201692c1b 100644 --- a/docs/tutorials/wiki2/src/models/MANIFEST.in +++ b/docs/tutorials/wiki2/src/models/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/models/README.md b/docs/tutorials/wiki2/src/models/README.md new file mode 100644 index 000000000..5ac1209d0 --- /dev/null +++ b/docs/tutorials/wiki2/src/models/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/models/README.txt b/docs/tutorials/wiki2/src/models/README.txt deleted file mode 100644 index ed6b88b49..000000000 --- a/docs/tutorials/wiki2/src/models/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/models/development.ini b/docs/tutorials/wiki2/src/models/development.ini index f02c4b1b6..e7baeed37 100644 --- a/docs/tutorials/wiki2/src/models/development.ini +++ b/docs/tutorials/wiki2/src/models/development.ini @@ -25,16 +25,16 @@ retry.attempts = 3 [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/models/production.ini b/docs/tutorials/wiki2/src/models/production.ini index f8e83f21f..f636aaba3 100644 --- a/docs/tutorials/wiki2/src/models/production.ini +++ b/docs/tutorials/wiki2/src/models/production.ini @@ -19,16 +19,16 @@ retry.attempts = 3 [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/models/pyproject.toml b/docs/tutorials/wiki2/src/models/pyproject.toml new file mode 100644 index 000000000..973944f98 --- /dev/null +++ b/docs/tutorials/wiki2/src/models/pyproject.toml @@ -0,0 +1,59 @@ +[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", + "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/models/pytest.ini b/docs/tutorials/wiki2/src/models/pytest.ini deleted file mode 100644 index 3df78fe9d..000000000 --- a/docs/tutorials/wiki2/src/models/pytest.ini +++ /dev/null @@ -1,6 +0,0 @@ -[pytest] -addopts = --strict-markers - -testpaths = - tutorial - tests diff --git a/docs/tutorials/wiki2/src/models/setup.py b/docs/tutorials/wiki2/src/models/setup.py deleted file mode 100644 index fbd848136..000000000 --- a/docs/tutorials/wiki2/src/models/setup.py +++ /dev/null @@ -1,62 +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', - '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/models/testing.ini b/docs/tutorials/wiki2/src/models/testing.ini index 5caa1a8dc..503cf3018 100644 --- a/docs/tutorials/wiki2/src/models/testing.ini +++ b/docs/tutorials/wiki2/src/models/testing.ini @@ -19,16 +19,16 @@ retry.attempts = 3 [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/models/tutorial/models/meta.py b/docs/tutorials/wiki2/src/models/tutorial/models/meta.py index d659c7857..38712bfd2 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/models/meta.py +++ b/docs/tutorials/wiki2/src/models/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/models/tutorial/models/page.py b/docs/tutorials/wiki2/src/models/tutorial/models/page.py index 74ff1faf8..5a1c885c9 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/models/page.py +++ b/docs/tutorials/wiki2/src/models/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('User', back_populates='created_pages') diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/user.py b/docs/tutorials/wiki2/src/models/tutorial/models/user.py index 9228b48f7..d85d890c6 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/models/user.py +++ b/docs/tutorials/wiki2/src/models/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 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['Page'] = relationship('Page', back_populates='creator') diff --git a/docs/tutorials/wiki2/src/models/tutorial/views/default.py b/docs/tutorials/wiki2/src/models/tutorial/views/default.py index a0f654d38..3eeb7f26d 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/models/tutorial/views/default.py @@ -1,6 +1,6 @@ from pyramid.view import view_config from pyramid.response import Response -from sqlalchemy.exc import SQLAlchemyError +import sqlalchemy as sa from .. import models @@ -8,9 +8,9 @@ from .. import models @view_config(route_name='home', renderer='tutorial:templates/mytemplate.jinja2') def my_view(request): try: - query = request.dbsession.query(models.MyModel) - one = query.filter(models.MyModel.name == 'one').one() - except SQLAlchemyError: + query = sa.select(models.MyModel).where(models.MyModel.name == 'one') + one = request.dbsession.execute(query).scalar_one() + except sa.exc.SQLAlchemyError: return Response(db_err_msg, content_type='text/plain', status=500) return {'one': one, 'project': 'myproj'} -- cgit v1.2.3 From 569f0a53d0bedecceb1365a0159c9fe514f198a9 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 4 Feb 2024 14:41:18 -0700 Subject: add missing migration files --- .../alembic/versions/20240204_4b6614165904.py | 33 ++++++++++++++ .../alembic/versions/20240204_4b6614165904.py | 33 ++++++++++++++ .../alembic/versions/20240204_07f9d6b626b2.py | 52 ++++++++++++++++++++++ .../alembic/versions/20240204_4b6614165904.py | 33 ++++++++++++++ 4 files changed, 151 insertions(+) create mode 100644 docs/tutorials/wiki2/src/basiclayout/tutorial/alembic/versions/20240204_4b6614165904.py create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/alembic/versions/20240204_4b6614165904.py create mode 100644 docs/tutorials/wiki2/src/models/tutorial/alembic/versions/20240204_07f9d6b626b2.py create mode 100644 docs/tutorials/wiki2/src/models/tutorial/alembic/versions/20240204_4b6614165904.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/alembic/versions/20240204_4b6614165904.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/alembic/versions/20240204_4b6614165904.py new file mode 100644 index 000000000..4f7a97d30 --- /dev/null +++ b/docs/tutorials/wiki2/src/basiclayout/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/installation/tutorial/alembic/versions/20240204_4b6614165904.py b/docs/tutorials/wiki2/src/installation/tutorial/alembic/versions/20240204_4b6614165904.py new file mode 100644 index 000000000..4f7a97d30 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/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/models/tutorial/alembic/versions/20240204_07f9d6b626b2.py b/docs/tutorials/wiki2/src/models/tutorial/alembic/versions/20240204_07f9d6b626b2.py new file mode 100644 index 000000000..329c00208 --- /dev/null +++ b/docs/tutorials/wiki2/src/models/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/models/tutorial/alembic/versions/20240204_4b6614165904.py b/docs/tutorials/wiki2/src/models/tutorial/alembic/versions/20240204_4b6614165904.py new file mode 100644 index 000000000..4f7a97d30 --- /dev/null +++ b/docs/tutorials/wiki2/src/models/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 ### -- cgit v1.2.3 From d3c8d93cfb1282f328ecd133df6565a2df92779f Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 4 Feb 2024 15:29:37 -0700 Subject: update the definingviews chapter --- .../wiki2/src/models/tutorial/models/page.py | 2 +- .../wiki2/src/models/tutorial/models/user.py | 4 +- docs/tutorials/wiki2/src/views/.coveragerc | 2 - docs/tutorials/wiki2/src/views/CHANGES.txt | 4 -- docs/tutorials/wiki2/src/views/MANIFEST.in | 2 +- docs/tutorials/wiki2/src/views/README.md | 60 +++++++++++++++++++++ docs/tutorials/wiki2/src/views/README.txt | 44 --------------- docs/tutorials/wiki2/src/views/development.ini | 8 +-- docs/tutorials/wiki2/src/views/production.ini | 8 +-- docs/tutorials/wiki2/src/views/pyproject.toml | 60 +++++++++++++++++++++ docs/tutorials/wiki2/src/views/pytest.ini | 6 --- docs/tutorials/wiki2/src/views/setup.py | 63 ---------------------- docs/tutorials/wiki2/src/views/testing.ini | 8 +-- .../alembic/versions/20240204_07f9d6b626b2.py | 52 ++++++++++++++++++ .../alembic/versions/20240204_4b6614165904.py | 33 ++++++++++++ .../wiki2/src/views/tutorial/models/meta.py | 28 +++++----- .../wiki2/src/views/tutorial/models/page.py | 20 +++---- .../wiki2/src/views/tutorial/models/user.py | 17 +++--- .../wiki2/src/views/tutorial/views/default.py | 27 +++++++--- 19 files changed, 271 insertions(+), 177 deletions(-) delete mode 100644 docs/tutorials/wiki2/src/views/.coveragerc delete mode 100644 docs/tutorials/wiki2/src/views/CHANGES.txt create mode 100644 docs/tutorials/wiki2/src/views/README.md delete mode 100644 docs/tutorials/wiki2/src/views/README.txt create mode 100644 docs/tutorials/wiki2/src/views/pyproject.toml delete mode 100644 docs/tutorials/wiki2/src/views/pytest.ini delete mode 100644 docs/tutorials/wiki2/src/views/setup.py create mode 100644 docs/tutorials/wiki2/src/views/tutorial/alembic/versions/20240204_07f9d6b626b2.py create mode 100644 docs/tutorials/wiki2/src/views/tutorial/alembic/versions/20240204_4b6614165904.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/page.py b/docs/tutorials/wiki2/src/models/tutorial/models/page.py index 5a1c885c9..6cc9aae3c 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/models/page.py +++ b/docs/tutorials/wiki2/src/models/tutorial/models/page.py @@ -13,4 +13,4 @@ class Page(Base): data: Mapped[str] creator_id: Mapped[int] = mapped_column(ForeignKey('users.id')) - creator: Mapped['User'] = relationship('User', back_populates='created_pages') + creator: Mapped['User'] = relationship(back_populates='created_pages') diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/user.py b/docs/tutorials/wiki2/src/models/tutorial/models/user.py index d85d890c6..926a66e64 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/models/user.py +++ b/docs/tutorials/wiki2/src/models/tutorial/models/user.py @@ -1,6 +1,6 @@ import bcrypt from sqlalchemy.orm import Mapped, mapped_column, relationship -from typing import Optional +from typing import List, Optional from .meta import Base @@ -24,4 +24,4 @@ class User(Base): return bcrypt.checkpw(pw.encode('utf8'), expected_hash) return False - created_pages: Mapped['Page'] = relationship('Page', back_populates='creator') + created_pages: Mapped[List['Page']] = relationship(back_populates='creator') diff --git a/docs/tutorials/wiki2/src/views/.coveragerc b/docs/tutorials/wiki2/src/views/.coveragerc deleted file mode 100644 index 5db0e79cf..000000000 --- a/docs/tutorials/wiki2/src/views/.coveragerc +++ /dev/null @@ -1,2 +0,0 @@ -[run] -source = tutorial diff --git a/docs/tutorials/wiki2/src/views/CHANGES.txt b/docs/tutorials/wiki2/src/views/CHANGES.txt deleted file mode 100644 index 14b902fd1..000000000 --- a/docs/tutorials/wiki2/src/views/CHANGES.txt +++ /dev/null @@ -1,4 +0,0 @@ -0.0 ---- - -- Initial version. diff --git a/docs/tutorials/wiki2/src/views/MANIFEST.in b/docs/tutorials/wiki2/src/views/MANIFEST.in index b4624fd1c..201692c1b 100644 --- a/docs/tutorials/wiki2/src/views/MANIFEST.in +++ b/docs/tutorials/wiki2/src/views/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/views/README.md b/docs/tutorials/wiki2/src/views/README.md new file mode 100644 index 000000000..5ac1209d0 --- /dev/null +++ b/docs/tutorials/wiki2/src/views/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/views/README.txt b/docs/tutorials/wiki2/src/views/README.txt deleted file mode 100644 index ed6b88b49..000000000 --- a/docs/tutorials/wiki2/src/views/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/views/development.ini b/docs/tutorials/wiki2/src/views/development.ini index f02c4b1b6..e7baeed37 100644 --- a/docs/tutorials/wiki2/src/views/development.ini +++ b/docs/tutorials/wiki2/src/views/development.ini @@ -25,16 +25,16 @@ retry.attempts = 3 [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/views/production.ini b/docs/tutorials/wiki2/src/views/production.ini index f8e83f21f..f636aaba3 100644 --- a/docs/tutorials/wiki2/src/views/production.ini +++ b/docs/tutorials/wiki2/src/views/production.ini @@ -19,16 +19,16 @@ retry.attempts = 3 [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/views/pyproject.toml b/docs/tutorials/wiki2/src/views/pyproject.toml new file mode 100644 index 000000000..2b054c578 --- /dev/null +++ b/docs/tutorials/wiki2/src/views/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/views/pytest.ini b/docs/tutorials/wiki2/src/views/pytest.ini deleted file mode 100644 index 3df78fe9d..000000000 --- a/docs/tutorials/wiki2/src/views/pytest.ini +++ /dev/null @@ -1,6 +0,0 @@ -[pytest] -addopts = --strict-markers - -testpaths = - tutorial - tests diff --git a/docs/tutorials/wiki2/src/views/setup.py b/docs/tutorials/wiki2/src/views/setup.py deleted file mode 100644 index 12eabaff2..000000000 --- a/docs/tutorials/wiki2/src/views/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/views/testing.ini b/docs/tutorials/wiki2/src/views/testing.ini index 5caa1a8dc..503cf3018 100644 --- a/docs/tutorials/wiki2/src/views/testing.ini +++ b/docs/tutorials/wiki2/src/views/testing.ini @@ -19,16 +19,16 @@ retry.attempts = 3 [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/views/tutorial/alembic/versions/20240204_07f9d6b626b2.py b/docs/tutorials/wiki2/src/views/tutorial/alembic/versions/20240204_07f9d6b626b2.py new file mode 100644 index 000000000..329c00208 --- /dev/null +++ b/docs/tutorials/wiki2/src/views/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/views/tutorial/alembic/versions/20240204_4b6614165904.py b/docs/tutorials/wiki2/src/views/tutorial/alembic/versions/20240204_4b6614165904.py new file mode 100644 index 000000000..4f7a97d30 --- /dev/null +++ b/docs/tutorials/wiki2/src/views/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/views/tutorial/models/meta.py b/docs/tutorials/wiki2/src/views/tutorial/models/meta.py index d659c7857..38712bfd2 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/models/meta.py +++ b/docs/tutorials/wiki2/src/views/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/views/tutorial/models/page.py b/docs/tutorials/wiki2/src/views/tutorial/models/page.py index 74ff1faf8..6cc9aae3c 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/models/page.py +++ b/docs/tutorials/wiki2/src/views/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/views/tutorial/models/user.py b/docs/tutorials/wiki2/src/views/tutorial/models/user.py index 9228b48f7..926a66e64 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/models/user.py +++ b/docs/tutorials/wiki2/src/views/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/views/tutorial/views/default.py b/docs/tutorials/wiki2/src/views/tutorial/views/default.py index df0e4cb9e..7feb6671f 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/views/tutorial/views/default.py @@ -6,6 +6,7 @@ from pyramid.httpexceptions import ( ) from pyramid.view import view_config import re +import sqlalchemy as sa from .. import models @@ -21,13 +22,17 @@ def view_wiki(request): @view_config(route_name='view_page', renderer='tutorial:templates/view.jinja2') def view_page(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('No such page') 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 '%s' % (view_url, escape(word)) @@ -43,7 +48,11 @@ def view_page(request): @view_config(route_name='edit_page', renderer='tutorial:templates/edit.jinja2') def edit_page(request): pagename = request.matchdict['pagename'] - page = request.dbsession.query(models.Page).filter_by(name=pagename).one() + page = request.dbsession.scalars( + sa.select(models.Page).where(models.Page.name == pagename) + ).one_or_none() + if page is None: + raise HTTPNotFound('No such page') if request.method == 'POST': page.data = request.params['body'] next_url = request.route_url('view_page', pagename=page.name) @@ -57,14 +66,18 @@ def edit_page(request): @view_config(route_name='add_page', renderer='tutorial:templates/edit.jinja2') def add_page(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) return HTTPSeeOther(location=next_url) if request.method == 'POST': body = request.params['body'] - page = models.Page(name=pagename, data=body) - page.creator = ( - request.dbsession.query(models.User).filter_by(name='editor').one()) + creator = request.dbsession.scalars( + sa.select(models.User).where(models.User.name == 'editor') + ).one() + page = models.Page(name=pagename, data=body, creator=creator) request.dbsession.add(page) next_url = request.route_url('view_page', pagename=pagename) return HTTPSeeOther(location=next_url) -- cgit v1.2.3 From 286a7a3ee8705b28fb82252daa37896d63ae6741 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 4 Feb 2024 16:14:01 -0700 Subject: update authentication chapter --- .../tutorials/wiki2/src/authentication/.coveragerc | 2 - .../tutorials/wiki2/src/authentication/CHANGES.txt | 4 -- .../tutorials/wiki2/src/authentication/MANIFEST.in | 2 +- docs/tutorials/wiki2/src/authentication/README.md | 60 +++++++++++++++++++++ docs/tutorials/wiki2/src/authentication/README.txt | 44 --------------- .../wiki2/src/authentication/development.ini | 8 +-- .../wiki2/src/authentication/production.ini | 8 +-- .../wiki2/src/authentication/pyproject.toml | 60 +++++++++++++++++++++ docs/tutorials/wiki2/src/authentication/pytest.ini | 6 --- docs/tutorials/wiki2/src/authentication/setup.py | 63 ---------------------- .../tutorials/wiki2/src/authentication/testing.ini | 8 +-- .../alembic/versions/20240204_07f9d6b626b2.py | 52 ++++++++++++++++++ .../alembic/versions/20240204_4b6614165904.py | 33 ++++++++++++ .../src/authentication/tutorial/models/meta.py | 28 +++++----- .../src/authentication/tutorial/models/page.py | 20 +++---- .../src/authentication/tutorial/models/user.py | 17 +++--- .../wiki2/src/authentication/tutorial/security.py | 2 +- .../src/authentication/tutorial/views/auth.py | 9 ++-- .../src/authentication/tutorial/views/default.py | 23 +++++--- 19 files changed, 270 insertions(+), 179 deletions(-) delete mode 100644 docs/tutorials/wiki2/src/authentication/.coveragerc delete mode 100644 docs/tutorials/wiki2/src/authentication/CHANGES.txt create mode 100644 docs/tutorials/wiki2/src/authentication/README.md delete mode 100644 docs/tutorials/wiki2/src/authentication/README.txt create mode 100644 docs/tutorials/wiki2/src/authentication/pyproject.toml delete mode 100644 docs/tutorials/wiki2/src/authentication/pytest.ini delete mode 100644 docs/tutorials/wiki2/src/authentication/setup.py create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/alembic/versions/20240204_07f9d6b626b2.py create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/alembic/versions/20240204_4b6614165904.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authentication/.coveragerc b/docs/tutorials/wiki2/src/authentication/.coveragerc deleted file mode 100644 index 5db0e79cf..000000000 --- a/docs/tutorials/wiki2/src/authentication/.coveragerc +++ /dev/null @@ -1,2 +0,0 @@ -[run] -source = tutorial diff --git a/docs/tutorials/wiki2/src/authentication/CHANGES.txt b/docs/tutorials/wiki2/src/authentication/CHANGES.txt deleted file mode 100644 index 14b902fd1..000000000 --- a/docs/tutorials/wiki2/src/authentication/CHANGES.txt +++ /dev/null @@ -1,4 +0,0 @@ -0.0 ---- - -- Initial version. diff --git a/docs/tutorials/wiki2/src/authentication/MANIFEST.in b/docs/tutorials/wiki2/src/authentication/MANIFEST.in index b4624fd1c..201692c1b 100644 --- a/docs/tutorials/wiki2/src/authentication/MANIFEST.in +++ b/docs/tutorials/wiki2/src/authentication/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/authentication/README.md b/docs/tutorials/wiki2/src/authentication/README.md new file mode 100644 index 000000000..5ac1209d0 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/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/authentication/README.txt b/docs/tutorials/wiki2/src/authentication/README.txt deleted file mode 100644 index ed6b88b49..000000000 --- a/docs/tutorials/wiki2/src/authentication/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/authentication/development.ini b/docs/tutorials/wiki2/src/authentication/development.ini index 7fda4cb7b..7dcb83da0 100644 --- a/docs/tutorials/wiki2/src/authentication/development.ini +++ b/docs/tutorials/wiki2/src/authentication/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/authentication/production.ini b/docs/tutorials/wiki2/src/authentication/production.ini index 8e878a707..b4c18b119 100644 --- a/docs/tutorials/wiki2/src/authentication/production.ini +++ b/docs/tutorials/wiki2/src/authentication/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/authentication/pyproject.toml b/docs/tutorials/wiki2/src/authentication/pyproject.toml new file mode 100644 index 000000000..2b054c578 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/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/authentication/pytest.ini b/docs/tutorials/wiki2/src/authentication/pytest.ini deleted file mode 100644 index 3df78fe9d..000000000 --- a/docs/tutorials/wiki2/src/authentication/pytest.ini +++ /dev/null @@ -1,6 +0,0 @@ -[pytest] -addopts = --strict-markers - -testpaths = - tutorial - tests diff --git a/docs/tutorials/wiki2/src/authentication/setup.py b/docs/tutorials/wiki2/src/authentication/setup.py deleted file mode 100644 index 12eabaff2..000000000 --- a/docs/tutorials/wiki2/src/authentication/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/authentication/testing.ini b/docs/tutorials/wiki2/src/authentication/testing.ini index d3c601f16..095666d67 100644 --- a/docs/tutorials/wiki2/src/authentication/testing.ini +++ b/docs/tutorials/wiki2/src/authentication/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/authentication/tutorial/alembic/versions/20240204_07f9d6b626b2.py b/docs/tutorials/wiki2/src/authentication/tutorial/alembic/versions/20240204_07f9d6b626b2.py new file mode 100644 index 000000000..329c00208 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/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/authentication/tutorial/alembic/versions/20240204_4b6614165904.py b/docs/tutorials/wiki2/src/authentication/tutorial/alembic/versions/20240204_4b6614165904.py new file mode 100644 index 000000000..4f7a97d30 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/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/authentication/tutorial/models/meta.py b/docs/tutorials/wiki2/src/authentication/tutorial/models/meta.py index d659c7857..38712bfd2 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/models/meta.py +++ b/docs/tutorials/wiki2/src/authentication/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/authentication/tutorial/models/page.py b/docs/tutorials/wiki2/src/authentication/tutorial/models/page.py index 74ff1faf8..6cc9aae3c 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/models/page.py +++ b/docs/tutorials/wiki2/src/authentication/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/authentication/tutorial/models/user.py b/docs/tutorials/wiki2/src/authentication/tutorial/models/user.py index 9228b48f7..926a66e64 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/models/user.py +++ b/docs/tutorials/wiki2/src/authentication/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/authentication/tutorial/security.py b/docs/tutorials/wiki2/src/authentication/tutorial/security.py index e0d8ed965..d3d332ec1 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/security.py +++ b/docs/tutorials/wiki2/src/authentication/tutorial/security.py @@ -16,7 +16,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/authentication/tutorial/views/auth.py b/docs/tutorials/wiki2/src/authentication/tutorial/views/auth.py index 807ff3464..e97908b63 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/views/auth.py +++ b/docs/tutorials/wiki2/src/authentication/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/authentication/tutorial/views/default.py b/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py index 4fb715737..50ce361cd 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py @@ -7,6 +7,7 @@ from pyramid.httpexceptions import ( ) from pyramid.view import view_config import re +import sqlalchemy as sa from .. import models @@ -22,13 +23,17 @@ def view_wiki(request): @view_config(route_name='view_page', renderer='tutorial:templates/view.jinja2') def view_page(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('No such page') 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 '%s' % (view_url, escape(word)) @@ -44,7 +49,11 @@ def view_page(request): @view_config(route_name='edit_page', renderer='tutorial:templates/edit.jinja2') def edit_page(request): pagename = request.matchdict['pagename'] - page = request.dbsession.query(models.Page).filter_by(name=pagename).one() + page = request.dbsession.scalars( + sa.select(models.Page).where(models.Page.name == pagename) + ).one_or_none() + if page is None: + raise HTTPNotFound('No such page') user = request.identity if user is None or (user.role != 'editor' and page.creator != user): raise HTTPForbidden @@ -64,13 +73,15 @@ def add_page(request): if user is None or user.role not in ('editor', 'basic'): raise HTTPForbidden 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) return HTTPSeeOther(location=next_url) 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=user) request.dbsession.add(page) next_url = request.route_url('view_page', pagename=pagename) return HTTPSeeOther(location=next_url) -- cgit v1.2.3 From 3c61799661ea5c36ae97326574a3dc7a695ce6aa Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 4 Feb 2024 16:58:08 -0700 Subject: update authorization chapter --- docs/tutorials/wiki2/src/authorization/.coveragerc | 2 - docs/tutorials/wiki2/src/authorization/CHANGES.txt | 4 -- docs/tutorials/wiki2/src/authorization/MANIFEST.in | 2 +- docs/tutorials/wiki2/src/authorization/README.md | 60 +++++++++++++++++++++ docs/tutorials/wiki2/src/authorization/README.txt | 44 --------------- .../wiki2/src/authorization/development.ini | 8 +-- .../wiki2/src/authorization/production.ini | 8 +-- .../wiki2/src/authorization/pyproject.toml | 60 +++++++++++++++++++++ docs/tutorials/wiki2/src/authorization/pytest.ini | 6 --- docs/tutorials/wiki2/src/authorization/setup.py | 63 ---------------------- docs/tutorials/wiki2/src/authorization/testing.ini | 8 +-- .../alembic/versions/20240204_07f9d6b626b2.py | 52 ++++++++++++++++++ .../alembic/versions/20240204_4b6614165904.py | 33 ++++++++++++ .../src/authorization/tutorial/models/meta.py | 28 +++++----- .../src/authorization/tutorial/models/page.py | 20 +++---- .../src/authorization/tutorial/models/user.py | 17 +++--- .../wiki2/src/authorization/tutorial/routes.py | 10 +++- .../wiki2/src/authorization/tutorial/security.py | 2 +- .../wiki2/src/authorization/tutorial/views/auth.py | 9 ++-- .../src/authorization/tutorial/views/default.py | 8 +-- 20 files changed, 266 insertions(+), 178 deletions(-) delete mode 100644 docs/tutorials/wiki2/src/authorization/.coveragerc delete mode 100644 docs/tutorials/wiki2/src/authorization/CHANGES.txt create mode 100644 docs/tutorials/wiki2/src/authorization/README.md delete mode 100644 docs/tutorials/wiki2/src/authorization/README.txt create mode 100644 docs/tutorials/wiki2/src/authorization/pyproject.toml delete mode 100644 docs/tutorials/wiki2/src/authorization/pytest.ini delete mode 100644 docs/tutorials/wiki2/src/authorization/setup.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/alembic/versions/20240204_07f9d6b626b2.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/alembic/versions/20240204_4b6614165904.py (limited to 'docs/tutorials/wiki2/src') 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 '%s' % (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) -- cgit v1.2.3 From 66c6c321b267e112220cfcfd81772923e3691e77 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 4 Feb 2024 17:00:54 -0700 Subject: backport fix to authentication --- docs/tutorials/wiki2/src/authentication/tutorial/views/default.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py b/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py index 50ce361cd..bf37875f4 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py @@ -81,7 +81,7 @@ def add_page(request): return HTTPSeeOther(location=next_url) if request.method == 'POST': body = request.params['body'] - page = models.Page(name=pagename, data=body, creator=user) + 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) -- cgit v1.2.3 From 94ddd8d0519aeb1fe151059048ac365b18574cd0 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 4 Feb 2024 17:05:54 -0700 Subject: upgrade tests chapter --- docs/tutorials/wiki2/src/tests/.coveragerc | 2 - docs/tutorials/wiki2/src/tests/CHANGES.txt | 4 -- docs/tutorials/wiki2/src/tests/MANIFEST.in | 2 +- docs/tutorials/wiki2/src/tests/README.md | 60 +++++++++++++++++++++ docs/tutorials/wiki2/src/tests/README.txt | 44 --------------- docs/tutorials/wiki2/src/tests/development.ini | 8 +-- docs/tutorials/wiki2/src/tests/production.ini | 8 +-- docs/tutorials/wiki2/src/tests/pyproject.toml | 60 +++++++++++++++++++++ docs/tutorials/wiki2/src/tests/pytest.ini | 6 --- docs/tutorials/wiki2/src/tests/setup.py | 63 ---------------------- docs/tutorials/wiki2/src/tests/testing.ini | 8 +-- .../alembic/versions/20240204_07f9d6b626b2.py | 52 ++++++++++++++++++ .../alembic/versions/20240204_4b6614165904.py | 33 ++++++++++++ .../wiki2/src/tests/tutorial/models/meta.py | 28 +++++----- .../wiki2/src/tests/tutorial/models/page.py | 20 +++---- .../wiki2/src/tests/tutorial/models/user.py | 17 +++--- docs/tutorials/wiki2/src/tests/tutorial/routes.py | 10 +++- .../tutorials/wiki2/src/tests/tutorial/security.py | 2 +- .../wiki2/src/tests/tutorial/views/auth.py | 9 ++-- .../wiki2/src/tests/tutorial/views/default.py | 8 +-- 20 files changed, 266 insertions(+), 178 deletions(-) delete mode 100644 docs/tutorials/wiki2/src/tests/.coveragerc delete mode 100644 docs/tutorials/wiki2/src/tests/CHANGES.txt create mode 100644 docs/tutorials/wiki2/src/tests/README.md delete mode 100644 docs/tutorials/wiki2/src/tests/README.txt create mode 100644 docs/tutorials/wiki2/src/tests/pyproject.toml delete mode 100644 docs/tutorials/wiki2/src/tests/pytest.ini delete mode 100644 docs/tutorials/wiki2/src/tests/setup.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/alembic/versions/20240204_07f9d6b626b2.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/alembic/versions/20240204_4b6614165904.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/tests/.coveragerc b/docs/tutorials/wiki2/src/tests/.coveragerc deleted file mode 100644 index 5db0e79cf..000000000 --- a/docs/tutorials/wiki2/src/tests/.coveragerc +++ /dev/null @@ -1,2 +0,0 @@ -[run] -source = tutorial diff --git a/docs/tutorials/wiki2/src/tests/CHANGES.txt b/docs/tutorials/wiki2/src/tests/CHANGES.txt deleted file mode 100644 index 14b902fd1..000000000 --- a/docs/tutorials/wiki2/src/tests/CHANGES.txt +++ /dev/null @@ -1,4 +0,0 @@ -0.0 ---- - -- Initial version. diff --git a/docs/tutorials/wiki2/src/tests/MANIFEST.in b/docs/tutorials/wiki2/src/tests/MANIFEST.in index b4624fd1c..201692c1b 100644 --- a/docs/tutorials/wiki2/src/tests/MANIFEST.in +++ b/docs/tutorials/wiki2/src/tests/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/tests/README.md b/docs/tutorials/wiki2/src/tests/README.md new file mode 100644 index 000000000..5ac1209d0 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/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/tests/README.txt b/docs/tutorials/wiki2/src/tests/README.txt deleted file mode 100644 index ed6b88b49..000000000 --- a/docs/tutorials/wiki2/src/tests/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/tests/development.ini b/docs/tutorials/wiki2/src/tests/development.ini index 7fda4cb7b..7dcb83da0 100644 --- a/docs/tutorials/wiki2/src/tests/development.ini +++ b/docs/tutorials/wiki2/src/tests/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/tests/production.ini b/docs/tutorials/wiki2/src/tests/production.ini index 8e878a707..b4c18b119 100644 --- a/docs/tutorials/wiki2/src/tests/production.ini +++ b/docs/tutorials/wiki2/src/tests/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/tests/pyproject.toml b/docs/tutorials/wiki2/src/tests/pyproject.toml new file mode 100644 index 000000000..2b054c578 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/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/tests/pytest.ini b/docs/tutorials/wiki2/src/tests/pytest.ini deleted file mode 100644 index 3df78fe9d..000000000 --- a/docs/tutorials/wiki2/src/tests/pytest.ini +++ /dev/null @@ -1,6 +0,0 @@ -[pytest] -addopts = --strict-markers - -testpaths = - tutorial - tests diff --git a/docs/tutorials/wiki2/src/tests/setup.py b/docs/tutorials/wiki2/src/tests/setup.py deleted file mode 100644 index 12eabaff2..000000000 --- a/docs/tutorials/wiki2/src/tests/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/tests/testing.ini b/docs/tutorials/wiki2/src/tests/testing.ini index d3c601f16..095666d67 100644 --- a/docs/tutorials/wiki2/src/tests/testing.ini +++ b/docs/tutorials/wiki2/src/tests/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/tests/tutorial/alembic/versions/20240204_07f9d6b626b2.py b/docs/tutorials/wiki2/src/tests/tutorial/alembic/versions/20240204_07f9d6b626b2.py new file mode 100644 index 000000000..329c00208 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/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/tests/tutorial/alembic/versions/20240204_4b6614165904.py b/docs/tutorials/wiki2/src/tests/tutorial/alembic/versions/20240204_4b6614165904.py new file mode 100644 index 000000000..4f7a97d30 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/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/tests/tutorial/models/meta.py b/docs/tutorials/wiki2/src/tests/tutorial/models/meta.py index d659c7857..38712bfd2 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/models/meta.py +++ b/docs/tutorials/wiki2/src/tests/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/tests/tutorial/models/page.py b/docs/tutorials/wiki2/src/tests/tutorial/models/page.py index 74ff1faf8..6cc9aae3c 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/models/page.py +++ b/docs/tutorials/wiki2/src/tests/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/tests/tutorial/models/user.py b/docs/tutorials/wiki2/src/tests/tutorial/models/user.py index 9228b48f7..926a66e64 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/models/user.py +++ b/docs/tutorials/wiki2/src/tests/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/tests/tutorial/routes.py b/docs/tutorials/wiki2/src/tests/tutorial/routes.py index fb352604d..7ee816132 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/routes.py +++ b/docs/tutorials/wiki2/src/tests/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/tests/tutorial/security.py b/docs/tutorials/wiki2/src/tests/tutorial/security.py index 18f0bd4c7..1f8fda099 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/security.py +++ b/docs/tutorials/wiki2/src/tests/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/tests/tutorial/views/auth.py b/docs/tutorials/wiki2/src/tests/tutorial/views/auth.py index 807ff3464..e97908b63 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/views/auth.py +++ b/docs/tutorials/wiki2/src/tests/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/tests/tutorial/views/default.py b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py index 4a2a66c84..50c34cbb5 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/tests/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 '%s' % (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) -- cgit v1.2.3 From 85e9b2af7d9fa19ec70a21e6ca27498389e19ba3 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 4 Feb 2024 20:16:30 -0700 Subject: fix query in test_views --- docs/tutorials/wiki2/src/tests/tests/test_views.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/tests/tests/test_views.py b/docs/tutorials/wiki2/src/tests/tests/test_views.py index e93b04b3c..dbf32799a 100644 --- a/docs/tutorials/wiki2/src/tests/tests/test_views.py +++ b/docs/tutorials/wiki2/src/tests/tests/test_views.py @@ -1,4 +1,5 @@ from pyramid.testing import DummySecurityPolicy +import sqlalchemy as sa from tutorial import models @@ -93,11 +94,9 @@ class Test_add_page: setUser(dummy_config, makeUser('foo', 'editor')) self._addRoutes(dummy_config) self._callFUT(dummy_request) - page = ( - dbsession.query(models.Page) - .filter_by(name='AnotherPage') - .one() - ) + page = dbsession.scalars( + sa.select(models.Page).where(models.Page.name == 'AnotherPage') + ).one() assert page.data == 'Hello yo!' class Test_edit_page: -- cgit v1.2.3