diff options
| author | Michael Merickel <michael@merickel.org> | 2018-08-04 19:05:58 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-08-04 19:05:58 -0500 |
| commit | 76c066c20f61fe99101e24c3781ed551e710e635 (patch) | |
| tree | 5433754a62d4ea5925bb1b1587bacc46b6f46f02 /docs/tutorials/wiki2/src | |
| parent | dc2eee4c65657da7d50bea8e2069f728ce012391 (diff) | |
| parent | 6dd7a85cf2b83ae0f34f4f00a129e8ba2b938fa0 (diff) | |
| download | pyramid-76c066c20f61fe99101e24c3781ed551e710e635.tar.gz pyramid-76c066c20f61fe99101e24c3781ed551e710e635.tar.bz2 pyramid-76c066c20f61fe99101e24c3781ed551e710e635.zip | |
Merge pull request #3307 from Pylons/alembic-cookiecutter-alchemy
Add Alembic support from alchemy cookiecutter
Diffstat (limited to 'docs/tutorials/wiki2/src')
101 files changed, 1348 insertions, 470 deletions
diff --git a/docs/tutorials/wiki2/src/authentication/.gitignore b/docs/tutorials/wiki2/src/authentication/.gitignore new file mode 100644 index 000000000..1853d983c --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/.gitignore @@ -0,0 +1,21 @@ +*.egg +*.egg-info +*.pyc +*$py.class +*~ +.coverage +coverage.xml +build/ +dist/ +.tox/ +nosetests.xml +env*/ +tmp/ +Data.fs* +*.sublime-project +*.sublime-workspace +.*.sw? +.sw? +.DS_Store +coverage +test diff --git a/docs/tutorials/wiki2/src/authentication/README.txt b/docs/tutorials/wiki2/src/authentication/README.txt index 7b33da610..5d5133e34 100644 --- a/docs/tutorials/wiki2/src/authentication/README.txt +++ b/docs/tutorials/wiki2/src/authentication/README.txt @@ -20,7 +20,17 @@ Getting Started env/bin/pip install -e ".[testing]" -- Configure the database. +- 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 diff --git a/docs/tutorials/wiki2/src/authentication/development.ini b/docs/tutorials/wiki2/src/authentication/development.ini index cc2a5586e..d76a6cd72 100644 --- a/docs/tutorials/wiki2/src/authentication/development.ini +++ b/docs/tutorials/wiki2/src/authentication/development.ini @@ -28,6 +28,12 @@ auth.secret = seekrit # 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 + [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 759807abf..c46adb7b5 100644 --- a/docs/tutorials/wiki2/src/authentication/production.ini +++ b/docs/tutorials/wiki2/src/authentication/production.ini @@ -22,6 +22,12 @@ auth.secret = real-seekrit # 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 + [server:main] use = egg:waitress#main listen = *:6543 diff --git a/docs/tutorials/wiki2/src/authentication/pytest.ini b/docs/tutorials/wiki2/src/authentication/pytest.ini index 8b76bc410..a3489cdf8 100644 --- a/docs/tutorials/wiki2/src/authentication/pytest.ini +++ b/docs/tutorials/wiki2/src/authentication/pytest.ini @@ -1,3 +1,3 @@ [pytest] testpaths = tutorial -python_files = *.py +python_files = test*.py diff --git a/docs/tutorials/wiki2/src/authentication/setup.py b/docs/tutorials/wiki2/src/authentication/setup.py index abc24876d..28b766cbe 100644 --- a/docs/tutorials/wiki2/src/authentication/setup.py +++ b/docs/tutorials/wiki2/src/authentication/setup.py @@ -9,10 +9,11 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: CHANGES = f.read() requires = [ + 'alembic', 'bcrypt', 'docutils', 'plaster_pastedeploy', - 'pyramid >= 1.9a', + 'pyramid >= 1.9', 'pyramid_debugtoolbar', 'pyramid_jinja2', 'pyramid_retry', @@ -56,7 +57,7 @@ setup( 'main = tutorial:main', ], 'console_scripts': [ - 'initialize_tutorial_db = tutorial.scripts.initializedb:main', + 'initialize_tutorial_db = tutorial.scripts.initialize_db:main', ], }, ) diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/alembic/env.py b/docs/tutorials/wiki2/src/authentication/tutorial/alembic/env.py new file mode 100644 index 000000000..ba116d0f3 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/alembic/env.py @@ -0,0 +1,58 @@ +"""Pyramid bootstrap environment. """ +from alembic import context +from pyramid.paster import get_appsettings, setup_logging +from sqlalchemy import engine_from_config + +from tutorial.models.meta import Base + +config = context.config + +setup_logging(config.config_file_name) + +settings = get_appsettings(config.config_file_name) +target_metadata = Base.metadata + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + context.configure(url=settings['sqlalchemy.url']) + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + engine = engine_from_config(settings, prefix='sqlalchemy.') + + connection = engine.connect() + context.configure( + connection=connection, + target_metadata=target_metadata + ) + + try: + with context.begin_transaction(): + context.run_migrations() + finally: + connection.close() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/alembic/script.py.mako b/docs/tutorials/wiki2/src/authentication/tutorial/alembic/script.py.mako new file mode 100644 index 000000000..2c0156303 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/alembic/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/alembic/versions/README.txt b/docs/tutorials/wiki2/src/authentication/tutorial/alembic/versions/README.txt new file mode 100644 index 000000000..09ed32c8d --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/alembic/versions/README.txt @@ -0,0 +1 @@ +Placeholder for alembic versions
\ No newline at end of file diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/authentication/tutorial/models/__init__.py index 3c9ba8e54..a4209a6e9 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/authentication/tutorial/models/__init__.py @@ -5,8 +5,8 @@ import zope.sqlalchemy # import or define all models here to ensure they are attached to the # Base.metadata prior to any initialization routines -from .page import Page # noqa -from .user import User # noqa +from .page import Page # flake8: noqa +from .user import User # flake8: noqa # run configure_mappers after defining all of the models to ensure # all relationships can be setup diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/scripts/initialize_db.py b/docs/tutorials/wiki2/src/authentication/tutorial/scripts/initialize_db.py new file mode 100644 index 000000000..231d5d44b --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/scripts/initialize_db.py @@ -0,0 +1,56 @@ +import os +import sys + +from pyramid.paster import bootstrap, setup_logging +from sqlalchemy.exc import OperationalError + +from .. import models + + +def setup_models(dbsession): + editor = models.User(name='editor', role='editor') + editor.set_password('editor') + dbsession.add(editor) + + basic = models.User(name='basic', role='basic') + basic.set_password('basic') + dbsession.add(basic) + + page = models.Page( + name='FrontPage', + creator=editor, + data='This is the front page', + ) + dbsession.add(page) + + +def usage(argv): + cmd = os.path.basename(argv[0]) + print('usage: %s <config_uri>\n' + '(example: "%s development.ini")' % (cmd, cmd)) + sys.exit(1) + + +def main(argv=sys.argv): + if len(argv) != 2: + usage(argv) + config_uri = argv[1] + setup_logging(config_uri) + env = bootstrap(config_uri) + + try: + with env['request'].tm: + dbsession = env['request'].dbsession + setup_models(dbsession) + except OperationalError: + print(''' +Pyramid is having a problem using your SQL database. The problem +might be caused by one of the following things: + +1. You may need to initialize your database tables with `alembic`. + Check your README.txt for description and try to run it. + +2. Your database server may not be running. Check that the + database server referred to by the "sqlalchemy.url" setting in + your "development.ini" file is running. + ''') diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/authentication/tutorial/scripts/initializedb.py deleted file mode 100644 index f3c0a6fef..000000000 --- a/docs/tutorials/wiki2/src/authentication/tutorial/scripts/initializedb.py +++ /dev/null @@ -1,57 +0,0 @@ -import os -import sys -import transaction - -from pyramid.paster import ( - get_appsettings, - setup_logging, - ) - -from pyramid.scripts.common import parse_vars - -from ..models.meta import Base -from ..models import ( - get_engine, - get_session_factory, - get_tm_session, - ) -from ..models import Page, User - - -def usage(argv): - cmd = os.path.basename(argv[0]) - print('usage: %s <config_uri> [var=value]\n' - '(example: "%s development.ini")' % (cmd, cmd)) - sys.exit(1) - - -def main(argv=sys.argv): - if len(argv) < 2: - usage(argv) - config_uri = argv[1] - options = parse_vars(argv[2:]) - setup_logging(config_uri) - settings = get_appsettings(config_uri, options=options) - - engine = get_engine(settings) - Base.metadata.create_all(engine) - - session_factory = get_session_factory(engine) - - with transaction.manager: - dbsession = get_tm_session(session_factory, transaction.manager) - - editor = User(name='editor', role='editor') - editor.set_password('editor') - dbsession.add(editor) - - basic = User(name='basic', role='basic') - basic.set_password('basic') - dbsession.add(basic) - - page = Page( - name='FrontPage', - creator=editor, - data='This is the front page', - ) - dbsession.add(page) diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/authentication/tutorial/templates/layout.jinja2 index b98ccbd95..4016b26c9 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/templates/layout.jinja2 +++ b/docs/tutorials/wiki2/src/authentication/tutorial/templates/layout.jinja2 @@ -29,7 +29,7 @@ <div class="container"> <div class="row"> <div class="col-md-2"> - <img class="logo img-responsive" src="{{request.static_url('tutorial:static/pyramid.png')}}" alt="pyramid web framework"> + <img class="logo img-responsive" src="{{request.static_url('tutorial:static/pyramid.png') }}" alt="pyramid web framework"> </div> <div class="col-md-10"> <div class="content"> diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py b/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py index 2d058d874..8ed90d5b2 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py @@ -10,7 +10,7 @@ from pyramid.httpexceptions import ( from pyramid.view import view_config -from ..models import Page +from .. import models # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") @@ -23,13 +23,13 @@ def view_wiki(request): @view_config(route_name='view_page', renderer='../templates/view.jinja2') def view_page(request): pagename = request.matchdict['pagename'] - page = request.dbsession.query(Page).filter_by(name=pagename).first() + page = request.dbsession.query(models.Page).filter_by(name=pagename).first() if page is None: raise HTTPNotFound('No such page') def add_link(match): word = match.group(1) - exists = request.dbsession.query(Page).filter_by(name=word).all() + exists = request.dbsession.query(models.Page).filter_by(name=word).all() if exists: view_url = request.route_url('view_page', pagename=word) return '<a href="%s">%s</a>' % (view_url, escape(word)) @@ -45,7 +45,7 @@ def view_page(request): @view_config(route_name='edit_page', renderer='../templates/edit.jinja2') def edit_page(request): pagename = request.matchdict['pagename'] - page = request.dbsession.query(Page).filter_by(name=pagename).one() + page = request.dbsession.query(models.Page).filter_by(name=pagename).one() user = request.user if user is None or (user.role != 'editor' and page.creator != user): raise HTTPForbidden @@ -65,12 +65,12 @@ 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(Page).filter_by(name=pagename).count() > 0: + if request.dbsession.query(models.Page).filter_by(name=pagename).count() > 0: next_url = request.route_url('edit_page', pagename=pagename) return HTTPFound(location=next_url) if 'form.submitted' in request.params: body = request.params['body'] - page = Page(name=pagename, data=body) + page = models.Page(name=pagename, data=body) page.creator = request.user request.dbsession.add(page) next_url = request.route_url('view_page', pagename=pagename) diff --git a/docs/tutorials/wiki2/src/authorization/.gitignore b/docs/tutorials/wiki2/src/authorization/.gitignore new file mode 100644 index 000000000..1853d983c --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/.gitignore @@ -0,0 +1,21 @@ +*.egg +*.egg-info +*.pyc +*$py.class +*~ +.coverage +coverage.xml +build/ +dist/ +.tox/ +nosetests.xml +env*/ +tmp/ +Data.fs* +*.sublime-project +*.sublime-workspace +.*.sw? +.sw? +.DS_Store +coverage +test diff --git a/docs/tutorials/wiki2/src/authorization/README.txt b/docs/tutorials/wiki2/src/authorization/README.txt index 7b33da610..5d5133e34 100644 --- a/docs/tutorials/wiki2/src/authorization/README.txt +++ b/docs/tutorials/wiki2/src/authorization/README.txt @@ -20,7 +20,17 @@ Getting Started env/bin/pip install -e ".[testing]" -- Configure the database. +- 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 diff --git a/docs/tutorials/wiki2/src/authorization/development.ini b/docs/tutorials/wiki2/src/authorization/development.ini index cc2a5586e..d76a6cd72 100644 --- a/docs/tutorials/wiki2/src/authorization/development.ini +++ b/docs/tutorials/wiki2/src/authorization/development.ini @@ -28,6 +28,12 @@ auth.secret = seekrit # 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 + [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 759807abf..c46adb7b5 100644 --- a/docs/tutorials/wiki2/src/authorization/production.ini +++ b/docs/tutorials/wiki2/src/authorization/production.ini @@ -22,6 +22,12 @@ auth.secret = real-seekrit # 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 + [server:main] use = egg:waitress#main listen = *:6543 diff --git a/docs/tutorials/wiki2/src/authorization/pytest.ini b/docs/tutorials/wiki2/src/authorization/pytest.ini index 8b76bc410..a3489cdf8 100644 --- a/docs/tutorials/wiki2/src/authorization/pytest.ini +++ b/docs/tutorials/wiki2/src/authorization/pytest.ini @@ -1,3 +1,3 @@ [pytest] testpaths = tutorial -python_files = *.py +python_files = test*.py diff --git a/docs/tutorials/wiki2/src/authorization/setup.py b/docs/tutorials/wiki2/src/authorization/setup.py index abc24876d..28b766cbe 100644 --- a/docs/tutorials/wiki2/src/authorization/setup.py +++ b/docs/tutorials/wiki2/src/authorization/setup.py @@ -9,10 +9,11 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: CHANGES = f.read() requires = [ + 'alembic', 'bcrypt', 'docutils', 'plaster_pastedeploy', - 'pyramid >= 1.9a', + 'pyramid >= 1.9', 'pyramid_debugtoolbar', 'pyramid_jinja2', 'pyramid_retry', @@ -56,7 +57,7 @@ setup( 'main = tutorial:main', ], 'console_scripts': [ - 'initialize_tutorial_db = tutorial.scripts.initializedb:main', + 'initialize_tutorial_db = tutorial.scripts.initialize_db:main', ], }, ) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/alembic/env.py b/docs/tutorials/wiki2/src/authorization/tutorial/alembic/env.py new file mode 100644 index 000000000..ba116d0f3 --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/alembic/env.py @@ -0,0 +1,58 @@ +"""Pyramid bootstrap environment. """ +from alembic import context +from pyramid.paster import get_appsettings, setup_logging +from sqlalchemy import engine_from_config + +from tutorial.models.meta import Base + +config = context.config + +setup_logging(config.config_file_name) + +settings = get_appsettings(config.config_file_name) +target_metadata = Base.metadata + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + context.configure(url=settings['sqlalchemy.url']) + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + engine = engine_from_config(settings, prefix='sqlalchemy.') + + connection = engine.connect() + context.configure( + connection=connection, + target_metadata=target_metadata + ) + + try: + with context.begin_transaction(): + context.run_migrations() + finally: + connection.close() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/alembic/script.py.mako b/docs/tutorials/wiki2/src/authorization/tutorial/alembic/script.py.mako new file mode 100644 index 000000000..2c0156303 --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/alembic/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/alembic/versions/README.txt b/docs/tutorials/wiki2/src/authorization/tutorial/alembic/versions/README.txt new file mode 100644 index 000000000..09ed32c8d --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/alembic/versions/README.txt @@ -0,0 +1 @@ +Placeholder for alembic versions
\ No newline at end of file diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py index 3c9ba8e54..a4209a6e9 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py @@ -5,8 +5,8 @@ import zope.sqlalchemy # import or define all models here to ensure they are attached to the # Base.metadata prior to any initialization routines -from .page import Page # noqa -from .user import User # noqa +from .page import Page # flake8: noqa +from .user import User # flake8: noqa # run configure_mappers after defining all of the models to ensure # all relationships can be setup diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/routes.py b/docs/tutorials/wiki2/src/authorization/tutorial/routes.py index f0a8b7f96..1fd45a994 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/routes.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/routes.py @@ -7,7 +7,7 @@ from pyramid.security import ( Everyone, ) -from .models import Page +from . import models def includeme(config): config.add_static_view('static', 'static', cache_max_age=3600) @@ -22,7 +22,7 @@ def includeme(config): def new_page_factory(request): pagename = request.matchdict['pagename'] - if request.dbsession.query(Page).filter_by(name=pagename).count() > 0: + if request.dbsession.query(models.Page).filter_by(name=pagename).count() > 0: next_url = request.route_url('edit_page', pagename=pagename) raise HTTPFound(location=next_url) return NewPage(pagename) @@ -39,7 +39,7 @@ class NewPage(object): def page_factory(request): pagename = request.matchdict['pagename'] - page = request.dbsession.query(Page).filter_by(name=pagename).first() + page = request.dbsession.query(models.Page).filter_by(name=pagename).first() if page is None: raise HTTPNotFound return PageResource(page) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initialize_db.py b/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initialize_db.py new file mode 100644 index 000000000..231d5d44b --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initialize_db.py @@ -0,0 +1,56 @@ +import os +import sys + +from pyramid.paster import bootstrap, setup_logging +from sqlalchemy.exc import OperationalError + +from .. import models + + +def setup_models(dbsession): + editor = models.User(name='editor', role='editor') + editor.set_password('editor') + dbsession.add(editor) + + basic = models.User(name='basic', role='basic') + basic.set_password('basic') + dbsession.add(basic) + + page = models.Page( + name='FrontPage', + creator=editor, + data='This is the front page', + ) + dbsession.add(page) + + +def usage(argv): + cmd = os.path.basename(argv[0]) + print('usage: %s <config_uri>\n' + '(example: "%s development.ini")' % (cmd, cmd)) + sys.exit(1) + + +def main(argv=sys.argv): + if len(argv) != 2: + usage(argv) + config_uri = argv[1] + setup_logging(config_uri) + env = bootstrap(config_uri) + + try: + with env['request'].tm: + dbsession = env['request'].dbsession + setup_models(dbsession) + except OperationalError: + print(''' +Pyramid is having a problem using your SQL database. The problem +might be caused by one of the following things: + +1. You may need to initialize your database tables with `alembic`. + Check your README.txt for description and try to run it. + +2. Your database server may not be running. Check that the + database server referred to by the "sqlalchemy.url" setting in + your "development.ini" file is running. + ''') diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py deleted file mode 100644 index f3c0a6fef..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py +++ /dev/null @@ -1,57 +0,0 @@ -import os -import sys -import transaction - -from pyramid.paster import ( - get_appsettings, - setup_logging, - ) - -from pyramid.scripts.common import parse_vars - -from ..models.meta import Base -from ..models import ( - get_engine, - get_session_factory, - get_tm_session, - ) -from ..models import Page, User - - -def usage(argv): - cmd = os.path.basename(argv[0]) - print('usage: %s <config_uri> [var=value]\n' - '(example: "%s development.ini")' % (cmd, cmd)) - sys.exit(1) - - -def main(argv=sys.argv): - if len(argv) < 2: - usage(argv) - config_uri = argv[1] - options = parse_vars(argv[2:]) - setup_logging(config_uri) - settings = get_appsettings(config_uri, options=options) - - engine = get_engine(settings) - Base.metadata.create_all(engine) - - session_factory = get_session_factory(engine) - - with transaction.manager: - dbsession = get_tm_session(session_factory, transaction.manager) - - editor = User(name='editor', role='editor') - editor.set_password('editor') - dbsession.add(editor) - - basic = User(name='basic', role='basic') - basic.set_password('basic') - dbsession.add(basic) - - page = Page( - name='FrontPage', - creator=editor, - data='This is the front page', - ) - dbsession.add(page) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/security.py b/docs/tutorials/wiki2/src/authorization/tutorial/security.py index 25cff7b05..1ce1c8753 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/security.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/security.py @@ -5,7 +5,7 @@ from pyramid.security import ( Everyone, ) -from .models import User +from . import models class MyAuthenticationPolicy(AuthTktAuthenticationPolicy): @@ -26,7 +26,7 @@ class MyAuthenticationPolicy(AuthTktAuthenticationPolicy): def get_user(request): user_id = request.unauthenticated_userid if user_id is not None: - user = request.dbsession.query(User).get(user_id) + user = request.dbsession.query(models.User).get(user_id) return user def includeme(config): diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 index b98ccbd95..4016b26c9 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 @@ -29,7 +29,7 @@ <div class="container"> <div class="row"> <div class="col-md-2"> - <img class="logo img-responsive" src="{{request.static_url('tutorial:static/pyramid.png')}}" alt="pyramid web framework"> + <img class="logo img-responsive" src="{{request.static_url('tutorial:static/pyramid.png') }}" alt="pyramid web framework"> </div> <div class="col-md-10"> <div class="content"> diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py index 99e95efd3..ce650ca7c 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py @@ -54,7 +54,7 @@ class TestMyViewSuccessCondition(BaseTest): from .views.default import my_view info = my_view(dummy_request(self.session)) self.assertEqual(info['one'].name, 'one') - self.assertEqual(info['project'], 'tutorial') + self.assertEqual(info['project'], 'myproj') class TestMyViewFailureCondition(BaseTest): diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py index 65c12ed3b..ad271fb46 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py @@ -5,7 +5,7 @@ from docutils.core import publish_parts from pyramid.httpexceptions import HTTPFound from pyramid.view import view_config -from ..models import Page +from .. import models # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") @@ -22,7 +22,7 @@ def view_page(request): def add_link(match): word = match.group(1) - exists = request.dbsession.query(Page).filter_by(name=word).all() + exists = request.dbsession.query(models.Page).filter_by(name=word).all() if exists: view_url = request.route_url('view_page', pagename=word) return '<a href="%s">%s</a>' % (view_url, escape(word)) @@ -55,7 +55,7 @@ def add_page(request): pagename = request.context.pagename if 'form.submitted' in request.params: body = request.params['body'] - page = Page(name=pagename, data=body) + page = models.Page(name=pagename, data=body) page.creator = request.user request.dbsession.add(page) next_url = request.route_url('view_page', pagename=pagename) diff --git a/docs/tutorials/wiki2/src/basiclayout/.gitignore b/docs/tutorials/wiki2/src/basiclayout/.gitignore new file mode 100644 index 000000000..1853d983c --- /dev/null +++ b/docs/tutorials/wiki2/src/basiclayout/.gitignore @@ -0,0 +1,21 @@ +*.egg +*.egg-info +*.pyc +*$py.class +*~ +.coverage +coverage.xml +build/ +dist/ +.tox/ +nosetests.xml +env*/ +tmp/ +Data.fs* +*.sublime-project +*.sublime-workspace +.*.sw? +.sw? +.DS_Store +coverage +test diff --git a/docs/tutorials/wiki2/src/basiclayout/README.txt b/docs/tutorials/wiki2/src/basiclayout/README.txt index 7b33da610..5d5133e34 100644 --- a/docs/tutorials/wiki2/src/basiclayout/README.txt +++ b/docs/tutorials/wiki2/src/basiclayout/README.txt @@ -20,7 +20,17 @@ Getting Started env/bin/pip install -e ".[testing]" -- Configure the database. +- 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 diff --git a/docs/tutorials/wiki2/src/basiclayout/development.ini b/docs/tutorials/wiki2/src/basiclayout/development.ini index 4a67896b2..ee050c0ea 100644 --- a/docs/tutorials/wiki2/src/basiclayout/development.ini +++ b/docs/tutorials/wiki2/src/basiclayout/development.ini @@ -26,6 +26,12 @@ retry.attempts = 3 # 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 + [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 a28e47a83..91d0f5ddb 100644 --- a/docs/tutorials/wiki2/src/basiclayout/production.ini +++ b/docs/tutorials/wiki2/src/basiclayout/production.ini @@ -20,6 +20,12 @@ retry.attempts = 3 # 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 + [server:main] use = egg:waitress#main listen = *:6543 diff --git a/docs/tutorials/wiki2/src/basiclayout/pytest.ini b/docs/tutorials/wiki2/src/basiclayout/pytest.ini index 8b76bc410..a3489cdf8 100644 --- a/docs/tutorials/wiki2/src/basiclayout/pytest.ini +++ b/docs/tutorials/wiki2/src/basiclayout/pytest.ini @@ -1,3 +1,3 @@ [pytest] testpaths = tutorial -python_files = *.py +python_files = test*.py diff --git a/docs/tutorials/wiki2/src/basiclayout/setup.py b/docs/tutorials/wiki2/src/basiclayout/setup.py index 9fc5519a5..e0cc964fe 100644 --- a/docs/tutorials/wiki2/src/basiclayout/setup.py +++ b/docs/tutorials/wiki2/src/basiclayout/setup.py @@ -9,8 +9,9 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: CHANGES = f.read() requires = [ + 'alembic', 'plaster_pastedeploy', - 'pyramid >= 1.9a', + 'pyramid >= 1.9', 'pyramid_debugtoolbar', 'pyramid_jinja2', 'pyramid_retry', @@ -54,7 +55,7 @@ setup( 'main = tutorial:main', ], 'console_scripts': [ - 'initialize_tutorial_db = tutorial.scripts.initializedb:main', + 'initialize_tutorial_db = tutorial.scripts.initialize_db:main', ], }, ) diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/alembic/env.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/alembic/env.py new file mode 100644 index 000000000..ba116d0f3 --- /dev/null +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/alembic/env.py @@ -0,0 +1,58 @@ +"""Pyramid bootstrap environment. """ +from alembic import context +from pyramid.paster import get_appsettings, setup_logging +from sqlalchemy import engine_from_config + +from tutorial.models.meta import Base + +config = context.config + +setup_logging(config.config_file_name) + +settings = get_appsettings(config.config_file_name) +target_metadata = Base.metadata + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + context.configure(url=settings['sqlalchemy.url']) + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + engine = engine_from_config(settings, prefix='sqlalchemy.') + + connection = engine.connect() + context.configure( + connection=connection, + target_metadata=target_metadata + ) + + try: + with context.begin_transaction(): + context.run_migrations() + finally: + connection.close() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/alembic/script.py.mako b/docs/tutorials/wiki2/src/basiclayout/tutorial/alembic/script.py.mako new file mode 100644 index 000000000..2c0156303 --- /dev/null +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/alembic/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/alembic/versions/README.txt b/docs/tutorials/wiki2/src/basiclayout/tutorial/alembic/versions/README.txt new file mode 100644 index 000000000..09ed32c8d --- /dev/null +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/alembic/versions/README.txt @@ -0,0 +1 @@ +Placeholder for alembic versions
\ No newline at end of file diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initialize_db.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initialize_db.py new file mode 100644 index 000000000..b882f9bf7 --- /dev/null +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initialize_db.py @@ -0,0 +1,44 @@ +import os +import sys + +from pyramid.paster import bootstrap, setup_logging +from sqlalchemy.exc import OperationalError + +from .. import models + + +def setup_models(dbsession): + model = models.mymodel.MyModel(name='one', value=1) + dbsession.add(model) + + +def usage(argv): + cmd = os.path.basename(argv[0]) + print('usage: %s <config_uri>\n' + '(example: "%s development.ini")' % (cmd, cmd)) + sys.exit(1) + + +def main(argv=sys.argv): + if len(argv) != 2: + usage(argv) + config_uri = argv[1] + setup_logging(config_uri) + env = bootstrap(config_uri) + + try: + with env['request'].tm: + dbsession = env['request'].dbsession + setup_models(dbsession) + except OperationalError: + print(''' +Pyramid is having a problem using your SQL database. The problem +might be caused by one of the following things: + +1. You may need to initialize your database tables with `alembic`. + Check your README.txt for description and try to run it. + +2. Your database server may not be running. Check that the + database server referred to by the "sqlalchemy.url" setting in + your "development.ini" file is running. + ''') diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initializedb.py deleted file mode 100644 index 7307ecc5c..000000000 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initializedb.py +++ /dev/null @@ -1,45 +0,0 @@ -import os -import sys -import transaction - -from pyramid.paster import ( - get_appsettings, - setup_logging, - ) - -from pyramid.scripts.common import parse_vars - -from ..models.meta import Base -from ..models import ( - get_engine, - get_session_factory, - get_tm_session, - ) -from ..models import MyModel - - -def usage(argv): - cmd = os.path.basename(argv[0]) - print('usage: %s <config_uri> [var=value]\n' - '(example: "%s development.ini")' % (cmd, cmd)) - sys.exit(1) - - -def main(argv=sys.argv): - if len(argv) < 2: - usage(argv) - config_uri = argv[1] - options = parse_vars(argv[2:]) - setup_logging(config_uri) - settings = get_appsettings(config_uri, options=options) - - engine = get_engine(settings) - Base.metadata.create_all(engine) - - session_factory = get_session_factory(engine) - - with transaction.manager: - dbsession = get_tm_session(session_factory, transaction.manager) - - model = MyModel(name='one', value=1) - dbsession.add(model) diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.jinja2 b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.jinja2 index 26d72c0a6..d8b0a4232 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.jinja2 +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.jinja2 @@ -5,4 +5,4 @@ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Alchemy project</span></h1> <p class="lead">Welcome to <span class="font-normal">{{project}}</span>, a Pyramid application generated by<br><span class="font-normal">Cookiecutter</span>.</p> </div> -{% endblock content %} +{% endblock content %}
\ No newline at end of file diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/views/default.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/views/default.py index a404d4154..ef69ff895 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/views/default.py @@ -3,14 +3,14 @@ from pyramid.view import view_config from sqlalchemy.exc import DBAPIError -from ..models import MyModel +from .. import models @view_config(route_name='home', renderer='../templates/mytemplate.jinja2') def my_view(request): try: - query = request.dbsession.query(MyModel) - one = query.filter(MyModel.name == 'one').first() + query = request.dbsession.query(models.MyModel) + one = query.filter(models.MyModel.name == 'one').first() except DBAPIError: return Response(db_err_msg, content_type='text/plain', status=500) return {'one': one, 'project': 'myproj'} @@ -20,9 +20,8 @@ db_err_msg = """\ Pyramid is having a problem using your SQL database. The problem might be caused by one of the following things: -1. You may need to run the "initialize_tutorial_db" script - to initialize your database tables. Check your virtual - environment's "bin" directory for this script and try to run it. +1. You may need to initialize your database tables with `alembic`. + Check your README.txt for description and try to run it. 2. Your database server may not be running. Check that the database server referred to by the "sqlalchemy.url" setting in diff --git a/docs/tutorials/wiki2/src/installation/.gitignore b/docs/tutorials/wiki2/src/installation/.gitignore new file mode 100644 index 000000000..1853d983c --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/.gitignore @@ -0,0 +1,21 @@ +*.egg +*.egg-info +*.pyc +*$py.class +*~ +.coverage +coverage.xml +build/ +dist/ +.tox/ +nosetests.xml +env*/ +tmp/ +Data.fs* +*.sublime-project +*.sublime-workspace +.*.sw? +.sw? +.DS_Store +coverage +test diff --git a/docs/tutorials/wiki2/src/installation/README.txt b/docs/tutorials/wiki2/src/installation/README.txt index 7b33da610..5d5133e34 100644 --- a/docs/tutorials/wiki2/src/installation/README.txt +++ b/docs/tutorials/wiki2/src/installation/README.txt @@ -20,7 +20,17 @@ Getting Started env/bin/pip install -e ".[testing]" -- Configure the database. +- 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 diff --git a/docs/tutorials/wiki2/src/installation/development.ini b/docs/tutorials/wiki2/src/installation/development.ini index 4a67896b2..ee050c0ea 100644 --- a/docs/tutorials/wiki2/src/installation/development.ini +++ b/docs/tutorials/wiki2/src/installation/development.ini @@ -26,6 +26,12 @@ retry.attempts = 3 # 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 + [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 a28e47a83..91d0f5ddb 100644 --- a/docs/tutorials/wiki2/src/installation/production.ini +++ b/docs/tutorials/wiki2/src/installation/production.ini @@ -20,6 +20,12 @@ retry.attempts = 3 # 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 + [server:main] use = egg:waitress#main listen = *:6543 diff --git a/docs/tutorials/wiki2/src/installation/pytest.ini b/docs/tutorials/wiki2/src/installation/pytest.ini index 8b76bc410..a3489cdf8 100644 --- a/docs/tutorials/wiki2/src/installation/pytest.ini +++ b/docs/tutorials/wiki2/src/installation/pytest.ini @@ -1,3 +1,3 @@ [pytest] testpaths = tutorial -python_files = *.py +python_files = test*.py diff --git a/docs/tutorials/wiki2/src/installation/setup.py b/docs/tutorials/wiki2/src/installation/setup.py index 9fc5519a5..e0cc964fe 100644 --- a/docs/tutorials/wiki2/src/installation/setup.py +++ b/docs/tutorials/wiki2/src/installation/setup.py @@ -9,8 +9,9 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: CHANGES = f.read() requires = [ + 'alembic', 'plaster_pastedeploy', - 'pyramid >= 1.9a', + 'pyramid >= 1.9', 'pyramid_debugtoolbar', 'pyramid_jinja2', 'pyramid_retry', @@ -54,7 +55,7 @@ setup( 'main = tutorial:main', ], 'console_scripts': [ - 'initialize_tutorial_db = tutorial.scripts.initializedb:main', + 'initialize_tutorial_db = tutorial.scripts.initialize_db:main', ], }, ) diff --git a/docs/tutorials/wiki2/src/installation/tutorial/alembic/env.py b/docs/tutorials/wiki2/src/installation/tutorial/alembic/env.py new file mode 100644 index 000000000..ba116d0f3 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/alembic/env.py @@ -0,0 +1,58 @@ +"""Pyramid bootstrap environment. """ +from alembic import context +from pyramid.paster import get_appsettings, setup_logging +from sqlalchemy import engine_from_config + +from tutorial.models.meta import Base + +config = context.config + +setup_logging(config.config_file_name) + +settings = get_appsettings(config.config_file_name) +target_metadata = Base.metadata + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + context.configure(url=settings['sqlalchemy.url']) + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + engine = engine_from_config(settings, prefix='sqlalchemy.') + + connection = engine.connect() + context.configure( + connection=connection, + target_metadata=target_metadata + ) + + try: + with context.begin_transaction(): + context.run_migrations() + finally: + connection.close() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/docs/tutorials/wiki2/src/installation/tutorial/alembic/script.py.mako b/docs/tutorials/wiki2/src/installation/tutorial/alembic/script.py.mako new file mode 100644 index 000000000..2c0156303 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/alembic/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/docs/tutorials/wiki2/src/installation/tutorial/alembic/versions/README.txt b/docs/tutorials/wiki2/src/installation/tutorial/alembic/versions/README.txt new file mode 100644 index 000000000..09ed32c8d --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/alembic/versions/README.txt @@ -0,0 +1 @@ +Placeholder for alembic versions
\ No newline at end of file diff --git a/docs/tutorials/wiki2/src/installation/tutorial/scripts/initialize_db.py b/docs/tutorials/wiki2/src/installation/tutorial/scripts/initialize_db.py new file mode 100644 index 000000000..b882f9bf7 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/scripts/initialize_db.py @@ -0,0 +1,44 @@ +import os +import sys + +from pyramid.paster import bootstrap, setup_logging +from sqlalchemy.exc import OperationalError + +from .. import models + + +def setup_models(dbsession): + model = models.mymodel.MyModel(name='one', value=1) + dbsession.add(model) + + +def usage(argv): + cmd = os.path.basename(argv[0]) + print('usage: %s <config_uri>\n' + '(example: "%s development.ini")' % (cmd, cmd)) + sys.exit(1) + + +def main(argv=sys.argv): + if len(argv) != 2: + usage(argv) + config_uri = argv[1] + setup_logging(config_uri) + env = bootstrap(config_uri) + + try: + with env['request'].tm: + dbsession = env['request'].dbsession + setup_models(dbsession) + except OperationalError: + print(''' +Pyramid is having a problem using your SQL database. The problem +might be caused by one of the following things: + +1. You may need to initialize your database tables with `alembic`. + Check your README.txt for description and try to run it. + +2. Your database server may not be running. Check that the + database server referred to by the "sqlalchemy.url" setting in + your "development.ini" file is running. + ''') diff --git a/docs/tutorials/wiki2/src/installation/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/installation/tutorial/scripts/initializedb.py deleted file mode 100644 index 7307ecc5c..000000000 --- a/docs/tutorials/wiki2/src/installation/tutorial/scripts/initializedb.py +++ /dev/null @@ -1,45 +0,0 @@ -import os -import sys -import transaction - -from pyramid.paster import ( - get_appsettings, - setup_logging, - ) - -from pyramid.scripts.common import parse_vars - -from ..models.meta import Base -from ..models import ( - get_engine, - get_session_factory, - get_tm_session, - ) -from ..models import MyModel - - -def usage(argv): - cmd = os.path.basename(argv[0]) - print('usage: %s <config_uri> [var=value]\n' - '(example: "%s development.ini")' % (cmd, cmd)) - sys.exit(1) - - -def main(argv=sys.argv): - if len(argv) < 2: - usage(argv) - config_uri = argv[1] - options = parse_vars(argv[2:]) - setup_logging(config_uri) - settings = get_appsettings(config_uri, options=options) - - engine = get_engine(settings) - Base.metadata.create_all(engine) - - session_factory = get_session_factory(engine) - - with transaction.manager: - dbsession = get_tm_session(session_factory, transaction.manager) - - model = MyModel(name='one', value=1) - dbsession.add(model) diff --git a/docs/tutorials/wiki2/src/installation/tutorial/templates/mytemplate.jinja2 b/docs/tutorials/wiki2/src/installation/tutorial/templates/mytemplate.jinja2 index 26d72c0a6..d8b0a4232 100644 --- a/docs/tutorials/wiki2/src/installation/tutorial/templates/mytemplate.jinja2 +++ b/docs/tutorials/wiki2/src/installation/tutorial/templates/mytemplate.jinja2 @@ -5,4 +5,4 @@ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Alchemy project</span></h1> <p class="lead">Welcome to <span class="font-normal">{{project}}</span>, a Pyramid application generated by<br><span class="font-normal">Cookiecutter</span>.</p> </div> -{% endblock content %} +{% endblock content %}
\ No newline at end of file diff --git a/docs/tutorials/wiki2/src/installation/tutorial/views/default.py b/docs/tutorials/wiki2/src/installation/tutorial/views/default.py index a404d4154..ef69ff895 100644 --- a/docs/tutorials/wiki2/src/installation/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/installation/tutorial/views/default.py @@ -3,14 +3,14 @@ from pyramid.view import view_config from sqlalchemy.exc import DBAPIError -from ..models import MyModel +from .. import models @view_config(route_name='home', renderer='../templates/mytemplate.jinja2') def my_view(request): try: - query = request.dbsession.query(MyModel) - one = query.filter(MyModel.name == 'one').first() + query = request.dbsession.query(models.MyModel) + one = query.filter(models.MyModel.name == 'one').first() except DBAPIError: return Response(db_err_msg, content_type='text/plain', status=500) return {'one': one, 'project': 'myproj'} @@ -20,9 +20,8 @@ db_err_msg = """\ Pyramid is having a problem using your SQL database. The problem might be caused by one of the following things: -1. You may need to run the "initialize_tutorial_db" script - to initialize your database tables. Check your virtual - environment's "bin" directory for this script and try to run it. +1. You may need to initialize your database tables with `alembic`. + Check your README.txt for description and try to run it. 2. Your database server may not be running. Check that the database server referred to by the "sqlalchemy.url" setting in diff --git a/docs/tutorials/wiki2/src/models/.gitignore b/docs/tutorials/wiki2/src/models/.gitignore new file mode 100644 index 000000000..1853d983c --- /dev/null +++ b/docs/tutorials/wiki2/src/models/.gitignore @@ -0,0 +1,21 @@ +*.egg +*.egg-info +*.pyc +*$py.class +*~ +.coverage +coverage.xml +build/ +dist/ +.tox/ +nosetests.xml +env*/ +tmp/ +Data.fs* +*.sublime-project +*.sublime-workspace +.*.sw? +.sw? +.DS_Store +coverage +test diff --git a/docs/tutorials/wiki2/src/models/README.txt b/docs/tutorials/wiki2/src/models/README.txt index 7b33da610..5d5133e34 100644 --- a/docs/tutorials/wiki2/src/models/README.txt +++ b/docs/tutorials/wiki2/src/models/README.txt @@ -20,7 +20,17 @@ Getting Started env/bin/pip install -e ".[testing]" -- Configure the database. +- 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 diff --git a/docs/tutorials/wiki2/src/models/development.ini b/docs/tutorials/wiki2/src/models/development.ini index 4a67896b2..ee050c0ea 100644 --- a/docs/tutorials/wiki2/src/models/development.ini +++ b/docs/tutorials/wiki2/src/models/development.ini @@ -26,6 +26,12 @@ retry.attempts = 3 # 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 + [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 a28e47a83..91d0f5ddb 100644 --- a/docs/tutorials/wiki2/src/models/production.ini +++ b/docs/tutorials/wiki2/src/models/production.ini @@ -20,6 +20,12 @@ retry.attempts = 3 # 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 + [server:main] use = egg:waitress#main listen = *:6543 diff --git a/docs/tutorials/wiki2/src/models/pytest.ini b/docs/tutorials/wiki2/src/models/pytest.ini index 8b76bc410..a3489cdf8 100644 --- a/docs/tutorials/wiki2/src/models/pytest.ini +++ b/docs/tutorials/wiki2/src/models/pytest.ini @@ -1,3 +1,3 @@ [pytest] testpaths = tutorial -python_files = *.py +python_files = test*.py diff --git a/docs/tutorials/wiki2/src/models/setup.py b/docs/tutorials/wiki2/src/models/setup.py index c688c6866..2576cc848 100644 --- a/docs/tutorials/wiki2/src/models/setup.py +++ b/docs/tutorials/wiki2/src/models/setup.py @@ -9,9 +9,10 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: CHANGES = f.read() requires = [ + 'alembic', 'bcrypt', 'plaster_pastedeploy', - 'pyramid >= 1.9a', + 'pyramid >= 1.9', 'pyramid_debugtoolbar', 'pyramid_jinja2', 'pyramid_retry', @@ -55,7 +56,7 @@ setup( 'main = tutorial:main', ], 'console_scripts': [ - 'initialize_tutorial_db = tutorial.scripts.initializedb:main', + 'initialize_tutorial_db = tutorial.scripts.initialize_db:main', ], }, ) diff --git a/docs/tutorials/wiki2/src/models/tutorial/alembic/env.py b/docs/tutorials/wiki2/src/models/tutorial/alembic/env.py new file mode 100644 index 000000000..ba116d0f3 --- /dev/null +++ b/docs/tutorials/wiki2/src/models/tutorial/alembic/env.py @@ -0,0 +1,58 @@ +"""Pyramid bootstrap environment. """ +from alembic import context +from pyramid.paster import get_appsettings, setup_logging +from sqlalchemy import engine_from_config + +from tutorial.models.meta import Base + +config = context.config + +setup_logging(config.config_file_name) + +settings = get_appsettings(config.config_file_name) +target_metadata = Base.metadata + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + context.configure(url=settings['sqlalchemy.url']) + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + engine = engine_from_config(settings, prefix='sqlalchemy.') + + connection = engine.connect() + context.configure( + connection=connection, + target_metadata=target_metadata + ) + + try: + with context.begin_transaction(): + context.run_migrations() + finally: + connection.close() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/docs/tutorials/wiki2/src/models/tutorial/alembic/script.py.mako b/docs/tutorials/wiki2/src/models/tutorial/alembic/script.py.mako new file mode 100644 index 000000000..2c0156303 --- /dev/null +++ b/docs/tutorials/wiki2/src/models/tutorial/alembic/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/docs/tutorials/wiki2/src/models/tutorial/alembic/versions/README.txt b/docs/tutorials/wiki2/src/models/tutorial/alembic/versions/README.txt new file mode 100644 index 000000000..09ed32c8d --- /dev/null +++ b/docs/tutorials/wiki2/src/models/tutorial/alembic/versions/README.txt @@ -0,0 +1 @@ +Placeholder for alembic versions
\ No newline at end of file diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py index 3c9ba8e54..a4209a6e9 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py @@ -5,8 +5,8 @@ import zope.sqlalchemy # import or define all models here to ensure they are attached to the # Base.metadata prior to any initialization routines -from .page import Page # noqa -from .user import User # noqa +from .page import Page # flake8: noqa +from .user import User # flake8: noqa # run configure_mappers after defining all of the models to ensure # all relationships can be setup diff --git a/docs/tutorials/wiki2/src/models/tutorial/scripts/initialize_db.py b/docs/tutorials/wiki2/src/models/tutorial/scripts/initialize_db.py new file mode 100644 index 000000000..231d5d44b --- /dev/null +++ b/docs/tutorials/wiki2/src/models/tutorial/scripts/initialize_db.py @@ -0,0 +1,56 @@ +import os +import sys + +from pyramid.paster import bootstrap, setup_logging +from sqlalchemy.exc import OperationalError + +from .. import models + + +def setup_models(dbsession): + editor = models.User(name='editor', role='editor') + editor.set_password('editor') + dbsession.add(editor) + + basic = models.User(name='basic', role='basic') + basic.set_password('basic') + dbsession.add(basic) + + page = models.Page( + name='FrontPage', + creator=editor, + data='This is the front page', + ) + dbsession.add(page) + + +def usage(argv): + cmd = os.path.basename(argv[0]) + print('usage: %s <config_uri>\n' + '(example: "%s development.ini")' % (cmd, cmd)) + sys.exit(1) + + +def main(argv=sys.argv): + if len(argv) != 2: + usage(argv) + config_uri = argv[1] + setup_logging(config_uri) + env = bootstrap(config_uri) + + try: + with env['request'].tm: + dbsession = env['request'].dbsession + setup_models(dbsession) + except OperationalError: + print(''' +Pyramid is having a problem using your SQL database. The problem +might be caused by one of the following things: + +1. You may need to initialize your database tables with `alembic`. + Check your README.txt for description and try to run it. + +2. Your database server may not be running. Check that the + database server referred to by the "sqlalchemy.url" setting in + your "development.ini" file is running. + ''') diff --git a/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py deleted file mode 100644 index f3c0a6fef..000000000 --- a/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py +++ /dev/null @@ -1,57 +0,0 @@ -import os -import sys -import transaction - -from pyramid.paster import ( - get_appsettings, - setup_logging, - ) - -from pyramid.scripts.common import parse_vars - -from ..models.meta import Base -from ..models import ( - get_engine, - get_session_factory, - get_tm_session, - ) -from ..models import Page, User - - -def usage(argv): - cmd = os.path.basename(argv[0]) - print('usage: %s <config_uri> [var=value]\n' - '(example: "%s development.ini")' % (cmd, cmd)) - sys.exit(1) - - -def main(argv=sys.argv): - if len(argv) < 2: - usage(argv) - config_uri = argv[1] - options = parse_vars(argv[2:]) - setup_logging(config_uri) - settings = get_appsettings(config_uri, options=options) - - engine = get_engine(settings) - Base.metadata.create_all(engine) - - session_factory = get_session_factory(engine) - - with transaction.manager: - dbsession = get_tm_session(session_factory, transaction.manager) - - editor = User(name='editor', role='editor') - editor.set_password('editor') - dbsession.add(editor) - - basic = User(name='basic', role='basic') - basic.set_password('basic') - dbsession.add(basic) - - page = Page( - name='FrontPage', - creator=editor, - data='This is the front page', - ) - dbsession.add(page) diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.jinja2 b/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.jinja2 index 26d72c0a6..d8b0a4232 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.jinja2 +++ b/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.jinja2 @@ -5,4 +5,4 @@ <h1><span class="font-semi-bold">Pyramid</span> <span class="smaller">Alchemy project</span></h1> <p class="lead">Welcome to <span class="font-normal">{{project}}</span>, a Pyramid application generated by<br><span class="font-normal">Cookiecutter</span>.</p> </div> -{% endblock content %} +{% endblock content %}
\ No newline at end of file diff --git a/docs/tutorials/wiki2/src/models/tutorial/views/default.py b/docs/tutorials/wiki2/src/models/tutorial/views/default.py index a404d4154..ef69ff895 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/models/tutorial/views/default.py @@ -3,14 +3,14 @@ from pyramid.view import view_config from sqlalchemy.exc import DBAPIError -from ..models import MyModel +from .. import models @view_config(route_name='home', renderer='../templates/mytemplate.jinja2') def my_view(request): try: - query = request.dbsession.query(MyModel) - one = query.filter(MyModel.name == 'one').first() + query = request.dbsession.query(models.MyModel) + one = query.filter(models.MyModel.name == 'one').first() except DBAPIError: return Response(db_err_msg, content_type='text/plain', status=500) return {'one': one, 'project': 'myproj'} @@ -20,9 +20,8 @@ db_err_msg = """\ Pyramid is having a problem using your SQL database. The problem might be caused by one of the following things: -1. You may need to run the "initialize_tutorial_db" script - to initialize your database tables. Check your virtual - environment's "bin" directory for this script and try to run it. +1. You may need to initialize your database tables with `alembic`. + Check your README.txt for description and try to run it. 2. Your database server may not be running. Check that the database server referred to by the "sqlalchemy.url" setting in diff --git a/docs/tutorials/wiki2/src/tests/.gitignore b/docs/tutorials/wiki2/src/tests/.gitignore new file mode 100644 index 000000000..1853d983c --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/.gitignore @@ -0,0 +1,21 @@ +*.egg +*.egg-info +*.pyc +*$py.class +*~ +.coverage +coverage.xml +build/ +dist/ +.tox/ +nosetests.xml +env*/ +tmp/ +Data.fs* +*.sublime-project +*.sublime-workspace +.*.sw? +.sw? +.DS_Store +coverage +test diff --git a/docs/tutorials/wiki2/src/tests/README.txt b/docs/tutorials/wiki2/src/tests/README.txt index 7b33da610..5d5133e34 100644 --- a/docs/tutorials/wiki2/src/tests/README.txt +++ b/docs/tutorials/wiki2/src/tests/README.txt @@ -20,7 +20,17 @@ Getting Started env/bin/pip install -e ".[testing]" -- Configure the database. +- 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 diff --git a/docs/tutorials/wiki2/src/tests/development.ini b/docs/tutorials/wiki2/src/tests/development.ini index cc2a5586e..d76a6cd72 100644 --- a/docs/tutorials/wiki2/src/tests/development.ini +++ b/docs/tutorials/wiki2/src/tests/development.ini @@ -28,6 +28,12 @@ auth.secret = seekrit # 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 + [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 759807abf..c46adb7b5 100644 --- a/docs/tutorials/wiki2/src/tests/production.ini +++ b/docs/tutorials/wiki2/src/tests/production.ini @@ -22,6 +22,12 @@ auth.secret = real-seekrit # 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 + [server:main] use = egg:waitress#main listen = *:6543 diff --git a/docs/tutorials/wiki2/src/tests/pytest.ini b/docs/tutorials/wiki2/src/tests/pytest.ini index 8b76bc410..a3489cdf8 100644 --- a/docs/tutorials/wiki2/src/tests/pytest.ini +++ b/docs/tutorials/wiki2/src/tests/pytest.ini @@ -1,3 +1,3 @@ [pytest] testpaths = tutorial -python_files = *.py +python_files = test*.py diff --git a/docs/tutorials/wiki2/src/tests/setup.py b/docs/tutorials/wiki2/src/tests/setup.py index abc24876d..28b766cbe 100644 --- a/docs/tutorials/wiki2/src/tests/setup.py +++ b/docs/tutorials/wiki2/src/tests/setup.py @@ -9,10 +9,11 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: CHANGES = f.read() requires = [ + 'alembic', 'bcrypt', 'docutils', 'plaster_pastedeploy', - 'pyramid >= 1.9a', + 'pyramid >= 1.9', 'pyramid_debugtoolbar', 'pyramid_jinja2', 'pyramid_retry', @@ -56,7 +57,7 @@ setup( 'main = tutorial:main', ], 'console_scripts': [ - 'initialize_tutorial_db = tutorial.scripts.initializedb:main', + 'initialize_tutorial_db = tutorial.scripts.initialize_db:main', ], }, ) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/alembic/env.py b/docs/tutorials/wiki2/src/tests/tutorial/alembic/env.py new file mode 100644 index 000000000..ba116d0f3 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/alembic/env.py @@ -0,0 +1,58 @@ +"""Pyramid bootstrap environment. """ +from alembic import context +from pyramid.paster import get_appsettings, setup_logging +from sqlalchemy import engine_from_config + +from tutorial.models.meta import Base + +config = context.config + +setup_logging(config.config_file_name) + +settings = get_appsettings(config.config_file_name) +target_metadata = Base.metadata + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + context.configure(url=settings['sqlalchemy.url']) + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + engine = engine_from_config(settings, prefix='sqlalchemy.') + + connection = engine.connect() + context.configure( + connection=connection, + target_metadata=target_metadata + ) + + try: + with context.begin_transaction(): + context.run_migrations() + finally: + connection.close() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/docs/tutorials/wiki2/src/tests/tutorial/alembic/script.py.mako b/docs/tutorials/wiki2/src/tests/tutorial/alembic/script.py.mako new file mode 100644 index 000000000..2c0156303 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/alembic/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/docs/tutorials/wiki2/src/tests/tutorial/alembic/versions/README.txt b/docs/tutorials/wiki2/src/tests/tutorial/alembic/versions/README.txt new file mode 100644 index 000000000..09ed32c8d --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/alembic/versions/README.txt @@ -0,0 +1 @@ +Placeholder for alembic versions
\ No newline at end of file diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py index 3c9ba8e54..a4209a6e9 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py @@ -5,8 +5,8 @@ import zope.sqlalchemy # import or define all models here to ensure they are attached to the # Base.metadata prior to any initialization routines -from .page import Page # noqa -from .user import User # noqa +from .page import Page # flake8: noqa +from .user import User # flake8: noqa # run configure_mappers after defining all of the models to ensure # all relationships can be setup diff --git a/docs/tutorials/wiki2/src/tests/tutorial/routes.py b/docs/tutorials/wiki2/src/tests/tutorial/routes.py index f0a8b7f96..1fd45a994 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/routes.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/routes.py @@ -7,7 +7,7 @@ from pyramid.security import ( Everyone, ) -from .models import Page +from . import models def includeme(config): config.add_static_view('static', 'static', cache_max_age=3600) @@ -22,7 +22,7 @@ def includeme(config): def new_page_factory(request): pagename = request.matchdict['pagename'] - if request.dbsession.query(Page).filter_by(name=pagename).count() > 0: + if request.dbsession.query(models.Page).filter_by(name=pagename).count() > 0: next_url = request.route_url('edit_page', pagename=pagename) raise HTTPFound(location=next_url) return NewPage(pagename) @@ -39,7 +39,7 @@ class NewPage(object): def page_factory(request): pagename = request.matchdict['pagename'] - page = request.dbsession.query(Page).filter_by(name=pagename).first() + page = request.dbsession.query(models.Page).filter_by(name=pagename).first() if page is None: raise HTTPNotFound return PageResource(page) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/scripts/initialize_db.py b/docs/tutorials/wiki2/src/tests/tutorial/scripts/initialize_db.py new file mode 100644 index 000000000..231d5d44b --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/scripts/initialize_db.py @@ -0,0 +1,56 @@ +import os +import sys + +from pyramid.paster import bootstrap, setup_logging +from sqlalchemy.exc import OperationalError + +from .. import models + + +def setup_models(dbsession): + editor = models.User(name='editor', role='editor') + editor.set_password('editor') + dbsession.add(editor) + + basic = models.User(name='basic', role='basic') + basic.set_password('basic') + dbsession.add(basic) + + page = models.Page( + name='FrontPage', + creator=editor, + data='This is the front page', + ) + dbsession.add(page) + + +def usage(argv): + cmd = os.path.basename(argv[0]) + print('usage: %s <config_uri>\n' + '(example: "%s development.ini")' % (cmd, cmd)) + sys.exit(1) + + +def main(argv=sys.argv): + if len(argv) != 2: + usage(argv) + config_uri = argv[1] + setup_logging(config_uri) + env = bootstrap(config_uri) + + try: + with env['request'].tm: + dbsession = env['request'].dbsession + setup_models(dbsession) + except OperationalError: + print(''' +Pyramid is having a problem using your SQL database. The problem +might be caused by one of the following things: + +1. You may need to initialize your database tables with `alembic`. + Check your README.txt for description and try to run it. + +2. Your database server may not be running. Check that the + database server referred to by the "sqlalchemy.url" setting in + your "development.ini" file is running. + ''') diff --git a/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py deleted file mode 100644 index c860ef8cf..000000000 --- a/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py +++ /dev/null @@ -1,58 +0,0 @@ -import os -import sys -import transaction - -from pyramid.paster import ( - get_appsettings, - setup_logging, - ) - -from pyramid.scripts.common import parse_vars - -from ..models.meta import Base -from ..models import ( - get_engine, - get_session_factory, - get_tm_session, - ) -from ..models import Page, User - - -def usage(argv): - cmd = os.path.basename(argv[0]) - print('usage: %s <config_uri> [var=value]\n' - '(example: "%s development.ini")' % (cmd, cmd)) - sys.exit(1) - - -def main(argv=sys.argv): - if len(argv) < 2: - usage(argv) - return - config_uri = argv[1] - options = parse_vars(argv[2:]) - setup_logging(config_uri) - settings = get_appsettings(config_uri, options=options) - - engine = get_engine(settings) - Base.metadata.create_all(engine) - - session_factory = get_session_factory(engine) - - with transaction.manager: - dbsession = get_tm_session(session_factory, transaction.manager) - - editor = User(name='editor', role='editor') - editor.set_password('editor') - dbsession.add(editor) - - basic = User(name='basic', role='basic') - basic.set_password('basic') - dbsession.add(basic) - - page = Page( - name='FrontPage', - creator=editor, - data='This is the front page', - ) - dbsession.add(page) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/security.py b/docs/tutorials/wiki2/src/tests/tutorial/security.py index 25cff7b05..1ce1c8753 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/security.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/security.py @@ -5,7 +5,7 @@ from pyramid.security import ( Everyone, ) -from .models import User +from . import models class MyAuthenticationPolicy(AuthTktAuthenticationPolicy): @@ -26,7 +26,7 @@ class MyAuthenticationPolicy(AuthTktAuthenticationPolicy): def get_user(request): user_id = request.unauthenticated_userid if user_id is not None: - user = request.dbsession.query(User).get(user_id) + user = request.dbsession.query(models.User).get(user_id) return user def includeme(config): diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 index b98ccbd95..4016b26c9 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 @@ -29,7 +29,7 @@ <div class="container"> <div class="row"> <div class="col-md-2"> - <img class="logo img-responsive" src="{{request.static_url('tutorial:static/pyramid.png')}}" alt="pyramid web framework"> + <img class="logo img-responsive" src="{{request.static_url('tutorial:static/pyramid.png') }}" alt="pyramid web framework"> </div> <div class="col-md-10"> <div class="content"> diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_initdb.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_initdb.py index f5273456e..72fbff04b 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_initdb.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_initdb.py @@ -5,12 +5,12 @@ import unittest class TestInitializeDB(unittest.TestCase): def test_usage(self): - from ..scripts.initializedb import main + from ..scripts.initialize_db import main with self.assertRaises(SystemExit): main(argv=['foo']) def test_run(self): - from ..scripts.initializedb import main + from ..scripts.initialize_db import main main(argv=['foo', 'development.ini']) self.assertTrue(os.path.exists('tutorial.sqlite')) os.remove('tutorial.sqlite') diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py index 65c12ed3b..ad271fb46 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py @@ -5,7 +5,7 @@ from docutils.core import publish_parts from pyramid.httpexceptions import HTTPFound from pyramid.view import view_config -from ..models import Page +from .. import models # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") @@ -22,7 +22,7 @@ def view_page(request): def add_link(match): word = match.group(1) - exists = request.dbsession.query(Page).filter_by(name=word).all() + exists = request.dbsession.query(models.Page).filter_by(name=word).all() if exists: view_url = request.route_url('view_page', pagename=word) return '<a href="%s">%s</a>' % (view_url, escape(word)) @@ -55,7 +55,7 @@ def add_page(request): pagename = request.context.pagename if 'form.submitted' in request.params: body = request.params['body'] - page = Page(name=pagename, data=body) + page = models.Page(name=pagename, data=body) page.creator = request.user request.dbsession.add(page) next_url = request.route_url('view_page', pagename=pagename) diff --git a/docs/tutorials/wiki2/src/views/.gitignore b/docs/tutorials/wiki2/src/views/.gitignore new file mode 100644 index 000000000..1853d983c --- /dev/null +++ b/docs/tutorials/wiki2/src/views/.gitignore @@ -0,0 +1,21 @@ +*.egg +*.egg-info +*.pyc +*$py.class +*~ +.coverage +coverage.xml +build/ +dist/ +.tox/ +nosetests.xml +env*/ +tmp/ +Data.fs* +*.sublime-project +*.sublime-workspace +.*.sw? +.sw? +.DS_Store +coverage +test diff --git a/docs/tutorials/wiki2/src/views/README.txt b/docs/tutorials/wiki2/src/views/README.txt index 7b33da610..5d5133e34 100644 --- a/docs/tutorials/wiki2/src/views/README.txt +++ b/docs/tutorials/wiki2/src/views/README.txt @@ -20,7 +20,17 @@ Getting Started env/bin/pip install -e ".[testing]" -- Configure the database. +- 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 diff --git a/docs/tutorials/wiki2/src/views/development.ini b/docs/tutorials/wiki2/src/views/development.ini index 4a67896b2..ee050c0ea 100644 --- a/docs/tutorials/wiki2/src/views/development.ini +++ b/docs/tutorials/wiki2/src/views/development.ini @@ -26,6 +26,12 @@ retry.attempts = 3 # 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 + [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 a28e47a83..91d0f5ddb 100644 --- a/docs/tutorials/wiki2/src/views/production.ini +++ b/docs/tutorials/wiki2/src/views/production.ini @@ -20,6 +20,12 @@ retry.attempts = 3 # 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 + [server:main] use = egg:waitress#main listen = *:6543 diff --git a/docs/tutorials/wiki2/src/views/pytest.ini b/docs/tutorials/wiki2/src/views/pytest.ini index 8b76bc410..a3489cdf8 100644 --- a/docs/tutorials/wiki2/src/views/pytest.ini +++ b/docs/tutorials/wiki2/src/views/pytest.ini @@ -1,3 +1,3 @@ [pytest] testpaths = tutorial -python_files = *.py +python_files = test*.py diff --git a/docs/tutorials/wiki2/src/views/setup.py b/docs/tutorials/wiki2/src/views/setup.py index abc24876d..28b766cbe 100644 --- a/docs/tutorials/wiki2/src/views/setup.py +++ b/docs/tutorials/wiki2/src/views/setup.py @@ -9,10 +9,11 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: CHANGES = f.read() requires = [ + 'alembic', 'bcrypt', 'docutils', 'plaster_pastedeploy', - 'pyramid >= 1.9a', + 'pyramid >= 1.9', 'pyramid_debugtoolbar', 'pyramid_jinja2', 'pyramid_retry', @@ -56,7 +57,7 @@ setup( 'main = tutorial:main', ], 'console_scripts': [ - 'initialize_tutorial_db = tutorial.scripts.initializedb:main', + 'initialize_tutorial_db = tutorial.scripts.initialize_db:main', ], }, ) diff --git a/docs/tutorials/wiki2/src/views/tutorial/alembic/env.py b/docs/tutorials/wiki2/src/views/tutorial/alembic/env.py new file mode 100644 index 000000000..ba116d0f3 --- /dev/null +++ b/docs/tutorials/wiki2/src/views/tutorial/alembic/env.py @@ -0,0 +1,58 @@ +"""Pyramid bootstrap environment. """ +from alembic import context +from pyramid.paster import get_appsettings, setup_logging +from sqlalchemy import engine_from_config + +from tutorial.models.meta import Base + +config = context.config + +setup_logging(config.config_file_name) + +settings = get_appsettings(config.config_file_name) +target_metadata = Base.metadata + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + context.configure(url=settings['sqlalchemy.url']) + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + engine = engine_from_config(settings, prefix='sqlalchemy.') + + connection = engine.connect() + context.configure( + connection=connection, + target_metadata=target_metadata + ) + + try: + with context.begin_transaction(): + context.run_migrations() + finally: + connection.close() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/docs/tutorials/wiki2/src/views/tutorial/alembic/script.py.mako b/docs/tutorials/wiki2/src/views/tutorial/alembic/script.py.mako new file mode 100644 index 000000000..2c0156303 --- /dev/null +++ b/docs/tutorials/wiki2/src/views/tutorial/alembic/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/docs/tutorials/wiki2/src/views/tutorial/alembic/versions/README.txt b/docs/tutorials/wiki2/src/views/tutorial/alembic/versions/README.txt new file mode 100644 index 000000000..09ed32c8d --- /dev/null +++ b/docs/tutorials/wiki2/src/views/tutorial/alembic/versions/README.txt @@ -0,0 +1 @@ +Placeholder for alembic versions
\ No newline at end of file diff --git a/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py index 3c9ba8e54..a4209a6e9 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py @@ -5,8 +5,8 @@ import zope.sqlalchemy # import or define all models here to ensure they are attached to the # Base.metadata prior to any initialization routines -from .page import Page # noqa -from .user import User # noqa +from .page import Page # flake8: noqa +from .user import User # flake8: noqa # run configure_mappers after defining all of the models to ensure # all relationships can be setup diff --git a/docs/tutorials/wiki2/src/views/tutorial/scripts/initialize_db.py b/docs/tutorials/wiki2/src/views/tutorial/scripts/initialize_db.py new file mode 100644 index 000000000..231d5d44b --- /dev/null +++ b/docs/tutorials/wiki2/src/views/tutorial/scripts/initialize_db.py @@ -0,0 +1,56 @@ +import os +import sys + +from pyramid.paster import bootstrap, setup_logging +from sqlalchemy.exc import OperationalError + +from .. import models + + +def setup_models(dbsession): + editor = models.User(name='editor', role='editor') + editor.set_password('editor') + dbsession.add(editor) + + basic = models.User(name='basic', role='basic') + basic.set_password('basic') + dbsession.add(basic) + + page = models.Page( + name='FrontPage', + creator=editor, + data='This is the front page', + ) + dbsession.add(page) + + +def usage(argv): + cmd = os.path.basename(argv[0]) + print('usage: %s <config_uri>\n' + '(example: "%s development.ini")' % (cmd, cmd)) + sys.exit(1) + + +def main(argv=sys.argv): + if len(argv) != 2: + usage(argv) + config_uri = argv[1] + setup_logging(config_uri) + env = bootstrap(config_uri) + + try: + with env['request'].tm: + dbsession = env['request'].dbsession + setup_models(dbsession) + except OperationalError: + print(''' +Pyramid is having a problem using your SQL database. The problem +might be caused by one of the following things: + +1. You may need to initialize your database tables with `alembic`. + Check your README.txt for description and try to run it. + +2. Your database server may not be running. Check that the + database server referred to by the "sqlalchemy.url" setting in + your "development.ini" file is running. + ''') diff --git a/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py deleted file mode 100644 index f3c0a6fef..000000000 --- a/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py +++ /dev/null @@ -1,57 +0,0 @@ -import os -import sys -import transaction - -from pyramid.paster import ( - get_appsettings, - setup_logging, - ) - -from pyramid.scripts.common import parse_vars - -from ..models.meta import Base -from ..models import ( - get_engine, - get_session_factory, - get_tm_session, - ) -from ..models import Page, User - - -def usage(argv): - cmd = os.path.basename(argv[0]) - print('usage: %s <config_uri> [var=value]\n' - '(example: "%s development.ini")' % (cmd, cmd)) - sys.exit(1) - - -def main(argv=sys.argv): - if len(argv) < 2: - usage(argv) - config_uri = argv[1] - options = parse_vars(argv[2:]) - setup_logging(config_uri) - settings = get_appsettings(config_uri, options=options) - - engine = get_engine(settings) - Base.metadata.create_all(engine) - - session_factory = get_session_factory(engine) - - with transaction.manager: - dbsession = get_tm_session(session_factory, transaction.manager) - - editor = User(name='editor', role='editor') - editor.set_password('editor') - dbsession.add(editor) - - basic = User(name='basic', role='basic') - basic.set_password('basic') - dbsession.add(basic) - - page = Page( - name='FrontPage', - creator=editor, - data='This is the front page', - ) - dbsession.add(page) diff --git a/docs/tutorials/wiki2/src/views/tutorial/views/default.py b/docs/tutorials/wiki2/src/views/tutorial/views/default.py index 3b95e0f59..a866af1de 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/views/tutorial/views/default.py @@ -9,7 +9,7 @@ from pyramid.httpexceptions import ( from pyramid.view import view_config -from ..models import Page, User +from .. import models # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") @@ -22,13 +22,13 @@ def view_wiki(request): @view_config(route_name='view_page', renderer='../templates/view.jinja2') def view_page(request): pagename = request.matchdict['pagename'] - page = request.dbsession.query(Page).filter_by(name=pagename).first() + page = request.dbsession.query(models.Page).filter_by(name=pagename).first() if page is None: raise HTTPNotFound('No such page') def add_link(match): word = match.group(1) - exists = request.dbsession.query(Page).filter_by(name=word).all() + exists = request.dbsession.query(models.Page).filter_by(name=word).all() if exists: view_url = request.route_url('view_page', pagename=word) return '<a href="%s">%s</a>' % (view_url, escape(word)) @@ -44,7 +44,7 @@ def view_page(request): @view_config(route_name='edit_page', renderer='../templates/edit.jinja2') def edit_page(request): pagename = request.matchdict['pagename'] - page = request.dbsession.query(Page).filter_by(name=pagename).one() + page = request.dbsession.query(models.Page).filter_by(name=pagename).one() if 'form.submitted' in request.params: page.data = request.params['body'] next_url = request.route_url('view_page', pagename=page.name) @@ -58,14 +58,14 @@ def edit_page(request): @view_config(route_name='add_page', renderer='../templates/edit.jinja2') def add_page(request): pagename = request.matchdict['pagename'] - if request.dbsession.query(Page).filter_by(name=pagename).count() > 0: + if request.dbsession.query(models.Page).filter_by(name=pagename).count() > 0: next_url = request.route_url('edit_page', pagename=pagename) return HTTPFound(location=next_url) if 'form.submitted' in request.params: body = request.params['body'] - page = Page(name=pagename, data=body) + page = models.Page(name=pagename, data=body) page.creator = ( - request.dbsession.query(User).filter_by(name='editor').one()) + request.dbsession.query(models.User).filter_by(name='editor').one()) request.dbsession.add(page) next_url = request.route_url('view_page', pagename=pagename) return HTTPFound(location=next_url) |
