From 406024de0d1792cbca5056ceaa94ff19654f730e Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Wed, 11 Nov 2015 21:28:28 -0800 Subject: basiclayout/tutorial/setup.py - update template binding from chameleon to jinja2 --- docs/tutorials/wiki2/src/basiclayout/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/basiclayout/setup.py b/docs/tutorials/wiki2/src/basiclayout/setup.py index 15e7e5923..eb771010f 100644 --- a/docs/tutorials/wiki2/src/basiclayout/setup.py +++ b/docs/tutorials/wiki2/src/basiclayout/setup.py @@ -10,7 +10,7 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: requires = [ 'pyramid', - 'pyramid_chameleon', + 'pyramid_jinja2', 'pyramid_debugtoolbar', 'pyramid_tm', 'SQLAlchemy', -- cgit v1.2.3 From 392cc17f5091d51a8e1b8908f8600c0b821ebd7f Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Wed, 11 Nov 2015 21:58:03 -0800 Subject: basiclayout/tutorial - views - use package instead of single file - add tests.py from scaffold - update basiclayout.rst with views section --- .../wiki2/src/basiclayout/tutorial/tests.py | 68 ++++++++++++++++------ .../wiki2/src/basiclayout/tutorial/views.py | 35 ----------- .../src/basiclayout/tutorial/views/__init__.py | 0 .../src/basiclayout/tutorial/views/default.py | 33 +++++++++++ 4 files changed, 83 insertions(+), 53 deletions(-) delete mode 100644 docs/tutorials/wiki2/src/basiclayout/tutorial/views.py create mode 100644 docs/tutorials/wiki2/src/basiclayout/tutorial/views/__init__.py create mode 100644 docs/tutorials/wiki2/src/basiclayout/tutorial/views/default.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py index 57a775e0a..b947e3bb1 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py @@ -3,31 +3,63 @@ import transaction from pyramid import testing -from .models import DBSession +def dummy_request(dbsession): + return testing.DummyRequest(dbsession=dbsession) -class TestMyView(unittest.TestCase): + +class BaseTest(unittest.TestCase): def setUp(self): - self.config = testing.setUp() - from sqlalchemy import create_engine - engine = create_engine('sqlite://') - from .models import ( - Base, - MyModel, + self.config = testing.setUp(settings={ + 'sqlalchemy.url': 'sqlite:///:memory:' + }) + self.config.include('.models.meta') + settings = self.config.get_settings() + + from .models.meta import ( + get_session, + get_engine, + get_dbmaker, ) - DBSession.configure(bind=engine) - Base.metadata.create_all(engine) - with transaction.manager: - model = MyModel(name='one', value=55) - DBSession.add(model) + + self.engine = get_engine(settings) + dbmaker = get_dbmaker(self.engine) + + self.session = get_session(transaction.manager, dbmaker) + + def init_database(self): + from .models.meta import Base + Base.metadata.create_all(self.engine) def tearDown(self): - DBSession.remove() + from .models.meta import Base + testing.tearDown() + transaction.abort() + Base.metadata.create_all(self.engine) + + +class TestMyViewSuccessCondition(BaseTest): - def test_it(self): - from .views import my_view - request = testing.DummyRequest() - info = my_view(request) + def setUp(self): + super(TestMyViewSuccessCondition, self).setUp() + self.init_database() + + from .models.mymodel import MyModel + + model = MyModel(name='one', value=55) + self.session.add(model) + + def test_passing_view(self): + from .views.default import my_view + info = my_view(dummy_request(self.session)) self.assertEqual(info['one'].name, 'one') self.assertEqual(info['project'], 'tutorial') + + +class TestMyViewFailureCondition(BaseTest): + + def test_failing_view(self): + from .views.default import my_view + info = my_view(dummy_request(self.session)) + self.assertEqual(info.status_int, 500) diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/views.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/views.py deleted file mode 100644 index 4cfcae4af..000000000 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/views.py +++ /dev/null @@ -1,35 +0,0 @@ -from pyramid.response import Response -from pyramid.view import view_config - -from sqlalchemy.exc import DBAPIError - -from .models import ( - DBSession, - MyModel, - ) - - -@view_config(route_name='home', renderer='templates/mytemplate.pt') -def my_view(request): - try: - one = DBSession.query(MyModel).filter(MyModel.name == 'one').first() - except DBAPIError: - return Response(conn_err_msg, content_type='text/plain', status_int=500) - return {'one': one, 'project': 'tutorial'} - -conn_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. - -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. - -After you fix the problem, please restart the Pyramid application to -try it again. -""" - diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/views/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/views/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/views/default.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/views/default.py new file mode 100644 index 000000000..13ad8793c --- /dev/null +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/views/default.py @@ -0,0 +1,33 @@ +from pyramid.response import Response +from pyramid.view import view_config + +from sqlalchemy.exc import DBAPIError + +from ..models.mymodel import MyModel + + +@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() + except DBAPIError: + return Response(db_err_msg, content_type='text/plain', status_int=500) + return {'one': one, 'project': 'tutorial'} + + +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. + +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. + +After you fix the problem, please restart the Pyramid application to +try it again. +""" -- cgit v1.2.3 From 0409cf178d4c0fa9a0d3b0858ab2294935b23609 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Thu, 12 Nov 2015 00:40:02 -0800 Subject: basiclayout/tutorial - models, scripts, static, templates - use package instead of single file - add tests.py from scaffold - update basiclayout.rst with models section --- .../wiki2/src/basiclayout/tutorial/models.py | 27 --------- .../src/basiclayout/tutorial/models/__init__.py | 7 +++ .../wiki2/src/basiclayout/tutorial/models/meta.py | 46 +++++++++++++++ .../src/basiclayout/tutorial/models/mymodel.py | 17 ++++++ .../basiclayout/tutorial/scripts/initializedb.py | 30 ++++++---- .../src/basiclayout/tutorial/static/theme.min.css | 2 +- .../basiclayout/tutorial/templates/layout.jinja2 | 66 ++++++++++++++++++++++ .../tutorial/templates/mytemplate.jinja2 | 8 +++ .../basiclayout/tutorial/templates/mytemplate.pt | 66 ---------------------- 9 files changed, 164 insertions(+), 105 deletions(-) delete mode 100644 docs/tutorials/wiki2/src/basiclayout/tutorial/models.py create mode 100644 docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py create mode 100644 docs/tutorials/wiki2/src/basiclayout/tutorial/models/meta.py create mode 100644 docs/tutorials/wiki2/src/basiclayout/tutorial/models/mymodel.py create mode 100644 docs/tutorials/wiki2/src/basiclayout/tutorial/templates/layout.jinja2 create mode 100644 docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.jinja2 delete mode 100644 docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py deleted file mode 100644 index 11ddccadb..000000000 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py +++ /dev/null @@ -1,27 +0,0 @@ -from sqlalchemy import ( - Column, - Integer, - Text, - Index, - ) - -from sqlalchemy.ext.declarative import declarative_base - -from sqlalchemy.orm import ( - scoped_session, - sessionmaker, - ) - -from zope.sqlalchemy import ZopeTransactionExtension - -DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) -Base = declarative_base() - - -class MyModel(Base): - __tablename__ = 'models' - id = Column(Integer, primary_key=True) - name = Column(Text, unique=True) - value = Column(Integer) - -Index('my_index', MyModel.name, unique=True, mysql_length=255) diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py new file mode 100644 index 000000000..6ffc10a78 --- /dev/null +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py @@ -0,0 +1,7 @@ +from sqlalchemy.orm import configure_mappers +# import all models classes here for sqlalchemy mappers +# to pick up +from .mymodel import MyModel # flake8: noqa + +# run configure mappers to ensure we avoid any race conditions +configure_mappers() diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/meta.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/meta.py new file mode 100644 index 000000000..b72b45f9f --- /dev/null +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/meta.py @@ -0,0 +1,46 @@ +from sqlalchemy import engine_from_config +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from sqlalchemy.schema import MetaData +import zope.sqlalchemy + +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) + + +def includeme(config): + settings = config.get_settings() + dbmaker = get_dbmaker(get_engine(settings)) + + config.add_request_method( + lambda r: get_session(r.tm, dbmaker), + 'dbsession', + reify=True + ) + + config.include('pyramid_tm') + + +def get_session(transaction_manager, dbmaker): + dbsession = dbmaker() + zope.sqlalchemy.register(dbsession, + transaction_manager=transaction_manager) + return dbsession + + +def get_engine(settings, prefix='sqlalchemy.'): + return engine_from_config(settings, prefix) + + +def get_dbmaker(engine): + dbmaker = sessionmaker() + dbmaker.configure(bind=engine) + return dbmaker diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/mymodel.py new file mode 100644 index 000000000..5a2b5890c --- /dev/null +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/mymodel.py @@ -0,0 +1,17 @@ +from .meta import Base +from sqlalchemy import ( + Column, + Index, + Integer, + Text, +) + + +class MyModel(Base): + __tablename__ = 'models' + id = Column(Integer, primary_key=True) + name = Column(Text) + value = Column(Integer) + + +Index('my_index', MyModel.name, unique=True, mysql_length=255) diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initializedb.py index 66feb3008..f0d09729e 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initializedb.py @@ -2,36 +2,44 @@ import os import sys import transaction -from sqlalchemy import engine_from_config - from pyramid.paster import ( get_appsettings, setup_logging, ) -from ..models import ( - DBSession, - MyModel, +from pyramid.scripts.common import parse_vars + +from ..models.meta import ( Base, + get_session, + get_engine, + get_dbmaker, ) +from ..models.mymodel import MyModel def usage(argv): cmd = os.path.basename(argv[0]) - print('usage: %s \n' + print('usage: %s [var=value]\n' '(example: "%s development.ini")' % (cmd, cmd)) sys.exit(1) def main(argv=sys.argv): - if len(argv) != 2: + if len(argv) < 2: usage(argv) config_uri = argv[1] + options = parse_vars(argv[2:]) setup_logging(config_uri) - settings = get_appsettings(config_uri) - engine = engine_from_config(settings, 'sqlalchemy.') - DBSession.configure(bind=engine) + settings = get_appsettings(config_uri, options=options) + + engine = get_engine(settings) + dbmaker = get_dbmaker(engine) + + dbsession = get_session(transaction.manager, dbmaker) + Base.metadata.create_all(engine) + with transaction.manager: model = MyModel(name='one', value=1) - DBSession.add(model) + dbsession.add(model) diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/theme.min.css b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/theme.min.css index 2f924bcc5..0d25de5b6 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/static/theme.min.css +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/static/theme.min.css @@ -1 +1 @@ -@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a{color:#fff}.starter-template .links ul li a:hover{text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} \ No newline at end of file +@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a,a{color:#f2b7bd;text-decoration:underline}.starter-template .links ul li a:hover,a:hover{color:#fff;text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/layout.jinja2 new file mode 100644 index 000000000..ff624c65b --- /dev/null +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/layout.jinja2 @@ -0,0 +1,66 @@ + + + + + + + + + + + Alchemy Scaffold for The Pyramid Web Framework + + + + + + + + + + + + + +
+
+
+
+ +
+
+ {% block content %} +

No content

+ {% endblock content %} +
+
+
+ +
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.jinja2 b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.jinja2 new file mode 100644 index 000000000..bb622bf5a --- /dev/null +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+

Pyramid Alchemy scaffold

+

Welcome to {{project}}, an application generated by
the Pyramid Web Framework 1.7.dev0.

+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt deleted file mode 100644 index c9b0cec21..000000000 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - Alchemy Scaffold for The Pyramid Web Framework - - - - - - - - - - - - - -
-
-
-
- -
-
-
-

Pyramid Alchemy scaffold

-

Welcome to ${project}, an application generated by
the Pyramid Web Framework.

-
-
-
- -
- -
-
-
- - - - - - - - -- cgit v1.2.3 From bfa4994ab0d1327c451509f466ccebbad8fa2320 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Thu, 12 Nov 2015 03:02:11 -0800 Subject: update wiki2/src/models and wiki2/definingmodels.rst --- docs/tutorials/wiki2/src/models/development.ini | 4 +- docs/tutorials/wiki2/src/models/production.ini | 14 +++-- docs/tutorials/wiki2/src/models/setup.py | 2 +- .../wiki2/src/models/tutorial/__init__.py | 12 +--- docs/tutorials/wiki2/src/models/tutorial/models.py | 25 -------- .../wiki2/src/models/tutorial/models/__init__.py | 7 +++ .../wiki2/src/models/tutorial/models/meta.py | 46 +++++++++++++++ .../wiki2/src/models/tutorial/models/mymodel.py | 14 +++++ .../src/models/tutorial/scripts/initializedb.py | 25 ++++---- .../wiki2/src/models/tutorial/static/theme.min.css | 2 +- .../src/models/tutorial/templates/layout.jinja2 | 66 +++++++++++++++++++++ .../models/tutorial/templates/mytemplate.jinja2 | 8 +++ .../src/models/tutorial/templates/mytemplate.pt | 66 --------------------- docs/tutorials/wiki2/src/models/tutorial/tests.py | 68 ++++++++++++++++------ docs/tutorials/wiki2/src/models/tutorial/views.py | 35 ----------- .../wiki2/src/models/tutorial/views/__init__.py | 0 .../wiki2/src/models/tutorial/views/default.py | 33 +++++++++++ 17 files changed, 255 insertions(+), 172 deletions(-) delete mode 100644 docs/tutorials/wiki2/src/models/tutorial/models.py create mode 100644 docs/tutorials/wiki2/src/models/tutorial/models/__init__.py create mode 100644 docs/tutorials/wiki2/src/models/tutorial/models/meta.py create mode 100644 docs/tutorials/wiki2/src/models/tutorial/models/mymodel.py create mode 100644 docs/tutorials/wiki2/src/models/tutorial/templates/layout.jinja2 create mode 100644 docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.jinja2 delete mode 100644 docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt delete mode 100644 docs/tutorials/wiki2/src/models/tutorial/views.py create mode 100644 docs/tutorials/wiki2/src/models/tutorial/views/__init__.py create mode 100644 docs/tutorials/wiki2/src/models/tutorial/views/default.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/models/development.ini b/docs/tutorials/wiki2/src/models/development.ini index a9d53b296..99c4ff0fe 100644 --- a/docs/tutorials/wiki2/src/models/development.ini +++ b/docs/tutorials/wiki2/src/models/development.ini @@ -27,7 +27,7 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -host = 0.0.0.0 +host = 127.0.0.1 port = 6543 ### @@ -68,4 +68,4 @@ level = NOTSET formatter = generic [formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/tutorials/wiki2/src/models/production.ini b/docs/tutorials/wiki2/src/models/production.ini index 4684d2f7a..97acfbd7d 100644 --- a/docs/tutorials/wiki2/src/models/production.ini +++ b/docs/tutorials/wiki2/src/models/production.ini @@ -1,3 +1,8 @@ +### +# app configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +### + [app:main] use = egg:tutorial @@ -16,7 +21,10 @@ use = egg:waitress#main host = 0.0.0.0 port = 6543 -# Begin logging configuration +### +# logging configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html +### [loggers] keys = root, tutorial, sqlalchemy @@ -51,6 +59,4 @@ level = NOTSET formatter = generic [formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s - -# End logging configuration +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/tutorials/wiki2/src/models/setup.py b/docs/tutorials/wiki2/src/models/setup.py index 15e7e5923..eb771010f 100644 --- a/docs/tutorials/wiki2/src/models/setup.py +++ b/docs/tutorials/wiki2/src/models/setup.py @@ -10,7 +10,7 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: requires = [ 'pyramid', - 'pyramid_chameleon', + 'pyramid_jinja2', 'pyramid_debugtoolbar', 'pyramid_tm', 'SQLAlchemy', diff --git a/docs/tutorials/wiki2/src/models/tutorial/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/__init__.py index 867049e4f..7994bbfa8 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/models/tutorial/__init__.py @@ -1,20 +1,12 @@ from pyramid.config import Configurator -from sqlalchemy import engine_from_config - -from .models import ( - DBSession, - Base, - ) def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ - engine = engine_from_config(settings, 'sqlalchemy.') - DBSession.configure(bind=engine) - Base.metadata.bind = engine config = Configurator(settings=settings) - config.include('pyramid_chameleon') + config.include('pyramid_jinja2') + config.include('.models.meta') config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('home', '/') config.scan() diff --git a/docs/tutorials/wiki2/src/models/tutorial/models.py b/docs/tutorials/wiki2/src/models/tutorial/models.py deleted file mode 100644 index f028c917a..000000000 --- a/docs/tutorials/wiki2/src/models/tutorial/models.py +++ /dev/null @@ -1,25 +0,0 @@ -from sqlalchemy import ( - Column, - Integer, - Text, - ) - -from sqlalchemy.ext.declarative import declarative_base - -from sqlalchemy.orm import ( - scoped_session, - sessionmaker, - ) - -from zope.sqlalchemy import ZopeTransactionExtension - -DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) -Base = declarative_base() - - -class Page(Base): - """ The SQLAlchemy declarative model class for a Page object. """ - __tablename__ = 'pages' - id = Column(Integer, primary_key=True) - name = Column(Text, unique=True) - data = Column(Text) diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py new file mode 100644 index 000000000..7b1c62867 --- /dev/null +++ b/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py @@ -0,0 +1,7 @@ +from sqlalchemy.orm import configure_mappers +# import all models classes here for sqlalchemy mappers +# to pick up +from .mymodel import Page # flake8: noqa + +# run configure mappers to ensure we avoid any race conditions +configure_mappers() diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/meta.py b/docs/tutorials/wiki2/src/models/tutorial/models/meta.py new file mode 100644 index 000000000..b72b45f9f --- /dev/null +++ b/docs/tutorials/wiki2/src/models/tutorial/models/meta.py @@ -0,0 +1,46 @@ +from sqlalchemy import engine_from_config +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from sqlalchemy.schema import MetaData +import zope.sqlalchemy + +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) + + +def includeme(config): + settings = config.get_settings() + dbmaker = get_dbmaker(get_engine(settings)) + + config.add_request_method( + lambda r: get_session(r.tm, dbmaker), + 'dbsession', + reify=True + ) + + config.include('pyramid_tm') + + +def get_session(transaction_manager, dbmaker): + dbsession = dbmaker() + zope.sqlalchemy.register(dbsession, + transaction_manager=transaction_manager) + return dbsession + + +def get_engine(settings, prefix='sqlalchemy.'): + return engine_from_config(settings, prefix) + + +def get_dbmaker(engine): + dbmaker = sessionmaker() + dbmaker.configure(bind=engine) + return dbmaker diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/models/tutorial/models/mymodel.py new file mode 100644 index 000000000..45571d78e --- /dev/null +++ b/docs/tutorials/wiki2/src/models/tutorial/models/mymodel.py @@ -0,0 +1,14 @@ +from .meta import Base +from sqlalchemy import ( + Column, + Integer, + Text, +) + + +class Page(Base): + """ The SQLAlchemy declarative model class for a Page object. """ + __tablename__ = 'pages' + id = Column(Integer, primary_key=True) + name = Column(Text, unique=True) + data = Column(Integer) diff --git a/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py index 23a5f13f4..4aac4a848 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py @@ -2,36 +2,41 @@ import os import sys import transaction -from sqlalchemy import engine_from_config - from pyramid.paster import ( get_appsettings, setup_logging, ) -from ..models import ( - DBSession, - Page, +from ..models.meta import ( Base, + get_session, + get_engine, + get_dbmaker, ) +from ..models.mymodel import Page def usage(argv): cmd = os.path.basename(argv[0]) - print('usage: %s \n' + print('usage: %s [var=value]\n' '(example: "%s development.ini")' % (cmd, cmd)) sys.exit(1) def main(argv=sys.argv): - if len(argv) != 2: + if len(argv) < 2: usage(argv) config_uri = argv[1] setup_logging(config_uri) settings = get_appsettings(config_uri) - engine = engine_from_config(settings, 'sqlalchemy.') - DBSession.configure(bind=engine) + + engine = get_engine(settings) + dbmaker = get_dbmaker(engine) + + dbsession = get_session(transaction.manager, dbmaker) + Base.metadata.create_all(engine) + with transaction.manager: model = Page(name='FrontPage', data='This is the front page') - DBSession.add(model) + dbsession.add(model) diff --git a/docs/tutorials/wiki2/src/models/tutorial/static/theme.min.css b/docs/tutorials/wiki2/src/models/tutorial/static/theme.min.css index 2f924bcc5..0d25de5b6 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/static/theme.min.css +++ b/docs/tutorials/wiki2/src/models/tutorial/static/theme.min.css @@ -1 +1 @@ -@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a{color:#fff}.starter-template .links ul li a:hover{text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} \ No newline at end of file +@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a,a{color:#f2b7bd;text-decoration:underline}.starter-template .links ul li a:hover,a:hover{color:#fff;text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/models/tutorial/templates/layout.jinja2 new file mode 100644 index 000000000..ff624c65b --- /dev/null +++ b/docs/tutorials/wiki2/src/models/tutorial/templates/layout.jinja2 @@ -0,0 +1,66 @@ + + + + + + + + + + + Alchemy Scaffold for The Pyramid Web Framework + + + + + + + + + + + + + +
+
+
+
+ +
+
+ {% block content %} +

No content

+ {% endblock content %} +
+
+
+ +
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.jinja2 b/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.jinja2 new file mode 100644 index 000000000..bb622bf5a --- /dev/null +++ b/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+

Pyramid Alchemy scaffold

+

Welcome to {{project}}, an application generated by
the Pyramid Web Framework 1.7.dev0.

+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt deleted file mode 100644 index c9b0cec21..000000000 --- a/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - Alchemy Scaffold for The Pyramid Web Framework - - - - - - - - - - - - - -
-
-
-
- -
-
-
-

Pyramid Alchemy scaffold

-

Welcome to ${project}, an application generated by
the Pyramid Web Framework.

-
-
-
- -
- -
-
-
- - - - - - - - diff --git a/docs/tutorials/wiki2/src/models/tutorial/tests.py b/docs/tutorials/wiki2/src/models/tutorial/tests.py index 57a775e0a..b947e3bb1 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/models/tutorial/tests.py @@ -3,31 +3,63 @@ import transaction from pyramid import testing -from .models import DBSession +def dummy_request(dbsession): + return testing.DummyRequest(dbsession=dbsession) -class TestMyView(unittest.TestCase): + +class BaseTest(unittest.TestCase): def setUp(self): - self.config = testing.setUp() - from sqlalchemy import create_engine - engine = create_engine('sqlite://') - from .models import ( - Base, - MyModel, + self.config = testing.setUp(settings={ + 'sqlalchemy.url': 'sqlite:///:memory:' + }) + self.config.include('.models.meta') + settings = self.config.get_settings() + + from .models.meta import ( + get_session, + get_engine, + get_dbmaker, ) - DBSession.configure(bind=engine) - Base.metadata.create_all(engine) - with transaction.manager: - model = MyModel(name='one', value=55) - DBSession.add(model) + + self.engine = get_engine(settings) + dbmaker = get_dbmaker(self.engine) + + self.session = get_session(transaction.manager, dbmaker) + + def init_database(self): + from .models.meta import Base + Base.metadata.create_all(self.engine) def tearDown(self): - DBSession.remove() + from .models.meta import Base + testing.tearDown() + transaction.abort() + Base.metadata.create_all(self.engine) + + +class TestMyViewSuccessCondition(BaseTest): - def test_it(self): - from .views import my_view - request = testing.DummyRequest() - info = my_view(request) + def setUp(self): + super(TestMyViewSuccessCondition, self).setUp() + self.init_database() + + from .models.mymodel import MyModel + + model = MyModel(name='one', value=55) + self.session.add(model) + + def test_passing_view(self): + from .views.default import my_view + info = my_view(dummy_request(self.session)) self.assertEqual(info['one'].name, 'one') self.assertEqual(info['project'], 'tutorial') + + +class TestMyViewFailureCondition(BaseTest): + + def test_failing_view(self): + from .views.default import my_view + info = my_view(dummy_request(self.session)) + self.assertEqual(info.status_int, 500) diff --git a/docs/tutorials/wiki2/src/models/tutorial/views.py b/docs/tutorials/wiki2/src/models/tutorial/views.py deleted file mode 100644 index 4cfcae4af..000000000 --- a/docs/tutorials/wiki2/src/models/tutorial/views.py +++ /dev/null @@ -1,35 +0,0 @@ -from pyramid.response import Response -from pyramid.view import view_config - -from sqlalchemy.exc import DBAPIError - -from .models import ( - DBSession, - MyModel, - ) - - -@view_config(route_name='home', renderer='templates/mytemplate.pt') -def my_view(request): - try: - one = DBSession.query(MyModel).filter(MyModel.name == 'one').first() - except DBAPIError: - return Response(conn_err_msg, content_type='text/plain', status_int=500) - return {'one': one, 'project': 'tutorial'} - -conn_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. - -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. - -After you fix the problem, please restart the Pyramid application to -try it again. -""" - diff --git a/docs/tutorials/wiki2/src/models/tutorial/views/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/views/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/docs/tutorials/wiki2/src/models/tutorial/views/default.py b/docs/tutorials/wiki2/src/models/tutorial/views/default.py new file mode 100644 index 000000000..13ad8793c --- /dev/null +++ b/docs/tutorials/wiki2/src/models/tutorial/views/default.py @@ -0,0 +1,33 @@ +from pyramid.response import Response +from pyramid.view import view_config + +from sqlalchemy.exc import DBAPIError + +from ..models.mymodel import MyModel + + +@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() + except DBAPIError: + return Response(db_err_msg, content_type='text/plain', status_int=500) + return {'one': one, 'project': 'tutorial'} + + +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. + +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. + +After you fix the problem, please restart the Pyramid application to +try it again. +""" -- cgit v1.2.3 From 414b67b45bab156b9738105e180908c4eee8600a Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 12 Nov 2015 12:56:27 -0600 Subject: Restore progress after backing changes out of master. This reverts commit 049e670aef9ea5611561546fd5c0e2dd6152b9b7. --- docs/tutorials/wiki2/src/basiclayout/development.ini | 4 ++-- docs/tutorials/wiki2/src/basiclayout/production.ini | 2 +- docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py | 12 ++---------- 3 files changed, 5 insertions(+), 13 deletions(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/basiclayout/development.ini b/docs/tutorials/wiki2/src/basiclayout/development.ini index a9d53b296..99c4ff0fe 100644 --- a/docs/tutorials/wiki2/src/basiclayout/development.ini +++ b/docs/tutorials/wiki2/src/basiclayout/development.ini @@ -27,7 +27,7 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -host = 0.0.0.0 +host = 127.0.0.1 port = 6543 ### @@ -68,4 +68,4 @@ level = NOTSET formatter = generic [formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/tutorials/wiki2/src/basiclayout/production.ini b/docs/tutorials/wiki2/src/basiclayout/production.ini index fa94c1b3e..97acfbd7d 100644 --- a/docs/tutorials/wiki2/src/basiclayout/production.ini +++ b/docs/tutorials/wiki2/src/basiclayout/production.ini @@ -59,4 +59,4 @@ level = NOTSET formatter = generic [formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py index 867049e4f..7994bbfa8 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py @@ -1,20 +1,12 @@ from pyramid.config import Configurator -from sqlalchemy import engine_from_config - -from .models import ( - DBSession, - Base, - ) def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ - engine = engine_from_config(settings, 'sqlalchemy.') - DBSession.configure(bind=engine) - Base.metadata.bind = engine config = Configurator(settings=settings) - config.include('pyramid_chameleon') + config.include('pyramid_jinja2') + config.include('.models.meta') config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('home', '/') config.scan() -- cgit v1.2.3 From 3214690b08e3c495ab64170e1430ece240853110 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Fri, 13 Nov 2015 02:30:32 -0800 Subject: update wiki2/src/views and wiki2/definingviews.rst --- docs/tutorials/wiki2/src/views/development.ini | 4 +- docs/tutorials/wiki2/src/views/production.ini | 14 +- docs/tutorials/wiki2/src/views/setup.py | 2 +- .../tutorials/wiki2/src/views/tutorial/__init__.py | 12 +- docs/tutorials/wiki2/src/views/tutorial/models.py | 25 --- .../src/views/tutorial/scripts/initializedb.py | 25 +-- .../wiki2/src/views/tutorial/templates/edit.pt | 69 -------- .../src/views/tutorial/templates/mytemplate.pt | 66 -------- .../wiki2/src/views/tutorial/templates/view.pt | 69 -------- docs/tutorials/wiki2/src/views/tutorial/tests.py | 175 ++++++--------------- docs/tutorials/wiki2/src/views/tutorial/views.py | 72 --------- 11 files changed, 77 insertions(+), 456 deletions(-) delete mode 100644 docs/tutorials/wiki2/src/views/tutorial/models.py delete mode 100644 docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt delete mode 100644 docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt delete mode 100644 docs/tutorials/wiki2/src/views/tutorial/templates/view.pt delete mode 100644 docs/tutorials/wiki2/src/views/tutorial/views.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/views/development.ini b/docs/tutorials/wiki2/src/views/development.ini index a9d53b296..99c4ff0fe 100644 --- a/docs/tutorials/wiki2/src/views/development.ini +++ b/docs/tutorials/wiki2/src/views/development.ini @@ -27,7 +27,7 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -host = 0.0.0.0 +host = 127.0.0.1 port = 6543 ### @@ -68,4 +68,4 @@ level = NOTSET formatter = generic [formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/tutorials/wiki2/src/views/production.ini b/docs/tutorials/wiki2/src/views/production.ini index 4684d2f7a..97acfbd7d 100644 --- a/docs/tutorials/wiki2/src/views/production.ini +++ b/docs/tutorials/wiki2/src/views/production.ini @@ -1,3 +1,8 @@ +### +# app configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +### + [app:main] use = egg:tutorial @@ -16,7 +21,10 @@ use = egg:waitress#main host = 0.0.0.0 port = 6543 -# Begin logging configuration +### +# logging configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html +### [loggers] keys = root, tutorial, sqlalchemy @@ -51,6 +59,4 @@ level = NOTSET formatter = generic [formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s - -# End logging configuration +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/tutorials/wiki2/src/views/setup.py b/docs/tutorials/wiki2/src/views/setup.py index 09bd63d33..d4e5a4072 100644 --- a/docs/tutorials/wiki2/src/views/setup.py +++ b/docs/tutorials/wiki2/src/views/setup.py @@ -10,7 +10,7 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: requires = [ 'pyramid', - 'pyramid_chameleon', + 'pyramid_jinja2', 'pyramid_debugtoolbar', 'pyramid_tm', 'SQLAlchemy', diff --git a/docs/tutorials/wiki2/src/views/tutorial/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/__init__.py index 37cae1997..d28f09ca4 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/views/tutorial/__init__.py @@ -1,20 +1,12 @@ from pyramid.config import Configurator -from sqlalchemy import engine_from_config - -from .models import ( - DBSession, - Base, - ) def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ - engine = engine_from_config(settings, 'sqlalchemy.') - DBSession.configure(bind=engine) - Base.metadata.bind = engine config = Configurator(settings=settings) - config.include('pyramid_chameleon') + config.include('pyramid_jinja2') + config.include('.models.meta') config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('view_wiki', '/') config.add_route('view_page', '/{pagename}') diff --git a/docs/tutorials/wiki2/src/views/tutorial/models.py b/docs/tutorials/wiki2/src/views/tutorial/models.py deleted file mode 100644 index f028c917a..000000000 --- a/docs/tutorials/wiki2/src/views/tutorial/models.py +++ /dev/null @@ -1,25 +0,0 @@ -from sqlalchemy import ( - Column, - Integer, - Text, - ) - -from sqlalchemy.ext.declarative import declarative_base - -from sqlalchemy.orm import ( - scoped_session, - sessionmaker, - ) - -from zope.sqlalchemy import ZopeTransactionExtension - -DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) -Base = declarative_base() - - -class Page(Base): - """ The SQLAlchemy declarative model class for a Page object. """ - __tablename__ = 'pages' - id = Column(Integer, primary_key=True) - name = Column(Text, unique=True) - data = Column(Text) diff --git a/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py index 23a5f13f4..4aac4a848 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py @@ -2,36 +2,41 @@ import os import sys import transaction -from sqlalchemy import engine_from_config - from pyramid.paster import ( get_appsettings, setup_logging, ) -from ..models import ( - DBSession, - Page, +from ..models.meta import ( Base, + get_session, + get_engine, + get_dbmaker, ) +from ..models.mymodel import Page def usage(argv): cmd = os.path.basename(argv[0]) - print('usage: %s \n' + print('usage: %s [var=value]\n' '(example: "%s development.ini")' % (cmd, cmd)) sys.exit(1) def main(argv=sys.argv): - if len(argv) != 2: + if len(argv) < 2: usage(argv) config_uri = argv[1] setup_logging(config_uri) settings = get_appsettings(config_uri) - engine = engine_from_config(settings, 'sqlalchemy.') - DBSession.configure(bind=engine) + + engine = get_engine(settings) + dbmaker = get_dbmaker(engine) + + dbsession = get_session(transaction.manager, dbmaker) + Base.metadata.create_all(engine) + with transaction.manager: model = Page(name='FrontPage', data='This is the front page') - DBSession.add(model) + dbsession.add(model) diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt deleted file mode 100644 index c0c1b6c20..000000000 --- a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.pt +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - ${page.name} - Pyramid tutorial wiki (based on - TurboGears 20-Minute Wiki) - - - - - - - - - - - - -
-
-
-
- -
-
-
-

- Editing Page Name Goes - Here -

-

You can return to the - FrontPage. -

-
-
- -
-
- -
-
-
-
-
-
- -
-
-
- - - - - - - - diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt deleted file mode 100644 index c9b0cec21..000000000 --- a/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - Alchemy Scaffold for The Pyramid Web Framework - - - - - - - - - - - - - -
-
-
-
- -
-
-
-

Pyramid Alchemy scaffold

-

Welcome to ${project}, an application generated by
the Pyramid Web Framework.

-
-
-
- -
- -
-
-
- - - - - - - - diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/view.pt b/docs/tutorials/wiki2/src/views/tutorial/templates/view.pt deleted file mode 100644 index 0f564b16c..000000000 --- a/docs/tutorials/wiki2/src/views/tutorial/templates/view.pt +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - ${page.name} - Pyramid tutorial wiki (based on - TurboGears 20-Minute Wiki) - - - - - - - - - - - - -
-
-
-
- -
-
-
-
- Page text goes here. -
-

- - Edit this page - -

-

- Viewing - Page Name Goes Here -

-

You can return to the - FrontPage. -

-
-
-
-
- -
-
-
- - - - - - - - diff --git a/docs/tutorials/wiki2/src/views/tutorial/tests.py b/docs/tutorials/wiki2/src/views/tutorial/tests.py index 9f01d2da5..b947e3bb1 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/views/tutorial/tests.py @@ -3,144 +3,63 @@ import transaction from pyramid import testing -def _initTestingDB(): - from sqlalchemy import create_engine - from tutorial.models import ( - DBSession, - Page, - Base - ) - engine = create_engine('sqlite://') - Base.metadata.create_all(engine) - DBSession.configure(bind=engine) - with transaction.manager: - model = Page(name='FrontPage', data='This is the front page') - DBSession.add(model) - return DBSession - -def _registerRoutes(config): - config.add_route('view_page', '{pagename}') - config.add_route('edit_page', '{pagename}/edit_page') - config.add_route('add_page', 'add_page/{pagename}') - -class ViewWikiTests(unittest.TestCase): + +def dummy_request(dbsession): + return testing.DummyRequest(dbsession=dbsession) + + +class BaseTest(unittest.TestCase): def setUp(self): - self.config = testing.setUp() - self.session = _initTestingDB() + self.config = testing.setUp(settings={ + 'sqlalchemy.url': 'sqlite:///:memory:' + }) + self.config.include('.models.meta') + settings = self.config.get_settings() - def tearDown(self): - self.session.remove() - testing.tearDown() + from .models.meta import ( + get_session, + get_engine, + get_dbmaker, + ) - def _callFUT(self, request): - from tutorial.views import view_wiki - return view_wiki(request) + self.engine = get_engine(settings) + dbmaker = get_dbmaker(self.engine) - def test_it(self): - _registerRoutes(self.config) - request = testing.DummyRequest() - response = self._callFUT(request) - self.assertEqual(response.location, 'http://example.com/FrontPage') + self.session = get_session(transaction.manager, dbmaker) -class ViewPageTests(unittest.TestCase): - def setUp(self): - self.session = _initTestingDB() - self.config = testing.setUp() + def init_database(self): + from .models.meta import Base + Base.metadata.create_all(self.engine) def tearDown(self): - self.session.remove() - testing.tearDown() - - def _callFUT(self, request): - from tutorial.views import view_page - return view_page(request) - - def test_it(self): - from tutorial.models import Page - request = testing.DummyRequest() - request.matchdict['pagename'] = 'IDoExist' - page = Page(name='IDoExist', data='Hello CruelWorld IDoExist') - self.session.add(page) - _registerRoutes(self.config) - info = self._callFUT(request) - self.assertEqual(info['page'], page) - self.assertEqual( - info['content'], - '
\n' - '

Hello ' - 'CruelWorld ' - '' - 'IDoExist' - '

\n
\n') - self.assertEqual(info['edit_url'], - 'http://example.com/IDoExist/edit_page') - - -class AddPageTests(unittest.TestCase): - def setUp(self): - self.session = _initTestingDB() - self.config = testing.setUp() + from .models.meta import Base - def tearDown(self): - self.session.remove() testing.tearDown() + transaction.abort() + Base.metadata.create_all(self.engine) + + +class TestMyViewSuccessCondition(BaseTest): - def _callFUT(self, request): - from tutorial.views import add_page - return add_page(request) - - def test_it_notsubmitted(self): - _registerRoutes(self.config) - request = testing.DummyRequest() - request.matchdict = {'pagename':'AnotherPage'} - info = self._callFUT(request) - self.assertEqual(info['page'].data,'') - self.assertEqual(info['save_url'], - 'http://example.com/add_page/AnotherPage') - - def test_it_submitted(self): - from tutorial.models import Page - _registerRoutes(self.config) - request = testing.DummyRequest({'form.submitted':True, - 'body':'Hello yo!'}) - request.matchdict = {'pagename':'AnotherPage'} - self._callFUT(request) - page = self.session.query(Page).filter_by(name='AnotherPage').one() - self.assertEqual(page.data, 'Hello yo!') - -class EditPageTests(unittest.TestCase): def setUp(self): - self.session = _initTestingDB() - self.config = testing.setUp() + super(TestMyViewSuccessCondition, self).setUp() + self.init_database() - def tearDown(self): - self.session.remove() - testing.tearDown() + from .models.mymodel import MyModel + + model = MyModel(name='one', value=55) + self.session.add(model) + + def test_passing_view(self): + from .views.default import my_view + info = my_view(dummy_request(self.session)) + self.assertEqual(info['one'].name, 'one') + self.assertEqual(info['project'], 'tutorial') + + +class TestMyViewFailureCondition(BaseTest): - def _callFUT(self, request): - from tutorial.views import edit_page - return edit_page(request) - - def test_it_notsubmitted(self): - from tutorial.models import Page - _registerRoutes(self.config) - request = testing.DummyRequest() - request.matchdict = {'pagename':'abc'} - page = Page(name='abc', data='hello') - self.session.add(page) - info = self._callFUT(request) - self.assertEqual(info['page'], page) - self.assertEqual(info['save_url'], - 'http://example.com/abc/edit_page') - - def test_it_submitted(self): - from tutorial.models import Page - _registerRoutes(self.config) - request = testing.DummyRequest({'form.submitted':True, - 'body':'Hello yo!'}) - request.matchdict = {'pagename':'abc'} - page = Page(name='abc', data='hello') - self.session.add(page) - response = self._callFUT(request) - self.assertEqual(response.location, 'http://example.com/abc') - self.assertEqual(page.data, 'Hello yo!') + def test_failing_view(self): + from .views.default import my_view + info = my_view(dummy_request(self.session)) + self.assertEqual(info.status_int, 500) diff --git a/docs/tutorials/wiki2/src/views/tutorial/views.py b/docs/tutorials/wiki2/src/views/tutorial/views.py deleted file mode 100644 index a3707dab5..000000000 --- a/docs/tutorials/wiki2/src/views/tutorial/views.py +++ /dev/null @@ -1,72 +0,0 @@ -import cgi -import re -from docutils.core import publish_parts - -from pyramid.httpexceptions import ( - HTTPFound, - HTTPNotFound, - ) - -from pyramid.view import view_config - -from .models import ( - DBSession, - Page, - ) - -# regular expression used to find WikiWords -wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") - -@view_config(route_name='view_wiki') -def view_wiki(request): - return HTTPFound(location = request.route_url('view_page', - pagename='FrontPage')) - -@view_config(route_name='view_page', renderer='templates/view.pt') -def view_page(request): - pagename = request.matchdict['pagename'] - page = DBSession.query(Page).filter_by(name=pagename).first() - if page is None: - return HTTPNotFound('No such page') - - def check(match): - word = match.group(1) - exists = DBSession.query(Page).filter_by(name=word).all() - if exists: - view_url = request.route_url('view_page', pagename=word) - return '%s' % (view_url, cgi.escape(word)) - else: - add_url = request.route_url('add_page', pagename=word) - return '%s' % (add_url, cgi.escape(word)) - - content = publish_parts(page.data, writer_name='html')['html_body'] - content = wikiwords.sub(check, content) - edit_url = request.route_url('edit_page', pagename=pagename) - return dict(page=page, content=content, edit_url=edit_url) - -@view_config(route_name='add_page', renderer='templates/edit.pt') -def add_page(request): - pagename = request.matchdict['pagename'] - if 'form.submitted' in request.params: - body = request.params['body'] - page = Page(name=pagename, data=body) - DBSession.add(page) - return HTTPFound(location = request.route_url('view_page', - pagename=pagename)) - save_url = request.route_url('add_page', pagename=pagename) - page = Page(name='', data='') - return dict(page=page, save_url=save_url) - -@view_config(route_name='edit_page', renderer='templates/edit.pt') -def edit_page(request): - pagename = request.matchdict['pagename'] - page = DBSession.query(Page).filter_by(name=pagename).one() - if 'form.submitted' in request.params: - page.data = request.params['body'] - DBSession.add(page) - return HTTPFound(location = request.route_url('view_page', - pagename=pagename)) - return dict( - page=page, - save_url = request.route_url('edit_page', pagename=pagename), - ) -- cgit v1.2.3 From 73c256efdbe0b5663c726623b95c05e800939fbc Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sat, 14 Nov 2015 03:51:45 -0800 Subject: commit new wiki2/src/views/tutorial files --- .../wiki2/src/views/tutorial/models/__init__.py | 7 +++ .../wiki2/src/views/tutorial/models/meta.py | 46 +++++++++++++++ .../wiki2/src/views/tutorial/models/mymodel.py | 14 +++++ .../wiki2/src/views/tutorial/templates/edit.jinja2 | 68 +++++++++++++++++++++ .../src/views/tutorial/templates/layout.jinja2 | 66 +++++++++++++++++++++ .../src/views/tutorial/templates/mytemplate.jinja2 | 8 +++ .../wiki2/src/views/tutorial/templates/view.jinja2 | 66 +++++++++++++++++++++ .../wiki2/src/views/tutorial/views/__init__.py | 0 .../wiki2/src/views/tutorial/views/default.py | 69 ++++++++++++++++++++++ 9 files changed, 344 insertions(+) create mode 100644 docs/tutorials/wiki2/src/views/tutorial/models/__init__.py create mode 100644 docs/tutorials/wiki2/src/views/tutorial/models/meta.py create mode 100644 docs/tutorials/wiki2/src/views/tutorial/models/mymodel.py create mode 100644 docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 create mode 100644 docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2 create mode 100644 docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.jinja2 create mode 100644 docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 create mode 100644 docs/tutorials/wiki2/src/views/tutorial/views/__init__.py create mode 100644 docs/tutorials/wiki2/src/views/tutorial/views/default.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py new file mode 100644 index 000000000..7b1c62867 --- /dev/null +++ b/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py @@ -0,0 +1,7 @@ +from sqlalchemy.orm import configure_mappers +# import all models classes here for sqlalchemy mappers +# to pick up +from .mymodel import Page # flake8: noqa + +# run configure mappers to ensure we avoid any race conditions +configure_mappers() diff --git a/docs/tutorials/wiki2/src/views/tutorial/models/meta.py b/docs/tutorials/wiki2/src/views/tutorial/models/meta.py new file mode 100644 index 000000000..b72b45f9f --- /dev/null +++ b/docs/tutorials/wiki2/src/views/tutorial/models/meta.py @@ -0,0 +1,46 @@ +from sqlalchemy import engine_from_config +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from sqlalchemy.schema import MetaData +import zope.sqlalchemy + +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) + + +def includeme(config): + settings = config.get_settings() + dbmaker = get_dbmaker(get_engine(settings)) + + config.add_request_method( + lambda r: get_session(r.tm, dbmaker), + 'dbsession', + reify=True + ) + + config.include('pyramid_tm') + + +def get_session(transaction_manager, dbmaker): + dbsession = dbmaker() + zope.sqlalchemy.register(dbsession, + transaction_manager=transaction_manager) + return dbsession + + +def get_engine(settings, prefix='sqlalchemy.'): + return engine_from_config(settings, prefix) + + +def get_dbmaker(engine): + dbmaker = sessionmaker() + dbmaker.configure(bind=engine) + return dbmaker diff --git a/docs/tutorials/wiki2/src/views/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/views/tutorial/models/mymodel.py new file mode 100644 index 000000000..45571d78e --- /dev/null +++ b/docs/tutorials/wiki2/src/views/tutorial/models/mymodel.py @@ -0,0 +1,14 @@ +from .meta import Base +from sqlalchemy import ( + Column, + Integer, + Text, +) + + +class Page(Base): + """ The SQLAlchemy declarative model class for a Page object. """ + __tablename__ = 'pages' + id = Column(Integer, primary_key=True) + name = Column(Text, unique=True) + data = Column(Integer) diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 new file mode 100644 index 000000000..ad4cd17e1 --- /dev/null +++ b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 @@ -0,0 +1,68 @@ + + + + + + + + + + + {{page.name}} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+

+ Editing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} +

+

You can return to the + FrontPage. +

+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2 new file mode 100644 index 000000000..ff624c65b --- /dev/null +++ b/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2 @@ -0,0 +1,66 @@ + + + + + + + + + + + Alchemy Scaffold for The Pyramid Web Framework + + + + + + + + + + + + + +
+
+
+
+ +
+
+ {% block content %} +

No content

+ {% endblock content %} +
+
+
+ +
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.jinja2 new file mode 100644 index 000000000..bb622bf5a --- /dev/null +++ b/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+

Pyramid Alchemy scaffold

+

Welcome to {{project}}, an application generated by
the Pyramid Web Framework 1.7.dev0.

+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 new file mode 100644 index 000000000..5d47dc52f --- /dev/null +++ b/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 @@ -0,0 +1,66 @@ + + + + + + + + + + + {{page.name}} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+ {{ content|safe }} +

+ + Edit this page + +

+

+ Viewing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} +

+

You can return to the + FrontPage. +

+
+
+
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/views/tutorial/views/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/views/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/docs/tutorials/wiki2/src/views/tutorial/views/default.py b/docs/tutorials/wiki2/src/views/tutorial/views/default.py new file mode 100644 index 000000000..3e5c61a72 --- /dev/null +++ b/docs/tutorials/wiki2/src/views/tutorial/views/default.py @@ -0,0 +1,69 @@ +import cgi +import re +from docutils.core import publish_parts + +from pyramid.httpexceptions import ( + HTTPFound, + HTTPNotFound, + ) + +from pyramid.view import view_config + +from ..models.mymodel import Page + +# regular expression used to find WikiWords +wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") + +@view_config(route_name='view_wiki') +def view_wiki(request): + return HTTPFound(location=request.route_url('view_page', + pagename='FrontPage')) + +@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() + if page is None: + return HTTPNotFound('No such page') + + def check(match): + word = match.group(1) + exists = request.dbsession.query(Page).filter_by(name=word).all() + if exists: + view_url = request.route_url('view_page', pagename=word) + return '%s' % (view_url, cgi.escape(word)) + else: + add_url = request.route_url('add_page', pagename=word) + return '%s' % (add_url, cgi.escape(word)) + + content = publish_parts(page.data, writer_name='html')['html_body'] + content = wikiwords.sub(check, content) + edit_url = request.route_url('edit_page', pagename=pagename) + return dict(page=page, content=content, edit_url=edit_url) + +@view_config(route_name='add_page', renderer='templates/edit.jinja2') +def add_page(request): + pagename = request.matchdict['pagename'] + if 'form.submitted' in request.params: + body = request.params['body'] + page = Page(name=pagename, data=body) + request.dbsession.add(page) + return HTTPFound(location = request.route_url('view_page', + pagename=pagename)) + save_url = request.route_url('add_page', pagename=pagename) + page = Page(name='', data='') + return dict(page=page, save_url=save_url) + +@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() + if 'form.submitted' in request.params: + page.data = request.params['body'] + request.dbsession.add(page) + return HTTPFound(location = request.route_url('view_page', + pagename=pagename)) + return dict( + page=page, + save_url = request.route_url('edit_page', pagename=pagename), + ) -- cgit v1.2.3 From a81ac719ce8a61305f20d05e10b3397b31ec8951 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 15 Nov 2015 23:46:30 -0800 Subject: wrap content with

--- docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 index 5d47dc52f..36bb96870 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 +++ b/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 @@ -33,7 +33,7 @@

- {{ content|safe }} +

{{ content|safe }}

Edit this page -- cgit v1.2.3 From 4040cf7ef5a9843e25db69b3a17b3796f3a39fb8 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Mon, 16 Nov 2015 00:17:20 -0800 Subject: - complete rewrite of wiki2/authorization.rst - add wiki2/src/authorization/ files - improve tag in views/tutorial/templates/edit.jinja2 --- .../wiki2/src/authorization/development.ini | 4 +- .../wiki2/src/authorization/production.ini | 14 +- docs/tutorials/wiki2/src/authorization/setup.py | 2 +- .../wiki2/src/authorization/tutorial/__init__.py | 18 +-- .../wiki2/src/authorization/tutorial/models.py | 37 ----- .../src/authorization/tutorial/models/__init__.py | 7 + .../src/authorization/tutorial/models/meta.py | 46 ++++++ .../src/authorization/tutorial/models/mymodel.py | 26 +++ .../authorization/tutorial/scripts/initializedb.py | 25 +-- .../wiki2/src/authorization/tutorial/security.py | 7 - .../authorization/tutorial/security/__init__.py | 1 + .../src/authorization/tutorial/security/default.py | 7 + .../authorization/tutorial/static/theme.min.css | 2 +- .../authorization/tutorial/templates/edit.jinja2 | 73 +++++++++ .../src/authorization/tutorial/templates/edit.pt | 72 --------- .../authorization/tutorial/templates/layout.jinja2 | 66 ++++++++ .../authorization/tutorial/templates/login.jinja2 | 74 +++++++++ .../src/authorization/tutorial/templates/login.pt | 74 --------- .../tutorial/templates/mytemplate.jinja2 | 8 + .../authorization/tutorial/templates/mytemplate.pt | 66 -------- .../authorization/tutorial/templates/view.jinja2 | 71 +++++++++ .../src/authorization/tutorial/templates/view.pt | 72 --------- .../wiki2/src/authorization/tutorial/tests.py | 175 ++++++--------------- .../wiki2/src/authorization/tutorial/views.py | 124 --------------- .../src/authorization/tutorial/views/__init__.py | 0 .../src/authorization/tutorial/views/default.py | 120 ++++++++++++++ .../wiki2/src/views/tutorial/templates/edit.jinja2 | 2 +- 27 files changed, 580 insertions(+), 613 deletions(-) delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/models.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/security.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/security/default.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2 delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.jinja2 delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/views.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/views/__init__.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/views/default.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authorization/development.ini b/docs/tutorials/wiki2/src/authorization/development.ini index a9d53b296..99c4ff0fe 100644 --- a/docs/tutorials/wiki2/src/authorization/development.ini +++ b/docs/tutorials/wiki2/src/authorization/development.ini @@ -27,7 +27,7 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -host = 0.0.0.0 +host = 127.0.0.1 port = 6543 ### @@ -68,4 +68,4 @@ level = NOTSET formatter = generic [formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/tutorials/wiki2/src/authorization/production.ini b/docs/tutorials/wiki2/src/authorization/production.ini index 4684d2f7a..97acfbd7d 100644 --- a/docs/tutorials/wiki2/src/authorization/production.ini +++ b/docs/tutorials/wiki2/src/authorization/production.ini @@ -1,3 +1,8 @@ +### +# app configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +### + [app:main] use = egg:tutorial @@ -16,7 +21,10 @@ use = egg:waitress#main host = 0.0.0.0 port = 6543 -# Begin logging configuration +### +# logging configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html +### [loggers] keys = root, tutorial, sqlalchemy @@ -51,6 +59,4 @@ level = NOTSET formatter = generic [formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s - -# End logging configuration +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/tutorials/wiki2/src/authorization/setup.py b/docs/tutorials/wiki2/src/authorization/setup.py index 09bd63d33..d4e5a4072 100644 --- a/docs/tutorials/wiki2/src/authorization/setup.py +++ b/docs/tutorials/wiki2/src/authorization/setup.py @@ -10,7 +10,7 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: requires = [ 'pyramid', - 'pyramid_chameleon', + 'pyramid_jinja2', 'pyramid_debugtoolbar', 'pyramid_tm', 'SQLAlchemy', diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py index 2ada42171..084fee19f 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py @@ -2,30 +2,20 @@ from pyramid.config import Configurator from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy -from sqlalchemy import engine_from_config - -from tutorial.security import groupfinder - -from .models import ( - DBSession, - Base, - ) - +from security.default import groupfinder def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ - engine = engine_from_config(settings, 'sqlalchemy.') - DBSession.configure(bind=engine) - Base.metadata.bind = engine authn_policy = AuthTktAuthenticationPolicy( 'sosecret', callback=groupfinder, hashalg='sha512') authz_policy = ACLAuthorizationPolicy() config = Configurator(settings=settings, - root_factory='tutorial.models.RootFactory') + root_factory='tutorial.models.mymodel.RootFactory') config.set_authentication_policy(authn_policy) config.set_authorization_policy(authz_policy) - config.include('pyramid_chameleon') + config.include('pyramid_jinja2') + config.include('.models.meta') config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('view_wiki', '/') config.add_route('login', '/login') diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models.py b/docs/tutorials/wiki2/src/authorization/tutorial/models.py deleted file mode 100644 index 4f7e1e024..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/models.py +++ /dev/null @@ -1,37 +0,0 @@ -from pyramid.security import ( - Allow, - Everyone, - ) - -from sqlalchemy import ( - Column, - Integer, - Text, - ) - -from sqlalchemy.ext.declarative import declarative_base - -from sqlalchemy.orm import ( - scoped_session, - sessionmaker, - ) - -from zope.sqlalchemy import ZopeTransactionExtension - -DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) -Base = declarative_base() - - -class Page(Base): - """ The SQLAlchemy declarative model class for a Page object. """ - __tablename__ = 'pages' - id = Column(Integer, primary_key=True) - name = Column(Text, unique=True) - data = Column(Text) - - -class RootFactory(object): - __acl__ = [ (Allow, Everyone, 'view'), - (Allow, 'group:editors', 'edit') ] - def __init__(self, request): - pass diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py new file mode 100644 index 000000000..7b1c62867 --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py @@ -0,0 +1,7 @@ +from sqlalchemy.orm import configure_mappers +# import all models classes here for sqlalchemy mappers +# to pick up +from .mymodel import Page # flake8: noqa + +# run configure mappers to ensure we avoid any race conditions +configure_mappers() diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py new file mode 100644 index 000000000..b72b45f9f --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py @@ -0,0 +1,46 @@ +from sqlalchemy import engine_from_config +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from sqlalchemy.schema import MetaData +import zope.sqlalchemy + +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) + + +def includeme(config): + settings = config.get_settings() + dbmaker = get_dbmaker(get_engine(settings)) + + config.add_request_method( + lambda r: get_session(r.tm, dbmaker), + 'dbsession', + reify=True + ) + + config.include('pyramid_tm') + + +def get_session(transaction_manager, dbmaker): + dbsession = dbmaker() + zope.sqlalchemy.register(dbsession, + transaction_manager=transaction_manager) + return dbsession + + +def get_engine(settings, prefix='sqlalchemy.'): + return engine_from_config(settings, prefix) + + +def get_dbmaker(engine): + dbmaker = sessionmaker() + dbmaker.configure(bind=engine) + return dbmaker diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py new file mode 100644 index 000000000..03e2f90ca --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py @@ -0,0 +1,26 @@ +from .meta import Base + +from pyramid.security import ( + Allow, + Everyone, + ) + +from sqlalchemy import ( + Column, + Integer, + Text, + ) + + +class Page(Base): + """ The SQLAlchemy declarative model class for a Page object. """ + __tablename__ = 'pages' + id = Column(Integer, primary_key=True) + name = Column(Text, unique=True) + data = Column(Integer) + +class RootFactory(object): + __acl__ = [ (Allow, Everyone, 'view'), + (Allow, 'group:editors', 'edit') ] + def __init__(self, request): + pass \ No newline at end of file diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py index 23a5f13f4..4aac4a848 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py @@ -2,36 +2,41 @@ import os import sys import transaction -from sqlalchemy import engine_from_config - from pyramid.paster import ( get_appsettings, setup_logging, ) -from ..models import ( - DBSession, - Page, +from ..models.meta import ( Base, + get_session, + get_engine, + get_dbmaker, ) +from ..models.mymodel import Page def usage(argv): cmd = os.path.basename(argv[0]) - print('usage: %s <config_uri>\n' + 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: + if len(argv) < 2: usage(argv) config_uri = argv[1] setup_logging(config_uri) settings = get_appsettings(config_uri) - engine = engine_from_config(settings, 'sqlalchemy.') - DBSession.configure(bind=engine) + + engine = get_engine(settings) + dbmaker = get_dbmaker(engine) + + dbsession = get_session(transaction.manager, dbmaker) + Base.metadata.create_all(engine) + with transaction.manager: model = Page(name='FrontPage', data='This is the front page') - DBSession.add(model) + dbsession.add(model) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/security.py b/docs/tutorials/wiki2/src/authorization/tutorial/security.py deleted file mode 100644 index d88c9c71f..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/security.py +++ /dev/null @@ -1,7 +0,0 @@ -USERS = {'editor':'editor', - 'viewer':'viewer'} -GROUPS = {'editor':['group:editors']} - -def groupfinder(userid, request): - if userid in USERS: - return GROUPS.get(userid, []) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py @@ -0,0 +1 @@ +# package diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/security/default.py b/docs/tutorials/wiki2/src/authorization/tutorial/security/default.py new file mode 100644 index 000000000..d88c9c71f --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/security/default.py @@ -0,0 +1,7 @@ +USERS = {'editor':'editor', + 'viewer':'viewer'} +GROUPS = {'editor':['group:editors']} + +def groupfinder(userid, request): + if userid in USERS: + return GROUPS.get(userid, []) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/static/theme.min.css b/docs/tutorials/wiki2/src/authorization/tutorial/static/theme.min.css index 2f924bcc5..0d25de5b6 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/static/theme.min.css +++ b/docs/tutorials/wiki2/src/authorization/tutorial/static/theme.min.css @@ -1 +1 @@ -@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a{color:#fff}.starter-template .links ul li a:hover{text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} \ No newline at end of file +@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a,a{color:#f2b7bd;text-decoration:underline}.starter-template .links ul li a:hover,a:hover{color:#fff;text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 new file mode 100644 index 000000000..c4f3a2c93 --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 @@ -0,0 +1,73 @@ +<!DOCTYPE html> +<html lang="{{request.locale_name}}"> + <head> + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <meta name="description" content="pyramid web application"> + <meta name="author" content="Pylons Project"> + <link rel="shortcut icon" href="{{request.static_url('tutorial:static/pyramid-16x16.png')}}"> + + <title>Edit{% if page.name %} {{page.name}}{% endif %} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + + + + + + + + + + + + + +

+
+
+
+ +
+
+
+ {% if logged_in %} +

+ Logout +

+ {% endif %} +

+ Editing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} +

+

You can return to the + FrontPage. +

+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt deleted file mode 100644 index ed355434d..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.pt +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - ${page.name} - Pyramid tutorial wiki (based on - TurboGears 20-Minute Wiki) - - - - - - - - - - - - -
-
-
-
- -
-
-
-

- Logout -

-

- Editing Page Name Goes - Here -

-

You can return to the - FrontPage. -

-
-
- -
-
- -
-
-
-
-
-
- -
-
-
- - - - - - - - diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 new file mode 100644 index 000000000..ff624c65b --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 @@ -0,0 +1,66 @@ + + + + + + + + + + + Alchemy Scaffold for The Pyramid Web Framework + + + + + + + + + + + + + +
+
+
+
+ +
+
+ {% block content %} +

No content

+ {% endblock content %} +
+
+
+ +
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2 new file mode 100644 index 000000000..a80a2a165 --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2 @@ -0,0 +1,74 @@ + + + + + + + + + + + Login - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+

+ + Login +
+ {{ message }} +

+
+ +
+ + +
+
+ + +
+
+ +
+
+
+
+
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt deleted file mode 100644 index 4a938e9bb..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.pt +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - Login - Pyramid tutorial wiki (based on - TurboGears 20-Minute Wiki) - - - - - - - - - - - - -
-
-
-
- -
-
-
-

- - Login -
- -

-
- -
- - -
-
- - -
-
- -
-
-
-
-
-
- -
-
-
- - - - - - - - diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.jinja2 new file mode 100644 index 000000000..bb622bf5a --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+

Pyramid Alchemy scaffold

+

Welcome to {{project}}, an application generated by
the Pyramid Web Framework 1.7.dev0.

+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt deleted file mode 100644 index c9b0cec21..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - Alchemy Scaffold for The Pyramid Web Framework - - - - - - - - - - - - - -
-
-
-
- -
-
-
-

Pyramid Alchemy scaffold

-

Welcome to ${project}, an application generated by
the Pyramid Web Framework.

-
-
-
- -
- -
-
-
- - - - - - - - diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 new file mode 100644 index 000000000..a7afc66fc --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 @@ -0,0 +1,71 @@ + + + + + + + + + + + {{page.name}} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+ {% if logged_in %} +

+ Logout +

+ {% endif %} +

{{ content|safe }}

+

+ + Edit this page + +

+

+ Viewing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} +

+

You can return to the + FrontPage. +

+
+
+
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt deleted file mode 100644 index 02cb8e73b..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.pt +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - ${page.name} - Pyramid tutorial wiki (based on - TurboGears 20-Minute Wiki) - - - - - - - - - - - - -
-
-
-
- -
-
-
-

- Logout -

-
- Page text goes here. -
-

- - Edit this page - -

-

- Viewing - Page Name Goes Here -

-

You can return to the - FrontPage. -

-
-
-
-
- -
-
-
- - - - - - - - diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py index 9f01d2da5..b947e3bb1 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py @@ -3,144 +3,63 @@ import transaction from pyramid import testing -def _initTestingDB(): - from sqlalchemy import create_engine - from tutorial.models import ( - DBSession, - Page, - Base - ) - engine = create_engine('sqlite://') - Base.metadata.create_all(engine) - DBSession.configure(bind=engine) - with transaction.manager: - model = Page(name='FrontPage', data='This is the front page') - DBSession.add(model) - return DBSession - -def _registerRoutes(config): - config.add_route('view_page', '{pagename}') - config.add_route('edit_page', '{pagename}/edit_page') - config.add_route('add_page', 'add_page/{pagename}') - -class ViewWikiTests(unittest.TestCase): + +def dummy_request(dbsession): + return testing.DummyRequest(dbsession=dbsession) + + +class BaseTest(unittest.TestCase): def setUp(self): - self.config = testing.setUp() - self.session = _initTestingDB() + self.config = testing.setUp(settings={ + 'sqlalchemy.url': 'sqlite:///:memory:' + }) + self.config.include('.models.meta') + settings = self.config.get_settings() - def tearDown(self): - self.session.remove() - testing.tearDown() + from .models.meta import ( + get_session, + get_engine, + get_dbmaker, + ) - def _callFUT(self, request): - from tutorial.views import view_wiki - return view_wiki(request) + self.engine = get_engine(settings) + dbmaker = get_dbmaker(self.engine) - def test_it(self): - _registerRoutes(self.config) - request = testing.DummyRequest() - response = self._callFUT(request) - self.assertEqual(response.location, 'http://example.com/FrontPage') + self.session = get_session(transaction.manager, dbmaker) -class ViewPageTests(unittest.TestCase): - def setUp(self): - self.session = _initTestingDB() - self.config = testing.setUp() + def init_database(self): + from .models.meta import Base + Base.metadata.create_all(self.engine) def tearDown(self): - self.session.remove() - testing.tearDown() - - def _callFUT(self, request): - from tutorial.views import view_page - return view_page(request) - - def test_it(self): - from tutorial.models import Page - request = testing.DummyRequest() - request.matchdict['pagename'] = 'IDoExist' - page = Page(name='IDoExist', data='Hello CruelWorld IDoExist') - self.session.add(page) - _registerRoutes(self.config) - info = self._callFUT(request) - self.assertEqual(info['page'], page) - self.assertEqual( - info['content'], - '
\n' - '

Hello ' - 'CruelWorld ' - '' - 'IDoExist' - '

\n
\n') - self.assertEqual(info['edit_url'], - 'http://example.com/IDoExist/edit_page') - - -class AddPageTests(unittest.TestCase): - def setUp(self): - self.session = _initTestingDB() - self.config = testing.setUp() + from .models.meta import Base - def tearDown(self): - self.session.remove() testing.tearDown() + transaction.abort() + Base.metadata.create_all(self.engine) + + +class TestMyViewSuccessCondition(BaseTest): - def _callFUT(self, request): - from tutorial.views import add_page - return add_page(request) - - def test_it_notsubmitted(self): - _registerRoutes(self.config) - request = testing.DummyRequest() - request.matchdict = {'pagename':'AnotherPage'} - info = self._callFUT(request) - self.assertEqual(info['page'].data,'') - self.assertEqual(info['save_url'], - 'http://example.com/add_page/AnotherPage') - - def test_it_submitted(self): - from tutorial.models import Page - _registerRoutes(self.config) - request = testing.DummyRequest({'form.submitted':True, - 'body':'Hello yo!'}) - request.matchdict = {'pagename':'AnotherPage'} - self._callFUT(request) - page = self.session.query(Page).filter_by(name='AnotherPage').one() - self.assertEqual(page.data, 'Hello yo!') - -class EditPageTests(unittest.TestCase): def setUp(self): - self.session = _initTestingDB() - self.config = testing.setUp() + super(TestMyViewSuccessCondition, self).setUp() + self.init_database() - def tearDown(self): - self.session.remove() - testing.tearDown() + from .models.mymodel import MyModel + + model = MyModel(name='one', value=55) + self.session.add(model) + + def test_passing_view(self): + from .views.default import my_view + info = my_view(dummy_request(self.session)) + self.assertEqual(info['one'].name, 'one') + self.assertEqual(info['project'], 'tutorial') + + +class TestMyViewFailureCondition(BaseTest): - def _callFUT(self, request): - from tutorial.views import edit_page - return edit_page(request) - - def test_it_notsubmitted(self): - from tutorial.models import Page - _registerRoutes(self.config) - request = testing.DummyRequest() - request.matchdict = {'pagename':'abc'} - page = Page(name='abc', data='hello') - self.session.add(page) - info = self._callFUT(request) - self.assertEqual(info['page'], page) - self.assertEqual(info['save_url'], - 'http://example.com/abc/edit_page') - - def test_it_submitted(self): - from tutorial.models import Page - _registerRoutes(self.config) - request = testing.DummyRequest({'form.submitted':True, - 'body':'Hello yo!'}) - request.matchdict = {'pagename':'abc'} - page = Page(name='abc', data='hello') - self.session.add(page) - response = self._callFUT(request) - self.assertEqual(response.location, 'http://example.com/abc') - self.assertEqual(page.data, 'Hello yo!') + def test_failing_view(self): + from .views.default import my_view + info = my_view(dummy_request(self.session)) + self.assertEqual(info.status_int, 500) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views.py b/docs/tutorials/wiki2/src/authorization/tutorial/views.py deleted file mode 100644 index e954d5a31..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/views.py +++ /dev/null @@ -1,124 +0,0 @@ -import re -from docutils.core import publish_parts - -from pyramid.httpexceptions import ( - HTTPFound, - HTTPNotFound, - ) - -from pyramid.view import ( - view_config, - forbidden_view_config, - ) - -from pyramid.security import ( - remember, - forget, - ) - -from .security import USERS - -from .models import ( - DBSession, - Page, - ) - - -# regular expression used to find WikiWords -wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") - -@view_config(route_name='view_wiki', - permission='view') -def view_wiki(request): - return HTTPFound(location = request.route_url('view_page', - pagename='FrontPage')) - -@view_config(route_name='view_page', renderer='templates/view.pt', - permission='view') -def view_page(request): - pagename = request.matchdict['pagename'] - page = DBSession.query(Page).filter_by(name=pagename).first() - if page is None: - return HTTPNotFound('No such page') - - def check(match): - word = match.group(1) - exists = DBSession.query(Page).filter_by(name=word).all() - if exists: - view_url = request.route_url('view_page', pagename=word) - return '%s' % (view_url, word) - else: - add_url = request.route_url('add_page', pagename=word) - return '%s' % (add_url, word) - - content = publish_parts(page.data, writer_name='html')['html_body'] - content = wikiwords.sub(check, content) - edit_url = request.route_url('edit_page', pagename=pagename) - return dict(page=page, content=content, edit_url=edit_url, - logged_in=request.authenticated_userid) - -@view_config(route_name='add_page', renderer='templates/edit.pt', - permission='edit') -def add_page(request): - pagename = request.matchdict['pagename'] - if 'form.submitted' in request.params: - body = request.params['body'] - page = Page(name=pagename, data=body) - DBSession.add(page) - return HTTPFound(location = request.route_url('view_page', - pagename=pagename)) - save_url = request.route_url('add_page', pagename=pagename) - page = Page(name='', data='') - return dict(page=page, save_url=save_url, - logged_in=request.authenticated_userid) - -@view_config(route_name='edit_page', renderer='templates/edit.pt', - permission='edit') -def edit_page(request): - pagename = request.matchdict['pagename'] - page = DBSession.query(Page).filter_by(name=pagename).one() - if 'form.submitted' in request.params: - page.data = request.params['body'] - DBSession.add(page) - return HTTPFound(location = request.route_url('view_page', - pagename=pagename)) - return dict( - page=page, - save_url=request.route_url('edit_page', pagename=pagename), - logged_in=request.authenticated_userid - ) - -@view_config(route_name='login', renderer='templates/login.pt') -@forbidden_view_config(renderer='templates/login.pt') -def login(request): - login_url = request.route_url('login') - referrer = request.url - if referrer == login_url: - referrer = '/' # never use the login form itself as came_from - came_from = request.params.get('came_from', referrer) - message = '' - login = '' - password = '' - if 'form.submitted' in request.params: - login = request.params['login'] - password = request.params['password'] - if USERS.get(login) == password: - headers = remember(request, login) - return HTTPFound(location = came_from, - headers = headers) - message = 'Failed login' - - return dict( - message = message, - url = request.application_url + '/login', - came_from = came_from, - login = login, - password = password, - ) - -@view_config(route_name='logout') -def logout(request): - headers = forget(request) - return HTTPFound(location = request.route_url('view_wiki'), - headers = headers) - diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py new file mode 100644 index 000000000..f35f041a4 --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py @@ -0,0 +1,120 @@ +import cgi +import re +from docutils.core import publish_parts + +from pyramid.httpexceptions import ( + HTTPFound, + HTTPNotFound, + ) + +from pyramid.view import ( + view_config, + forbidden_view_config, + ) + +from pyramid.security import ( + remember, + forget, + ) + +from ..security.default import USERS + +from ..models.mymodel import Page + +# regular expression used to find WikiWords +wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") + +@view_config(route_name='view_wiki', + permission='view') +def view_wiki(request): + return HTTPFound(location=request.route_url('view_page', + pagename='FrontPage')) + +@view_config(route_name='view_page', renderer='templates/view.jinja2', + permission='view') +def view_page(request): + pagename = request.matchdict['pagename'] + page = request.dbsession.query(Page).filter_by(name=pagename).first() + if page is None: + return HTTPNotFound('No such page') + + def check(match): + word = match.group(1) + exists = request.dbsession.query(Page).filter_by(name=word).all() + if exists: + view_url = request.route_url('view_page', pagename=word) + return '%s' % (view_url, cgi.escape(word)) + else: + add_url = request.route_url('add_page', pagename=word) + return '%s' % (add_url, cgi.escape(word)) + + content = publish_parts(page.data, writer_name='html')['html_body'] + content = wikiwords.sub(check, content) + edit_url = request.route_url('edit_page', pagename=pagename) + return dict(page=page, content=content, edit_url=edit_url, + logged_in=request.authenticated_userid) + +@view_config(route_name='add_page', renderer='templates/edit.jinja2', + permission='edit') +def add_page(request): + pagename = request.matchdict['pagename'] + if 'form.submitted' in request.params: + body = request.params['body'] + page = Page(name=pagename, data=body) + request.dbsession.add(page) + return HTTPFound(location = request.route_url('view_page', + pagename=pagename)) + save_url = request.route_url('add_page', pagename=pagename) + page = Page(name='', data='') + return dict(page=page, save_url=save_url, + logged_in=request.authenticated_userid) + +@view_config(route_name='edit_page', renderer='templates/edit.jinja2', + permission='edit') +def edit_page(request): + pagename = request.matchdict['pagename'] + page = request.dbsession.query(Page).filter_by(name=pagename).one() + if 'form.submitted' in request.params: + page.data = request.params['body'] + request.dbsession.add(page) + return HTTPFound(location = request.route_url('view_page', + pagename=pagename)) + return dict( + page=page, + save_url = request.route_url('edit_page', pagename=pagename), + logged_in=request.authenticated_userid + ) + +@view_config(route_name='login', renderer='templates/login.jinja2') +@forbidden_view_config(renderer='templates/login.jinja2') +def login(request): + login_url = request.route_url('login') + referrer = request.url + if referrer == login_url: + referrer = '/' # never use the login form itself as came_from + came_from = request.params.get('came_from', referrer) + message = '' + login = '' + password = '' + if 'form.submitted' in request.params: + login = request.params['login'] + password = request.params['password'] + if USERS.get(login) == password: + headers = remember(request, login) + return HTTPFound(location = came_from, + headers = headers) + message = 'Failed login' + + return dict( + message = message, + url = request.application_url + '/login', + came_from = came_from, + login = login, + password = password, + ) + +@view_config(route_name='logout') +def logout(request): + headers = forget(request) + return HTTPFound(location = request.route_url('view_wiki'), + headers = headers) diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 index ad4cd17e1..b3aadfc2e 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 +++ b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 @@ -8,7 +8,7 @@ - {{page.name}} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + Edit{% if page.name %} {{page.name}}{% endif %} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) -- cgit v1.2.3 From f10fb24a3663cb070b28b8020fde957d16563400 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Wed, 2 Dec 2015 04:18:10 -0800 Subject: - rewrite wiki2/tests.rst (removed an obsolete reference to testing models, per 2885a7b96545c037109d7999319f74869a640050) - add wiki2/src/tests/ files. special thanks to @ppaez --- docs/tutorials/wiki2/src/tests/development.ini | 4 +- docs/tutorials/wiki2/src/tests/production.ini | 14 +- docs/tutorials/wiki2/src/tests/setup.py | 4 +- .../tutorials/wiki2/src/tests/tutorial/__init__.py | 18 +- docs/tutorials/wiki2/src/tests/tutorial/models.py | 37 ---- .../wiki2/src/tests/tutorial/models/__init__.py | 7 + .../wiki2/src/tests/tutorial/models/meta.py | 46 ++++ .../wiki2/src/tests/tutorial/models/mymodel.py | 26 +++ .../src/tests/tutorial/scripts/initializedb.py | 25 ++- .../tutorials/wiki2/src/tests/tutorial/security.py | 7 - .../wiki2/src/tests/tutorial/security/__init__.py | 1 + .../wiki2/src/tests/tutorial/security/default.py | 7 + .../wiki2/src/tests/tutorial/static/theme.min.css | 2 +- .../wiki2/src/tests/tutorial/templates/edit.jinja2 | 73 +++++++ .../wiki2/src/tests/tutorial/templates/edit.pt | 74 ------- .../src/tests/tutorial/templates/layout.jinja2 | 66 ++++++ .../src/tests/tutorial/templates/login.jinja2 | 74 +++++++ .../wiki2/src/tests/tutorial/templates/login.pt | 54 ----- .../src/tests/tutorial/templates/mytemplate.jinja2 | 8 + .../src/tests/tutorial/templates/mytemplate.pt | 66 ------ .../wiki2/src/tests/tutorial/templates/view.jinja2 | 71 +++++++ .../wiki2/src/tests/tutorial/templates/view.pt | 74 ------- docs/tutorials/wiki2/src/tests/tutorial/tests.py | 235 --------------------- .../wiki2/src/tests/tutorial/tests/__init__.py | 1 + .../src/tests/tutorial/tests/test_functional.py | 141 +++++++++++++ .../wiki2/src/tests/tutorial/tests/test_views.py | 168 +++++++++++++++ docs/tutorials/wiki2/src/tests/tutorial/views.py | 123 ----------- .../wiki2/src/tests/tutorial/views/__init__.py | 0 .../wiki2/src/tests/tutorial/views/default.py | 120 +++++++++++ 29 files changed, 843 insertions(+), 703 deletions(-) delete mode 100644 docs/tutorials/wiki2/src/tests/tutorial/models.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/models/meta.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/models/mymodel.py delete mode 100644 docs/tutorials/wiki2/src/tests/tutorial/security.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/security/default.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 delete mode 100644 docs/tutorials/wiki2/src/tests/tutorial/templates/edit.pt create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/templates/login.jinja2 delete mode 100644 docs/tutorials/wiki2/src/tests/tutorial/templates/login.pt create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.jinja2 delete mode 100644 docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 delete mode 100644 docs/tutorials/wiki2/src/tests/tutorial/templates/view.pt delete mode 100644 docs/tutorials/wiki2/src/tests/tutorial/tests.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/tests/__init__.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py delete mode 100644 docs/tutorials/wiki2/src/tests/tutorial/views.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/views/__init__.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/views/default.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/tests/development.ini b/docs/tutorials/wiki2/src/tests/development.ini index a9d53b296..99c4ff0fe 100644 --- a/docs/tutorials/wiki2/src/tests/development.ini +++ b/docs/tutorials/wiki2/src/tests/development.ini @@ -27,7 +27,7 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -host = 0.0.0.0 +host = 127.0.0.1 port = 6543 ### @@ -68,4 +68,4 @@ level = NOTSET formatter = generic [formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/tutorials/wiki2/src/tests/production.ini b/docs/tutorials/wiki2/src/tests/production.ini index 4684d2f7a..97acfbd7d 100644 --- a/docs/tutorials/wiki2/src/tests/production.ini +++ b/docs/tutorials/wiki2/src/tests/production.ini @@ -1,3 +1,8 @@ +### +# app configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +### + [app:main] use = egg:tutorial @@ -16,7 +21,10 @@ use = egg:waitress#main host = 0.0.0.0 port = 6543 -# Begin logging configuration +### +# logging configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html +### [loggers] keys = root, tutorial, sqlalchemy @@ -51,6 +59,4 @@ level = NOTSET formatter = generic [formatter_generic] -format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s - -# End logging configuration +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/tutorials/wiki2/src/tests/setup.py b/docs/tutorials/wiki2/src/tests/setup.py index d8486e462..f640b4399 100644 --- a/docs/tutorials/wiki2/src/tests/setup.py +++ b/docs/tutorials/wiki2/src/tests/setup.py @@ -10,7 +10,7 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: requires = [ 'pyramid', - 'pyramid_chameleon', + 'pyramid_jinja2', 'pyramid_debugtoolbar', 'pyramid_tm', 'SQLAlchemy', @@ -18,7 +18,7 @@ requires = [ 'zope.sqlalchemy', 'waitress', 'docutils', - 'WebTest', # add this + 'WebTest', ] setup(name='tutorial', diff --git a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py index cee89184b..084fee19f 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py @@ -2,30 +2,20 @@ from pyramid.config import Configurator from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy -from sqlalchemy import engine_from_config - -from tutorial.security import groupfinder - -from .models import ( - DBSession, - Base, - ) - +from security.default import groupfinder def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ - engine = engine_from_config(settings, 'sqlalchemy.') - DBSession.configure(bind=engine) - Base.metadata.bind = engine authn_policy = AuthTktAuthenticationPolicy( 'sosecret', callback=groupfinder, hashalg='sha512') authz_policy = ACLAuthorizationPolicy() config = Configurator(settings=settings, - root_factory='tutorial.models.RootFactory') - config.include('pyramid_chameleon') + root_factory='tutorial.models.mymodel.RootFactory') config.set_authentication_policy(authn_policy) config.set_authorization_policy(authz_policy) + config.include('pyramid_jinja2') + config.include('.models.meta') config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('view_wiki', '/') config.add_route('login', '/login') diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models.py b/docs/tutorials/wiki2/src/tests/tutorial/models.py deleted file mode 100644 index 4f7e1e024..000000000 --- a/docs/tutorials/wiki2/src/tests/tutorial/models.py +++ /dev/null @@ -1,37 +0,0 @@ -from pyramid.security import ( - Allow, - Everyone, - ) - -from sqlalchemy import ( - Column, - Integer, - Text, - ) - -from sqlalchemy.ext.declarative import declarative_base - -from sqlalchemy.orm import ( - scoped_session, - sessionmaker, - ) - -from zope.sqlalchemy import ZopeTransactionExtension - -DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) -Base = declarative_base() - - -class Page(Base): - """ The SQLAlchemy declarative model class for a Page object. """ - __tablename__ = 'pages' - id = Column(Integer, primary_key=True) - name = Column(Text, unique=True) - data = Column(Text) - - -class RootFactory(object): - __acl__ = [ (Allow, Everyone, 'view'), - (Allow, 'group:editors', 'edit') ] - def __init__(self, request): - pass diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py new file mode 100644 index 000000000..7b1c62867 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py @@ -0,0 +1,7 @@ +from sqlalchemy.orm import configure_mappers +# import all models classes here for sqlalchemy mappers +# to pick up +from .mymodel import Page # flake8: noqa + +# run configure mappers to ensure we avoid any race conditions +configure_mappers() diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models/meta.py b/docs/tutorials/wiki2/src/tests/tutorial/models/meta.py new file mode 100644 index 000000000..b72b45f9f --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/models/meta.py @@ -0,0 +1,46 @@ +from sqlalchemy import engine_from_config +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +from sqlalchemy.schema import MetaData +import zope.sqlalchemy + +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) + + +def includeme(config): + settings = config.get_settings() + dbmaker = get_dbmaker(get_engine(settings)) + + config.add_request_method( + lambda r: get_session(r.tm, dbmaker), + 'dbsession', + reify=True + ) + + config.include('pyramid_tm') + + +def get_session(transaction_manager, dbmaker): + dbsession = dbmaker() + zope.sqlalchemy.register(dbsession, + transaction_manager=transaction_manager) + return dbsession + + +def get_engine(settings, prefix='sqlalchemy.'): + return engine_from_config(settings, prefix) + + +def get_dbmaker(engine): + dbmaker = sessionmaker() + dbmaker.configure(bind=engine) + return dbmaker diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/tests/tutorial/models/mymodel.py new file mode 100644 index 000000000..03e2f90ca --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/models/mymodel.py @@ -0,0 +1,26 @@ +from .meta import Base + +from pyramid.security import ( + Allow, + Everyone, + ) + +from sqlalchemy import ( + Column, + Integer, + Text, + ) + + +class Page(Base): + """ The SQLAlchemy declarative model class for a Page object. """ + __tablename__ = 'pages' + id = Column(Integer, primary_key=True) + name = Column(Text, unique=True) + data = Column(Integer) + +class RootFactory(object): + __acl__ = [ (Allow, Everyone, 'view'), + (Allow, 'group:editors', 'edit') ] + def __init__(self, request): + pass \ No newline at end of file diff --git a/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py index 23a5f13f4..4aac4a848 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py @@ -2,36 +2,41 @@ import os import sys import transaction -from sqlalchemy import engine_from_config - from pyramid.paster import ( get_appsettings, setup_logging, ) -from ..models import ( - DBSession, - Page, +from ..models.meta import ( Base, + get_session, + get_engine, + get_dbmaker, ) +from ..models.mymodel import Page def usage(argv): cmd = os.path.basename(argv[0]) - print('usage: %s \n' + print('usage: %s [var=value]\n' '(example: "%s development.ini")' % (cmd, cmd)) sys.exit(1) def main(argv=sys.argv): - if len(argv) != 2: + if len(argv) < 2: usage(argv) config_uri = argv[1] setup_logging(config_uri) settings = get_appsettings(config_uri) - engine = engine_from_config(settings, 'sqlalchemy.') - DBSession.configure(bind=engine) + + engine = get_engine(settings) + dbmaker = get_dbmaker(engine) + + dbsession = get_session(transaction.manager, dbmaker) + Base.metadata.create_all(engine) + with transaction.manager: model = Page(name='FrontPage', data='This is the front page') - DBSession.add(model) + dbsession.add(model) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/security.py b/docs/tutorials/wiki2/src/tests/tutorial/security.py deleted file mode 100644 index d88c9c71f..000000000 --- a/docs/tutorials/wiki2/src/tests/tutorial/security.py +++ /dev/null @@ -1,7 +0,0 @@ -USERS = {'editor':'editor', - 'viewer':'viewer'} -GROUPS = {'editor':['group:editors']} - -def groupfinder(userid, request): - if userid in USERS: - return GROUPS.get(userid, []) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py @@ -0,0 +1 @@ +# package diff --git a/docs/tutorials/wiki2/src/tests/tutorial/security/default.py b/docs/tutorials/wiki2/src/tests/tutorial/security/default.py new file mode 100644 index 000000000..d88c9c71f --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/security/default.py @@ -0,0 +1,7 @@ +USERS = {'editor':'editor', + 'viewer':'viewer'} +GROUPS = {'editor':['group:editors']} + +def groupfinder(userid, request): + if userid in USERS: + return GROUPS.get(userid, []) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/static/theme.min.css b/docs/tutorials/wiki2/src/tests/tutorial/static/theme.min.css index 2f924bcc5..0d25de5b6 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/static/theme.min.css +++ b/docs/tutorials/wiki2/src/tests/tutorial/static/theme.min.css @@ -1 +1 @@ -@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a{color:#fff}.starter-template .links ul li a:hover{text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} \ No newline at end of file +@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a,a{color:#f2b7bd;text-decoration:underline}.starter-template .links ul li a:hover,a:hover{color:#fff;text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 new file mode 100644 index 000000000..c4f3a2c93 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 @@ -0,0 +1,73 @@ + + + + + + + + + + + Edit{% if page.name %} {{page.name}}{% endif %} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+ {% if logged_in %} +

+ Logout +

+ {% endif %} +

+ Editing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} +

+

You can return to the + FrontPage. +

+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.pt b/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.pt deleted file mode 100644 index 50e55c850..000000000 --- a/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.pt +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - ${page.name} - Pyramid tutorial wiki (based on - TurboGears 20-Minute Wiki) - - - - - - - - - - - - -
-
-
-
- -
-
-
-

- Editing Page Name Goes - Here -

-

You can return to the - FrontPage. -

-

- - Logout - -

-
-
- -
-
- -
-
-
-
-
-
- -
-
-
- - - - - - - - diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 new file mode 100644 index 000000000..ff624c65b --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 @@ -0,0 +1,66 @@ + + + + + + + + + + + Alchemy Scaffold for The Pyramid Web Framework + + + + + + + + + + + + + +
+
+
+
+ +
+
+ {% block content %} +

No content

+ {% endblock content %} +
+
+
+ +
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/login.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/login.jinja2 new file mode 100644 index 000000000..a80a2a165 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/login.jinja2 @@ -0,0 +1,74 @@ + + + + + + + + + + + Login - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+

+ + Login +
+ {{ message }} +

+
+ +
+ + +
+
+ + +
+
+ +
+
+
+
+
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/login.pt b/docs/tutorials/wiki2/src/tests/tutorial/templates/login.pt deleted file mode 100644 index 5f8e9b98c..000000000 --- a/docs/tutorials/wiki2/src/tests/tutorial/templates/login.pt +++ /dev/null @@ -1,54 +0,0 @@ - - - - Login - Pyramid tutorial wiki (based on TurboGears - 20-Minute Wiki) - - - - - - - - -
-
-
-
- pyramid -
-
-
-
-
-
- Login
- -
- -
-
-
-
-
- -
-
- -
-
-
-
- - diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.jinja2 new file mode 100644 index 000000000..bb622bf5a --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+

Pyramid Alchemy scaffold

+

Welcome to {{project}}, an application generated by
the Pyramid Web Framework 1.7.dev0.

+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt deleted file mode 100644 index c9b0cec21..000000000 --- a/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - Alchemy Scaffold for The Pyramid Web Framework - - - - - - - - - - - - - -
-
-
-
- -
-
-
-

Pyramid Alchemy scaffold

-

Welcome to ${project}, an application generated by
the Pyramid Web Framework.

-
-
-
- -
- -
-
-
- - - - - - - - diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 new file mode 100644 index 000000000..a7afc66fc --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 @@ -0,0 +1,71 @@ + + + + + + + + + + + {{page.name}} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+ {% if logged_in %} +

+ Logout +

+ {% endif %} +

{{ content|safe }}

+

+ + Edit this page + +

+

+ Viewing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} +

+

You can return to the + FrontPage. +

+
+
+
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/view.pt b/docs/tutorials/wiki2/src/tests/tutorial/templates/view.pt deleted file mode 100644 index 4e5772de0..000000000 --- a/docs/tutorials/wiki2/src/tests/tutorial/templates/view.pt +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - ${page.name} - Pyramid tutorial wiki (based on - TurboGears 20-Minute Wiki) - - - - - - - - - - - - -
-
-
-
- -
-
-
-
- Page text goes here. -
-

- - Edit this page - -

-

- Viewing - Page Name Goes Here -

-

You can return to the - FrontPage. -

-

- - Logout - -

-
-
-
-
- -
-
-
- - - - - - - - diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests.py b/docs/tutorials/wiki2/src/tests/tutorial/tests.py deleted file mode 100644 index c50e05b6d..000000000 --- a/docs/tutorials/wiki2/src/tests/tutorial/tests.py +++ /dev/null @@ -1,235 +0,0 @@ -import unittest -import transaction - -from pyramid import testing - - -def _initTestingDB(): - from sqlalchemy import create_engine - from tutorial.models import ( - DBSession, - Page, - Base - ) - engine = create_engine('sqlite://') - Base.metadata.create_all(engine) - DBSession.configure(bind=engine) - with transaction.manager: - model = Page(name='FrontPage', data='This is the front page') - DBSession.add(model) - return DBSession - - -def _registerRoutes(config): - config.add_route('view_page', '{pagename}') - config.add_route('edit_page', '{pagename}/edit_page') - config.add_route('add_page', 'add_page/{pagename}') - - -class ViewWikiTests(unittest.TestCase): - def setUp(self): - self.config = testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _callFUT(self, request): - from tutorial.views import view_wiki - return view_wiki(request) - - def test_it(self): - _registerRoutes(self.config) - request = testing.DummyRequest() - response = self._callFUT(request) - self.assertEqual(response.location, 'http://example.com/FrontPage') - - -class ViewPageTests(unittest.TestCase): - def setUp(self): - self.session = _initTestingDB() - self.config = testing.setUp() - - def tearDown(self): - self.session.remove() - testing.tearDown() - - def _callFUT(self, request): - from tutorial.views import view_page - return view_page(request) - - def test_it(self): - from tutorial.models import Page - request = testing.DummyRequest() - request.matchdict['pagename'] = 'IDoExist' - page = Page(name='IDoExist', data='Hello CruelWorld IDoExist') - self.session.add(page) - _registerRoutes(self.config) - info = self._callFUT(request) - self.assertEqual(info['page'], page) - self.assertEqual( - info['content'], - '
\n' - '

Hello ' - 'CruelWorld ' - '' - 'IDoExist' - '

\n
\n') - self.assertEqual(info['edit_url'], - 'http://example.com/IDoExist/edit_page') - - -class AddPageTests(unittest.TestCase): - def setUp(self): - self.session = _initTestingDB() - self.config = testing.setUp() - - def tearDown(self): - self.session.remove() - testing.tearDown() - - def _callFUT(self, request): - from tutorial.views import add_page - return add_page(request) - - def test_it_notsubmitted(self): - _registerRoutes(self.config) - request = testing.DummyRequest() - request.matchdict = {'pagename':'AnotherPage'} - info = self._callFUT(request) - self.assertEqual(info['page'].data,'') - self.assertEqual(info['save_url'], - 'http://example.com/add_page/AnotherPage') - - def test_it_submitted(self): - from tutorial.models import Page - _registerRoutes(self.config) - request = testing.DummyRequest({'form.submitted':True, - 'body':'Hello yo!'}) - request.matchdict = {'pagename':'AnotherPage'} - self._callFUT(request) - page = self.session.query(Page).filter_by(name='AnotherPage').one() - self.assertEqual(page.data, 'Hello yo!') - - -class EditPageTests(unittest.TestCase): - def setUp(self): - self.session = _initTestingDB() - self.config = testing.setUp() - - def tearDown(self): - self.session.remove() - testing.tearDown() - - def _callFUT(self, request): - from tutorial.views import edit_page - return edit_page(request) - - def test_it_notsubmitted(self): - from tutorial.models import Page - _registerRoutes(self.config) - request = testing.DummyRequest() - request.matchdict = {'pagename':'abc'} - page = Page(name='abc', data='hello') - self.session.add(page) - info = self._callFUT(request) - self.assertEqual(info['page'], page) - self.assertEqual(info['save_url'], - 'http://example.com/abc/edit_page') - - def test_it_submitted(self): - from tutorial.models import Page - _registerRoutes(self.config) - request = testing.DummyRequest({'form.submitted':True, - 'body':'Hello yo!'}) - request.matchdict = {'pagename':'abc'} - page = Page(name='abc', data='hello') - self.session.add(page) - response = self._callFUT(request) - self.assertEqual(response.location, 'http://example.com/abc') - self.assertEqual(page.data, 'Hello yo!') - - -class FunctionalTests(unittest.TestCase): - - viewer_login = '/login?login=viewer&password=viewer' \ - '&came_from=FrontPage&form.submitted=Login' - viewer_wrong_login = '/login?login=viewer&password=incorrect' \ - '&came_from=FrontPage&form.submitted=Login' - editor_login = '/login?login=editor&password=editor' \ - '&came_from=FrontPage&form.submitted=Login' - - def setUp(self): - from tutorial import main - settings = { 'sqlalchemy.url': 'sqlite://'} - app = main({}, **settings) - from webtest import TestApp - self.testapp = TestApp(app) - _initTestingDB() - - def tearDown(self): - del self.testapp - from tutorial.models import DBSession - DBSession.remove() - - def test_root(self): - res = self.testapp.get('/', status=302) - self.assertEqual(res.location, 'http://localhost/FrontPage') - - def test_FrontPage(self): - res = self.testapp.get('/FrontPage', status=200) - self.assertTrue(b'FrontPage' in res.body) - - def test_unexisting_page(self): - self.testapp.get('/SomePage', status=404) - - def test_successful_log_in(self): - res = self.testapp.get(self.viewer_login, status=302) - self.assertEqual(res.location, 'http://localhost/FrontPage') - - def test_failed_log_in(self): - res = self.testapp.get(self.viewer_wrong_login, status=200) - self.assertTrue(b'login' in res.body) - - def test_logout_link_present_when_logged_in(self): - self.testapp.get(self.viewer_login, status=302) - res = self.testapp.get('/FrontPage', status=200) - self.assertTrue(b'Logout' in res.body) - - def test_logout_link_not_present_after_logged_out(self): - self.testapp.get(self.viewer_login, status=302) - self.testapp.get('/FrontPage', status=200) - res = self.testapp.get('/logout', status=302) - self.assertTrue(b'Logout' not in res.body) - - def test_anonymous_user_cannot_edit(self): - res = self.testapp.get('/FrontPage/edit_page', status=200) - self.assertTrue(b'Login' in res.body) - - def test_anonymous_user_cannot_add(self): - res = self.testapp.get('/add_page/NewPage', status=200) - self.assertTrue(b'Login' in res.body) - - def test_viewer_user_cannot_edit(self): - self.testapp.get(self.viewer_login, status=302) - res = self.testapp.get('/FrontPage/edit_page', status=200) - self.assertTrue(b'Login' in res.body) - - def test_viewer_user_cannot_add(self): - self.testapp.get(self.viewer_login, status=302) - res = self.testapp.get('/add_page/NewPage', status=200) - self.assertTrue(b'Login' in res.body) - - def test_editors_member_user_can_edit(self): - self.testapp.get(self.editor_login, status=302) - res = self.testapp.get('/FrontPage/edit_page', status=200) - self.assertTrue(b'Editing' in res.body) - - def test_editors_member_user_can_add(self): - self.testapp.get(self.editor_login, status=302) - res = self.testapp.get('/add_page/NewPage', status=200) - self.assertTrue(b'Editing' in res.body) - - def test_editors_member_user_can_view(self): - self.testapp.get(self.editor_login, status=302) - res = self.testapp.get('/FrontPage', status=200) - self.assertTrue(b'FrontPage' in res.body) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/__init__.py @@ -0,0 +1 @@ + diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py new file mode 100644 index 000000000..339c60bc2 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py @@ -0,0 +1,141 @@ +import unittest + +from pyramid import testing + + +def dummy_request(dbsession): + return testing.DummyRequest(dbsession=dbsession) + + +def _register_routes(config): + config.add_route('view_page', '{pagename}') + config.add_route('edit_page', '{pagename}/edit_page') + config.add_route('add_page', 'add_page/{pagename}') + + +class FunctionalTests(unittest.TestCase): + + viewer_login = '/login?login=viewer&password=viewer' \ + '&came_from=FrontPage&form.submitted=Login' + viewer_wrong_login = '/login?login=viewer&password=incorrect' \ + '&came_from=FrontPage&form.submitted=Login' + editor_login = '/login?login=editor&password=editor' \ + '&came_from=FrontPage&form.submitted=Login' + + engine = None + + @staticmethod + def setup_database(): + import transaction + from tutorial.models.mymodel import Page + from tutorial.models.meta import ( + Base, + ) + import tutorial.models.meta + + + def initialize_db(dbsession, engine): + Base.metadata.create_all(engine) + with transaction.manager: + model = Page(name='FrontPage', data='This is the front page') + dbsession.add(model) + + def wrap_get_session(transaction_manager, dbmaker): + dbsession = get_session(transaction_manager, dbmaker) + initialize_db(dbsession, engine) + tutorial.models.meta.get_session = get_session + tutorial.models.meta.get_engine = get_engine + return dbsession + + def wrap_get_engine(settings): + global engine + engine = get_engine(settings) + return engine + + get_session = tutorial.models.meta.get_session + tutorial.models.meta.get_session = wrap_get_session + + get_engine = tutorial.models.meta.get_engine + tutorial.models.meta.get_engine = wrap_get_engine + + @classmethod + def setUpClass(cls): + cls.setup_database() + + from webtest import TestApp + from tutorial import main + settings = {'sqlalchemy.url': 'sqlite://'} + app = main({}, **settings) + cls.testapp = TestApp(app) + + @classmethod + def tearDownClass(cls): + from tutorial.models.meta import Base + Base.metadata.drop_all(engine) + + def tearDown(self): + import transaction + transaction.abort() + + def test_root(self): + res = self.testapp.get('/', status=302) + self.assertEqual(res.location, 'http://localhost/FrontPage') + + def test_FrontPage(self): + res = self.testapp.get('/FrontPage', status=200) + self.assertTrue(b'FrontPage' in res.body) + + def test_unexisting_page(self): + self.testapp.get('/SomePage', status=404) + + def test_successful_log_in(self): + res = self.testapp.get(self.viewer_login, status=302) + self.assertEqual(res.location, 'http://localhost/FrontPage') + + def test_failed_log_in(self): + res = self.testapp.get(self.viewer_wrong_login, status=200) + self.assertTrue(b'login' in res.body) + + def test_logout_link_present_when_logged_in(self): + self.testapp.get(self.viewer_login, status=302) + res = self.testapp.get('/FrontPage', status=200) + self.assertTrue(b'Logout' in res.body) + + def test_logout_link_not_present_after_logged_out(self): + self.testapp.get(self.viewer_login, status=302) + self.testapp.get('/FrontPage', status=200) + res = self.testapp.get('/logout', status=302) + self.assertTrue(b'Logout' not in res.body) + + def test_anonymous_user_cannot_edit(self): + res = self.testapp.get('/FrontPage/edit_page', status=200) + self.assertTrue(b'Login' in res.body) + + def test_anonymous_user_cannot_add(self): + res = self.testapp.get('/add_page/NewPage', status=200) + self.assertTrue(b'Login' in res.body) + + def test_viewer_user_cannot_edit(self): + self.testapp.get(self.viewer_login, status=302) + res = self.testapp.get('/FrontPage/edit_page', status=200) + self.assertTrue(b'Login' in res.body) + + def test_viewer_user_cannot_add(self): + self.testapp.get(self.viewer_login, status=302) + res = self.testapp.get('/add_page/NewPage', status=200) + self.assertTrue(b'Login' in res.body) + + def test_editors_member_user_can_edit(self): + self.testapp.get(self.editor_login, status=302) + res = self.testapp.get('/FrontPage/edit_page', status=200) + self.assertTrue(b'Editing' in res.body) + + def test_editors_member_user_can_add(self): + self.testapp.get(self.editor_login, status=302) + res = self.testapp.get('/add_page/NewPage', status=200) + self.assertTrue(b'Editing' in res.body) + + def test_editors_member_user_can_view(self): + self.testapp.get(self.editor_login, status=302) + res = self.testapp.get('/FrontPage', status=200) + self.assertTrue(b'FrontPage' in res.body) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py new file mode 100644 index 000000000..d70311e38 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py @@ -0,0 +1,168 @@ +import unittest +import transaction + +from pyramid import testing + + +def dummy_request(dbsession): + return testing.DummyRequest(dbsession=dbsession) + + +def _register_routes(config): + config.add_route('view_page', '{pagename}') + config.add_route('edit_page', '{pagename}/edit_page') + config.add_route('add_page', 'add_page/{pagename}') + + +class BaseTest(unittest.TestCase): + def setUp(self): + self.config = testing.setUp(settings={ + 'sqlalchemy.url': 'sqlite:///:memory:' + }) + self.config.include('..models.meta') + _register_routes(self.config) + settings = self.config.get_settings() + + from ..models.meta import ( + get_session, + get_engine, + get_dbmaker, + ) + + self.engine = get_engine(settings) + dbmaker = get_dbmaker(self.engine) + + self.session = get_session(transaction.manager, dbmaker) + + self.init_database() + + def init_database(self): + from ..models.meta import Base + Base.metadata.create_all(self.engine) + + def tearDown(self): + testing.tearDown() + transaction.abort() + + +class ViewWikiTests(unittest.TestCase): + + def setUp(self): + self.config = testing.setUp() + _register_routes(self.config) + + def tearDown(self): + testing.tearDown() + + def _callFUT(self, request): + from tutorial.views.default import view_wiki + return view_wiki(request) + + def test_it(self): + request = testing.DummyRequest() + response = self._callFUT(request) + self.assertEqual(response.location, 'http://example.com/FrontPage') + + +class ViewPageTests(BaseTest): + def setUp(self): + super(ViewPageTests, self).setUp() + + def tearDown(self): + transaction.abort() + testing.tearDown() + + def _callFUT(self, request): + from tutorial.views.default import view_page + return view_page(request) + + def test_it(self): + # add a page to the db + from ..models.mymodel import Page + page = Page(name='IDoExist', data='Hello CruelWorld IDoExist') + self.session.add(page) + + # create a request asking for the page we've created + request = dummy_request(self.session) + request.matchdict['pagename'] = 'IDoExist' + + # call the view we're testing and check its behavior + info = self._callFUT(request) + self.assertEqual(info['page'], page) + self.assertEqual( + info['content'], + '
\n' + '

Hello ' + 'CruelWorld ' + '' + 'IDoExist' + '

\n
\n') + self.assertEqual(info['edit_url'], + 'http://example.com/IDoExist/edit_page') + + +class AddPageTests(BaseTest): + def setUp(self): + super(AddPageTests, self).setUp() + + def tearDown(self): + transaction.abort() + testing.tearDown() + + def _callFUT(self, request): + from tutorial.views.default import add_page + return add_page(request) + + def test_it_notsubmitted(self): + request = dummy_request(self.session) + request.matchdict = {'pagename': 'AnotherPage'} + info = self._callFUT(request) + self.assertEqual(info['page'].data, '') + self.assertEqual(info['save_url'], + 'http://example.com/add_page/AnotherPage') + + def test_it_submitted(self): + from ..models.mymodel import Page + request = testing.DummyRequest({'form.submitted': True, + 'body': 'Hello yo!'}, + dbsession=self.session) + request.matchdict = {'pagename': 'AnotherPage'} + self._callFUT(request) + page = self.session.query(Page).filter_by(name='AnotherPage').one() + self.assertEqual(page.data, 'Hello yo!') + + +class EditPageTests(BaseTest): + def setUp(self): + super(EditPageTests, self).setUp() + + def tearDown(self): + transaction.abort() + testing.tearDown() + + def _callFUT(self, request): + from tutorial.views.default import edit_page + return edit_page(request) + + def test_it_notsubmitted(self): + from ..models.mymodel import Page + request = dummy_request(self.session) + request.matchdict = {'pagename': 'abc'} + page = Page(name='abc', data='hello') + self.session.add(page) + info = self._callFUT(request) + self.assertEqual(info['page'], page) + self.assertEqual(info['save_url'], + 'http://example.com/abc/edit_page') + + def test_it_submitted(self): + from ..models.mymodel import Page + request = testing.DummyRequest({'form.submitted': True, + 'body': 'Hello yo!'}, + dbsession=self.session) + request.matchdict = {'pagename': 'abc'} + page = Page(name='abc', data='hello') + self.session.add(page) + response = self._callFUT(request) + self.assertEqual(response.location, 'http://example.com/abc') + self.assertEqual(page.data, 'Hello yo!') diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views.py b/docs/tutorials/wiki2/src/tests/tutorial/views.py deleted file mode 100644 index 41bea4785..000000000 --- a/docs/tutorials/wiki2/src/tests/tutorial/views.py +++ /dev/null @@ -1,123 +0,0 @@ -import re -from docutils.core import publish_parts - -from pyramid.httpexceptions import ( - HTTPFound, - HTTPNotFound, - ) - -from pyramid.view import ( - view_config, - forbidden_view_config, - ) - -from pyramid.security import ( - remember, - forget, - ) - -from .security import USERS - -from .models import ( - DBSession, - Page, - ) - - -# regular expression used to find WikiWords -wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") - -@view_config(route_name='view_wiki', - permission='view') -def view_wiki(request): - return HTTPFound(location = request.route_url('view_page', - pagename='FrontPage')) - -@view_config(route_name='view_page', renderer='templates/view.pt', - permission='view') -def view_page(request): - pagename = request.matchdict['pagename'] - page = DBSession.query(Page).filter_by(name=pagename).first() - if page is None: - return HTTPNotFound('No such page') - - def check(match): - word = match.group(1) - exists = DBSession.query(Page).filter_by(name=word).all() - if exists: - view_url = request.route_url('view_page', pagename=word) - return '%s' % (view_url, word) - else: - add_url = request.route_url('add_page', pagename=word) - return '%s' % (add_url, word) - - content = publish_parts(page.data, writer_name='html')['html_body'] - content = wikiwords.sub(check, content) - edit_url = request.route_url('edit_page', pagename=pagename) - return dict(page=page, content=content, edit_url=edit_url, - logged_in=request.authenticated_userid) - -@view_config(route_name='add_page', renderer='templates/edit.pt', - permission='edit') -def add_page(request): - pagename = request.matchdict['pagename'] - if 'form.submitted' in request.params: - body = request.params['body'] - page = Page(name=pagename, data=body) - DBSession.add(page) - return HTTPFound(location = request.route_url('view_page', - pagename=pagename)) - save_url = request.route_url('add_page', pagename=pagename) - page = Page(name='', data='') - return dict(page=page, save_url=save_url, - logged_in=request.authenticated_userid) - -@view_config(route_name='edit_page', renderer='templates/edit.pt', - permission='edit') -def edit_page(request): - pagename = request.matchdict['pagename'] - page = DBSession.query(Page).filter_by(name=pagename).one() - if 'form.submitted' in request.params: - page.data = request.params['body'] - DBSession.add(page) - return HTTPFound(location = request.route_url('view_page', - pagename=pagename)) - return dict( - page=page, - save_url=request.route_url('edit_page', pagename=pagename), - logged_in=request.authenticated_userid - ) - -@view_config(route_name='login', renderer='templates/login.pt') -@forbidden_view_config(renderer='templates/login.pt') -def login(request): - login_url = request.route_url('login') - referrer = request.url - if referrer == login_url: - referrer = '/' # never use the login form itself as came_from - came_from = request.params.get('came_from', referrer) - message = '' - login = '' - password = '' - if 'form.submitted' in request.params: - login = request.params['login'] - password = request.params['password'] - if USERS.get(login) == password: - headers = remember(request, login) - return HTTPFound(location = came_from, - headers = headers) - message = 'Failed login' - - return dict( - message = message, - url = request.application_url + '/login', - came_from = came_from, - login = login, - password = password, - ) - -@view_config(route_name='logout') -def logout(request): - headers = forget(request) - return HTTPFound(location = request.route_url('view_wiki'), - headers = headers) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/views/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py new file mode 100644 index 000000000..f35f041a4 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py @@ -0,0 +1,120 @@ +import cgi +import re +from docutils.core import publish_parts + +from pyramid.httpexceptions import ( + HTTPFound, + HTTPNotFound, + ) + +from pyramid.view import ( + view_config, + forbidden_view_config, + ) + +from pyramid.security import ( + remember, + forget, + ) + +from ..security.default import USERS + +from ..models.mymodel import Page + +# regular expression used to find WikiWords +wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") + +@view_config(route_name='view_wiki', + permission='view') +def view_wiki(request): + return HTTPFound(location=request.route_url('view_page', + pagename='FrontPage')) + +@view_config(route_name='view_page', renderer='templates/view.jinja2', + permission='view') +def view_page(request): + pagename = request.matchdict['pagename'] + page = request.dbsession.query(Page).filter_by(name=pagename).first() + if page is None: + return HTTPNotFound('No such page') + + def check(match): + word = match.group(1) + exists = request.dbsession.query(Page).filter_by(name=word).all() + if exists: + view_url = request.route_url('view_page', pagename=word) + return '%s' % (view_url, cgi.escape(word)) + else: + add_url = request.route_url('add_page', pagename=word) + return '%s' % (add_url, cgi.escape(word)) + + content = publish_parts(page.data, writer_name='html')['html_body'] + content = wikiwords.sub(check, content) + edit_url = request.route_url('edit_page', pagename=pagename) + return dict(page=page, content=content, edit_url=edit_url, + logged_in=request.authenticated_userid) + +@view_config(route_name='add_page', renderer='templates/edit.jinja2', + permission='edit') +def add_page(request): + pagename = request.matchdict['pagename'] + if 'form.submitted' in request.params: + body = request.params['body'] + page = Page(name=pagename, data=body) + request.dbsession.add(page) + return HTTPFound(location = request.route_url('view_page', + pagename=pagename)) + save_url = request.route_url('add_page', pagename=pagename) + page = Page(name='', data='') + return dict(page=page, save_url=save_url, + logged_in=request.authenticated_userid) + +@view_config(route_name='edit_page', renderer='templates/edit.jinja2', + permission='edit') +def edit_page(request): + pagename = request.matchdict['pagename'] + page = request.dbsession.query(Page).filter_by(name=pagename).one() + if 'form.submitted' in request.params: + page.data = request.params['body'] + request.dbsession.add(page) + return HTTPFound(location = request.route_url('view_page', + pagename=pagename)) + return dict( + page=page, + save_url = request.route_url('edit_page', pagename=pagename), + logged_in=request.authenticated_userid + ) + +@view_config(route_name='login', renderer='templates/login.jinja2') +@forbidden_view_config(renderer='templates/login.jinja2') +def login(request): + login_url = request.route_url('login') + referrer = request.url + if referrer == login_url: + referrer = '/' # never use the login form itself as came_from + came_from = request.params.get('came_from', referrer) + message = '' + login = '' + password = '' + if 'form.submitted' in request.params: + login = request.params['login'] + password = request.params['password'] + if USERS.get(login) == password: + headers = remember(request, login) + return HTTPFound(location = came_from, + headers = headers) + message = 'Failed login' + + return dict( + message = message, + url = request.application_url + '/login', + came_from = came_from, + login = login, + password = password, + ) + +@view_config(route_name='logout') +def logout(request): + headers = forget(request) + return HTTPFound(location = request.route_url('view_wiki'), + headers = headers) -- cgit v1.2.3 From 022a6e0f05b72c679aada6b3c9313c4fd5b31b80 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Wed, 2 Dec 2015 04:30:47 -0800 Subject: - add comment to NAMING_CONVENTION per 9b12c01168cb756ec36351d7414cad95e87f6581 --- docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py | 3 +++ docs/tutorials/wiki2/src/basiclayout/tutorial/models/meta.py | 3 +++ docs/tutorials/wiki2/src/models/tutorial/models/meta.py | 3 +++ docs/tutorials/wiki2/src/tests/tutorial/models/meta.py | 3 +++ docs/tutorials/wiki2/src/views/tutorial/models/meta.py | 3 +++ 5 files changed, 15 insertions(+) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py index b72b45f9f..80ececd8c 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py @@ -4,6 +4,9 @@ from sqlalchemy.orm import sessionmaker from sqlalchemy.schema import MetaData import zope.sqlalchemy +# Recommended naming convention used by Alembic, as various different database +# providers will autogenerate vastly different names making migrations more +# difficult. See: http://alembic.readthedocs.org/en/latest/naming.html NAMING_CONVENTION = { "ix": 'ix_%(column_0_label)s', "uq": "uq_%(table_name)s_%(column_0_name)s", diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/meta.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/meta.py index b72b45f9f..80ececd8c 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/meta.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/meta.py @@ -4,6 +4,9 @@ from sqlalchemy.orm import sessionmaker from sqlalchemy.schema import MetaData import zope.sqlalchemy +# Recommended naming convention used by Alembic, as various different database +# providers will autogenerate vastly different names making migrations more +# difficult. See: http://alembic.readthedocs.org/en/latest/naming.html NAMING_CONVENTION = { "ix": 'ix_%(column_0_label)s', "uq": "uq_%(table_name)s_%(column_0_name)s", diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/meta.py b/docs/tutorials/wiki2/src/models/tutorial/models/meta.py index b72b45f9f..80ececd8c 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/models/meta.py +++ b/docs/tutorials/wiki2/src/models/tutorial/models/meta.py @@ -4,6 +4,9 @@ from sqlalchemy.orm import sessionmaker from sqlalchemy.schema import MetaData import zope.sqlalchemy +# Recommended naming convention used by Alembic, as various different database +# providers will autogenerate vastly different names making migrations more +# difficult. See: http://alembic.readthedocs.org/en/latest/naming.html NAMING_CONVENTION = { "ix": 'ix_%(column_0_label)s', "uq": "uq_%(table_name)s_%(column_0_name)s", diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models/meta.py b/docs/tutorials/wiki2/src/tests/tutorial/models/meta.py index b72b45f9f..80ececd8c 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/models/meta.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/models/meta.py @@ -4,6 +4,9 @@ from sqlalchemy.orm import sessionmaker from sqlalchemy.schema import MetaData import zope.sqlalchemy +# Recommended naming convention used by Alembic, as various different database +# providers will autogenerate vastly different names making migrations more +# difficult. See: http://alembic.readthedocs.org/en/latest/naming.html NAMING_CONVENTION = { "ix": 'ix_%(column_0_label)s', "uq": "uq_%(table_name)s_%(column_0_name)s", diff --git a/docs/tutorials/wiki2/src/views/tutorial/models/meta.py b/docs/tutorials/wiki2/src/views/tutorial/models/meta.py index b72b45f9f..80ececd8c 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/models/meta.py +++ b/docs/tutorials/wiki2/src/views/tutorial/models/meta.py @@ -4,6 +4,9 @@ from sqlalchemy.orm import sessionmaker from sqlalchemy.schema import MetaData import zope.sqlalchemy +# Recommended naming convention used by Alembic, as various different database +# providers will autogenerate vastly different names making migrations more +# difficult. See: http://alembic.readthedocs.org/en/latest/naming.html NAMING_CONVENTION = { "ix": 'ix_%(column_0_label)s', "uq": "uq_%(table_name)s_%(column_0_name)s", -- cgit v1.2.3 From 35dc507d9394a7dc0834838a5a596f4e47ab95fb Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 4 Feb 2016 23:38:43 -0600 Subject: update source for basiclayout --- docs/tutorials/wiki2/src/basiclayout/MANIFEST.in | 2 +- .../tutorials/wiki2/src/basiclayout/production.ini | 2 - .../wiki2/src/basiclayout/tutorial/__init__.py | 2 +- .../src/basiclayout/tutorial/models/__init__.py | 71 +++++++++++++++++++++- .../wiki2/src/basiclayout/tutorial/models/meta.py | 33 ---------- .../src/basiclayout/tutorial/models/mymodel.py | 3 +- .../basiclayout/tutorial/scripts/initializedb.py | 16 ++--- .../src/basiclayout/tutorial/templates/404.jinja2 | 8 +++ .../wiki2/src/basiclayout/tutorial/tests.py | 18 +++--- .../src/basiclayout/tutorial/views/default.py | 4 +- .../wiki2/src/basiclayout/tutorial/views/errors.py | 5 ++ 11 files changed, 104 insertions(+), 60 deletions(-) create mode 100644 docs/tutorials/wiki2/src/basiclayout/tutorial/templates/404.jinja2 create mode 100644 docs/tutorials/wiki2/src/basiclayout/tutorial/views/errors.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/basiclayout/MANIFEST.in b/docs/tutorials/wiki2/src/basiclayout/MANIFEST.in index 81beba1b1..42cd299b5 100644 --- a/docs/tutorials/wiki2/src/basiclayout/MANIFEST.in +++ b/docs/tutorials/wiki2/src/basiclayout/MANIFEST.in @@ -1,2 +1,2 @@ include *.txt *.ini *.cfg *.rst -recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml +recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.jinja2 *.pt *.txt *.mak *.mako *.js *.html *.xml diff --git a/docs/tutorials/wiki2/src/basiclayout/production.ini b/docs/tutorials/wiki2/src/basiclayout/production.ini index 97acfbd7d..cb1db3211 100644 --- a/docs/tutorials/wiki2/src/basiclayout/production.ini +++ b/docs/tutorials/wiki2/src/basiclayout/production.ini @@ -11,8 +11,6 @@ pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.debug_routematch = false pyramid.default_locale_name = en -pyramid.includes = - pyramid_tm sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py index 7994bbfa8..17763812a 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py @@ -6,7 +6,7 @@ def main(global_config, **settings): """ config = Configurator(settings=settings) config.include('pyramid_jinja2') - config.include('.models.meta') + config.include('.models') config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('home', '/') config.scan() diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py index 6ffc10a78..a4026fcd6 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py @@ -1,7 +1,72 @@ +from sqlalchemy import engine_from_config +from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import configure_mappers -# import all models classes here for sqlalchemy mappers -# to pick up +import zope.sqlalchemy + +# import or define all models here to ensure they are attached to the +# Base.metadata prior to any initialization routines from .mymodel import MyModel # flake8: noqa -# run configure mappers to ensure we avoid any race conditions +# run configure_mappers after defining all of the models to ensure +# all relationships can be setup configure_mappers() + + +def get_engine(settings, prefix='sqlalchemy.'): + return engine_from_config(settings, prefix) + + +def get_session_factory(engine): + factory = sessionmaker() + factory.configure(bind=engine) + return factory + + +def get_tm_session(session_factory, transaction_manager): + """ + Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. + + This function will hook the session to the transaction manager which + will take care of committing any changes. + + - When using pyramid_tm it will automatically be committed or aborted + depending on whether an exception is raised. + + - When using scripts you should wrap the session in a manager yourself. + For example:: + + import transaction + + engine = get_engine(settings) + session_factory = get_session_factory(engine) + with transaction.manager: + dbsession = get_tm_session(session_factory, transaction.manager) + + """ + dbsession = session_factory() + zope.sqlalchemy.register( + dbsession, transaction_manager=transaction_manager) + return dbsession + + +def includeme(config): + """ + Initialize the model for a Pyramid app. + + Activate this setup using ``config.include('tutorial.models')``. + + """ + settings = config.get_settings() + + # use pyramid_tm to hook the transaction lifecycle to the request + config.include('pyramid_tm') + + session_factory = get_session_factory(get_engine(settings)) + + # make request.dbsession available for use in Pyramid + config.add_request_method( + # r.tm is the transaction manager used by pyramid_tm + lambda r: get_tm_session(session_factory, r.tm), + 'dbsession', + reify=True + ) diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/meta.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/meta.py index 80ececd8c..fc3e8f1dd 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/meta.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/meta.py @@ -1,8 +1,5 @@ -from sqlalchemy import engine_from_config from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker from sqlalchemy.schema import MetaData -import zope.sqlalchemy # Recommended naming convention used by Alembic, as various different database # providers will autogenerate vastly different names making migrations more @@ -17,33 +14,3 @@ NAMING_CONVENTION = { metadata = MetaData(naming_convention=NAMING_CONVENTION) Base = declarative_base(metadata=metadata) - - -def includeme(config): - settings = config.get_settings() - dbmaker = get_dbmaker(get_engine(settings)) - - config.add_request_method( - lambda r: get_session(r.tm, dbmaker), - 'dbsession', - reify=True - ) - - config.include('pyramid_tm') - - -def get_session(transaction_manager, dbmaker): - dbsession = dbmaker() - zope.sqlalchemy.register(dbsession, - transaction_manager=transaction_manager) - return dbsession - - -def get_engine(settings, prefix='sqlalchemy.'): - return engine_from_config(settings, prefix) - - -def get_dbmaker(engine): - dbmaker = sessionmaker() - dbmaker.configure(bind=engine) - return dbmaker diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/mymodel.py index 5a2b5890c..d65a01a42 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/mymodel.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/mymodel.py @@ -1,4 +1,3 @@ -from .meta import Base from sqlalchemy import ( Column, Index, @@ -6,6 +5,8 @@ from sqlalchemy import ( Text, ) +from .meta import Base + class MyModel(Base): __tablename__ = 'models' diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initializedb.py index f0d09729e..da63c180a 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initializedb.py @@ -9,13 +9,13 @@ from pyramid.paster import ( from pyramid.scripts.common import parse_vars -from ..models.meta import ( +from ..models import ( Base, - get_session, get_engine, - get_dbmaker, + get_session_factory, + get_tm_session, ) -from ..models.mymodel import MyModel +from ..models import MyModel def usage(argv): @@ -34,12 +34,12 @@ def main(argv=sys.argv): settings = get_appsettings(config_uri, options=options) engine = get_engine(settings) - dbmaker = get_dbmaker(engine) - - dbsession = get_session(transaction.manager, dbmaker) - 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/404.jinja2 b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/404.jinja2 new file mode 100644 index 000000000..1917f83c7 --- /dev/null +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/404.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+

Pyramid Alchemy scaffold

+

404 Page Not Found

+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py index b947e3bb1..c54945c28 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/tests.py @@ -13,22 +13,22 @@ class BaseTest(unittest.TestCase): self.config = testing.setUp(settings={ 'sqlalchemy.url': 'sqlite:///:memory:' }) - self.config.include('.models.meta') + self.config.include('.models') settings = self.config.get_settings() - from .models.meta import ( - get_session, + from .models import ( get_engine, - get_dbmaker, + get_session_factory, + get_tm_session, ) self.engine = get_engine(settings) - dbmaker = get_dbmaker(self.engine) + session_factory = get_session_factory(self.engine) - self.session = get_session(transaction.manager, dbmaker) + self.session = get_tm_session(session_factory, transaction.manager) def init_database(self): - from .models.meta import Base + from .models import Base Base.metadata.create_all(self.engine) def tearDown(self): @@ -36,7 +36,7 @@ class BaseTest(unittest.TestCase): testing.tearDown() transaction.abort() - Base.metadata.create_all(self.engine) + Base.metadata.drop_all(self.engine) class TestMyViewSuccessCondition(BaseTest): @@ -45,7 +45,7 @@ class TestMyViewSuccessCondition(BaseTest): super(TestMyViewSuccessCondition, self).setUp() self.init_database() - from .models.mymodel import MyModel + from .models import MyModel model = MyModel(name='one', value=55) self.session.add(model) diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/views/default.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/views/default.py index 13ad8793c..ad0c728d7 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/views/default.py @@ -3,7 +3,7 @@ from pyramid.view import view_config from sqlalchemy.exc import DBAPIError -from ..models.mymodel import MyModel +from ..models import MyModel @view_config(route_name='home', renderer='../templates/mytemplate.jinja2') @@ -12,7 +12,7 @@ def my_view(request): query = request.dbsession.query(MyModel) one = query.filter(MyModel.name == 'one').first() except DBAPIError: - return Response(db_err_msg, content_type='text/plain', status_int=500) + return Response(db_err_msg, content_type='text/plain', status=500) return {'one': one, 'project': 'tutorial'} diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/views/errors.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/views/errors.py new file mode 100644 index 000000000..a4b8201f1 --- /dev/null +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/views/errors.py @@ -0,0 +1,5 @@ +from pyramid.view import notfound_view_config + +@notfound_view_config(renderer='../templates/404.jinja2') +def notfound_view(request): + return {} -- cgit v1.2.3 From 9b83981f85c1d822ecf5a9aa2a4fde1851c0d0cd Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 4 Feb 2016 23:50:00 -0600 Subject: fix the Base import --- docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initializedb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initializedb.py index da63c180a..7307ecc5c 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/scripts/initializedb.py @@ -9,8 +9,8 @@ from pyramid.paster import ( from pyramid.scripts.common import parse_vars +from ..models.meta import Base from ..models import ( - Base, get_engine, get_session_factory, get_tm_session, -- cgit v1.2.3 From d6243ac1e7724cce26a738de5b86f187ef444e77 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 7 Feb 2016 15:29:17 -0600 Subject: update definingmodels chapter of wiki2 tutorial --- docs/tutorials/wiki2/src/models/MANIFEST.in | 2 +- docs/tutorials/wiki2/src/models/production.ini | 2 - .../wiki2/src/models/tutorial/__init__.py | 2 +- .../wiki2/src/models/tutorial/models/__init__.py | 71 +++++++++++++++++++++- .../wiki2/src/models/tutorial/models/meta.py | 33 ---------- .../wiki2/src/models/tutorial/models/mymodel.py | 3 +- .../src/models/tutorial/scripts/initializedb.py | 27 ++++---- .../wiki2/src/models/tutorial/templates/404.jinja2 | 8 +++ docs/tutorials/wiki2/src/models/tutorial/tests.py | 18 +++--- .../wiki2/src/models/tutorial/views/default.py | 4 +- .../wiki2/src/models/tutorial/views/errors.py | 5 ++ 11 files changed, 111 insertions(+), 64 deletions(-) create mode 100644 docs/tutorials/wiki2/src/models/tutorial/templates/404.jinja2 create mode 100644 docs/tutorials/wiki2/src/models/tutorial/views/errors.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/models/MANIFEST.in b/docs/tutorials/wiki2/src/models/MANIFEST.in index 81beba1b1..42cd299b5 100644 --- a/docs/tutorials/wiki2/src/models/MANIFEST.in +++ b/docs/tutorials/wiki2/src/models/MANIFEST.in @@ -1,2 +1,2 @@ include *.txt *.ini *.cfg *.rst -recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml +recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.jinja2 *.pt *.txt *.mak *.mako *.js *.html *.xml diff --git a/docs/tutorials/wiki2/src/models/production.ini b/docs/tutorials/wiki2/src/models/production.ini index 97acfbd7d..cb1db3211 100644 --- a/docs/tutorials/wiki2/src/models/production.ini +++ b/docs/tutorials/wiki2/src/models/production.ini @@ -11,8 +11,6 @@ pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.debug_routematch = false pyramid.default_locale_name = en -pyramid.includes = - pyramid_tm sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite diff --git a/docs/tutorials/wiki2/src/models/tutorial/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/__init__.py index 7994bbfa8..17763812a 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/models/tutorial/__init__.py @@ -6,7 +6,7 @@ def main(global_config, **settings): """ config = Configurator(settings=settings) config.include('pyramid_jinja2') - config.include('.models.meta') + config.include('.models') config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('home', '/') config.scan() diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py index 7b1c62867..4810c357a 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py @@ -1,7 +1,72 @@ +from sqlalchemy import engine_from_config +from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import configure_mappers -# import all models classes here for sqlalchemy mappers -# to pick up +import zope.sqlalchemy + +# import or define all models here to ensure they are attached to the +# Base.metadata prior to any initialization routines from .mymodel import Page # flake8: noqa -# run configure mappers to ensure we avoid any race conditions +# run configure_mappers after defining all of the models to ensure +# all relationships can be setup configure_mappers() + + +def get_engine(settings, prefix='sqlalchemy.'): + return engine_from_config(settings, prefix) + + +def get_session_factory(engine): + factory = sessionmaker() + factory.configure(bind=engine) + return factory + + +def get_tm_session(session_factory, transaction_manager): + """ + Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. + + This function will hook the session to the transaction manager which + will take care of committing any changes. + + - When using pyramid_tm it will automatically be committed or aborted + depending on whether an exception is raised. + + - When using scripts you should wrap the session in a manager yourself. + For example:: + + import transaction + + engine = get_engine(settings) + session_factory = get_session_factory(engine) + with transaction.manager: + dbsession = get_tm_session(session_factory, transaction.manager) + + """ + dbsession = session_factory() + zope.sqlalchemy.register( + dbsession, transaction_manager=transaction_manager) + return dbsession + + +def includeme(config): + """ + Initialize the model for a Pyramid app. + + Activate this setup using ``config.include('tutorial.models')``. + + """ + settings = config.get_settings() + + # use pyramid_tm to hook the transaction lifecycle to the request + config.include('pyramid_tm') + + session_factory = get_session_factory(get_engine(settings)) + + # make request.dbsession available for use in Pyramid + config.add_request_method( + # r.tm is the transaction manager used by pyramid_tm + lambda r: get_tm_session(session_factory, r.tm), + 'dbsession', + reify=True + ) diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/meta.py b/docs/tutorials/wiki2/src/models/tutorial/models/meta.py index 80ececd8c..fc3e8f1dd 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/models/meta.py +++ b/docs/tutorials/wiki2/src/models/tutorial/models/meta.py @@ -1,8 +1,5 @@ -from sqlalchemy import engine_from_config from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker from sqlalchemy.schema import MetaData -import zope.sqlalchemy # Recommended naming convention used by Alembic, as various different database # providers will autogenerate vastly different names making migrations more @@ -17,33 +14,3 @@ NAMING_CONVENTION = { metadata = MetaData(naming_convention=NAMING_CONVENTION) Base = declarative_base(metadata=metadata) - - -def includeme(config): - settings = config.get_settings() - dbmaker = get_dbmaker(get_engine(settings)) - - config.add_request_method( - lambda r: get_session(r.tm, dbmaker), - 'dbsession', - reify=True - ) - - config.include('pyramid_tm') - - -def get_session(transaction_manager, dbmaker): - dbsession = dbmaker() - zope.sqlalchemy.register(dbsession, - transaction_manager=transaction_manager) - return dbsession - - -def get_engine(settings, prefix='sqlalchemy.'): - return engine_from_config(settings, prefix) - - -def get_dbmaker(engine): - dbmaker = sessionmaker() - dbmaker.configure(bind=engine) - return dbmaker diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/models/tutorial/models/mymodel.py index 45571d78e..b23d0c0d2 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/models/mymodel.py +++ b/docs/tutorials/wiki2/src/models/tutorial/models/mymodel.py @@ -1,10 +1,11 @@ -from .meta import Base from sqlalchemy import ( Column, Integer, Text, ) +from .meta import Base + class Page(Base): """ The SQLAlchemy declarative model class for a Page object. """ diff --git a/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py index 4aac4a848..601a6e73f 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py @@ -7,13 +7,15 @@ from pyramid.paster import ( setup_logging, ) -from ..models.meta import ( - Base, - get_session, +from pyramid.scripts.common import parse_vars + +from ..models.meta import Base +from ..models import ( get_engine, - get_dbmaker, + get_session_factory, + get_tm_session, ) -from ..models.mymodel import Page +from ..models import Page def usage(argv): @@ -27,16 +29,17 @@ 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) + settings = get_appsettings(config_uri, options=options) engine = get_engine(settings) - dbmaker = get_dbmaker(engine) - - dbsession = get_session(transaction.manager, dbmaker) - Base.metadata.create_all(engine) + session_factory = get_session_factory(engine) + with transaction.manager: - model = Page(name='FrontPage', data='This is the front page') - dbsession.add(model) + dbsession = get_tm_session(session_factory, transaction.manager) + + page = Page(name='FrontPage', data='This is the front page') + dbsession.add(page) diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/404.jinja2 b/docs/tutorials/wiki2/src/models/tutorial/templates/404.jinja2 new file mode 100644 index 000000000..1917f83c7 --- /dev/null +++ b/docs/tutorials/wiki2/src/models/tutorial/templates/404.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+

Pyramid Alchemy scaffold

+

404 Page Not Found

+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/models/tutorial/tests.py b/docs/tutorials/wiki2/src/models/tutorial/tests.py index b947e3bb1..c54945c28 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/models/tutorial/tests.py @@ -13,22 +13,22 @@ class BaseTest(unittest.TestCase): self.config = testing.setUp(settings={ 'sqlalchemy.url': 'sqlite:///:memory:' }) - self.config.include('.models.meta') + self.config.include('.models') settings = self.config.get_settings() - from .models.meta import ( - get_session, + from .models import ( get_engine, - get_dbmaker, + get_session_factory, + get_tm_session, ) self.engine = get_engine(settings) - dbmaker = get_dbmaker(self.engine) + session_factory = get_session_factory(self.engine) - self.session = get_session(transaction.manager, dbmaker) + self.session = get_tm_session(session_factory, transaction.manager) def init_database(self): - from .models.meta import Base + from .models import Base Base.metadata.create_all(self.engine) def tearDown(self): @@ -36,7 +36,7 @@ class BaseTest(unittest.TestCase): testing.tearDown() transaction.abort() - Base.metadata.create_all(self.engine) + Base.metadata.drop_all(self.engine) class TestMyViewSuccessCondition(BaseTest): @@ -45,7 +45,7 @@ class TestMyViewSuccessCondition(BaseTest): super(TestMyViewSuccessCondition, self).setUp() self.init_database() - from .models.mymodel import MyModel + from .models import MyModel model = MyModel(name='one', value=55) self.session.add(model) diff --git a/docs/tutorials/wiki2/src/models/tutorial/views/default.py b/docs/tutorials/wiki2/src/models/tutorial/views/default.py index 13ad8793c..ad0c728d7 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/models/tutorial/views/default.py @@ -3,7 +3,7 @@ from pyramid.view import view_config from sqlalchemy.exc import DBAPIError -from ..models.mymodel import MyModel +from ..models import MyModel @view_config(route_name='home', renderer='../templates/mytemplate.jinja2') @@ -12,7 +12,7 @@ def my_view(request): query = request.dbsession.query(MyModel) one = query.filter(MyModel.name == 'one').first() except DBAPIError: - return Response(db_err_msg, content_type='text/plain', status_int=500) + return Response(db_err_msg, content_type='text/plain', status=500) return {'one': one, 'project': 'tutorial'} diff --git a/docs/tutorials/wiki2/src/models/tutorial/views/errors.py b/docs/tutorials/wiki2/src/models/tutorial/views/errors.py new file mode 100644 index 000000000..a4b8201f1 --- /dev/null +++ b/docs/tutorials/wiki2/src/models/tutorial/views/errors.py @@ -0,0 +1,5 @@ +from pyramid.view import notfound_view_config + +@notfound_view_config(renderer='../templates/404.jinja2') +def notfound_view(request): + return {} -- cgit v1.2.3 From 4b23c9f1344a359214455668741b52c3db8cf6ea Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 7 Feb 2016 22:08:52 -0600 Subject: update definingviews chapter of wiki2 tutorial --- docs/tutorials/wiki2/src/views/MANIFEST.in | 2 +- docs/tutorials/wiki2/src/views/production.ini | 2 - .../tutorials/wiki2/src/views/tutorial/__init__.py | 2 +- .../wiki2/src/views/tutorial/models/__init__.py | 71 +++++++++++++++++++++- .../wiki2/src/views/tutorial/models/meta.py | 33 ---------- .../wiki2/src/views/tutorial/models/mymodel.py | 3 +- .../src/views/tutorial/scripts/initializedb.py | 27 ++++---- .../wiki2/src/views/tutorial/templates/404.jinja2 | 8 +++ .../wiki2/src/views/tutorial/templates/edit.jinja2 | 2 +- .../wiki2/src/views/tutorial/templates/view.jinja2 | 2 +- docs/tutorials/wiki2/src/views/tutorial/tests.py | 18 +++--- .../wiki2/src/views/tutorial/views/default.py | 23 ++++--- .../wiki2/src/views/tutorial/views/errors.py | 5 ++ 13 files changed, 122 insertions(+), 76 deletions(-) create mode 100644 docs/tutorials/wiki2/src/views/tutorial/templates/404.jinja2 create mode 100644 docs/tutorials/wiki2/src/views/tutorial/views/errors.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/views/MANIFEST.in b/docs/tutorials/wiki2/src/views/MANIFEST.in index 81beba1b1..42cd299b5 100644 --- a/docs/tutorials/wiki2/src/views/MANIFEST.in +++ b/docs/tutorials/wiki2/src/views/MANIFEST.in @@ -1,2 +1,2 @@ include *.txt *.ini *.cfg *.rst -recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml +recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.jinja2 *.pt *.txt *.mak *.mako *.js *.html *.xml diff --git a/docs/tutorials/wiki2/src/views/production.ini b/docs/tutorials/wiki2/src/views/production.ini index 97acfbd7d..cb1db3211 100644 --- a/docs/tutorials/wiki2/src/views/production.ini +++ b/docs/tutorials/wiki2/src/views/production.ini @@ -11,8 +11,6 @@ pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.debug_routematch = false pyramid.default_locale_name = en -pyramid.includes = - pyramid_tm sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite diff --git a/docs/tutorials/wiki2/src/views/tutorial/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/__init__.py index d28f09ca4..5d8c7fba2 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/views/tutorial/__init__.py @@ -6,7 +6,7 @@ def main(global_config, **settings): """ config = Configurator(settings=settings) config.include('pyramid_jinja2') - config.include('.models.meta') + config.include('.models') config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('view_wiki', '/') config.add_route('view_page', '/{pagename}') diff --git a/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py index 7b1c62867..4810c357a 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py @@ -1,7 +1,72 @@ +from sqlalchemy import engine_from_config +from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import configure_mappers -# import all models classes here for sqlalchemy mappers -# to pick up +import zope.sqlalchemy + +# import or define all models here to ensure they are attached to the +# Base.metadata prior to any initialization routines from .mymodel import Page # flake8: noqa -# run configure mappers to ensure we avoid any race conditions +# run configure_mappers after defining all of the models to ensure +# all relationships can be setup configure_mappers() + + +def get_engine(settings, prefix='sqlalchemy.'): + return engine_from_config(settings, prefix) + + +def get_session_factory(engine): + factory = sessionmaker() + factory.configure(bind=engine) + return factory + + +def get_tm_session(session_factory, transaction_manager): + """ + Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. + + This function will hook the session to the transaction manager which + will take care of committing any changes. + + - When using pyramid_tm it will automatically be committed or aborted + depending on whether an exception is raised. + + - When using scripts you should wrap the session in a manager yourself. + For example:: + + import transaction + + engine = get_engine(settings) + session_factory = get_session_factory(engine) + with transaction.manager: + dbsession = get_tm_session(session_factory, transaction.manager) + + """ + dbsession = session_factory() + zope.sqlalchemy.register( + dbsession, transaction_manager=transaction_manager) + return dbsession + + +def includeme(config): + """ + Initialize the model for a Pyramid app. + + Activate this setup using ``config.include('tutorial.models')``. + + """ + settings = config.get_settings() + + # use pyramid_tm to hook the transaction lifecycle to the request + config.include('pyramid_tm') + + session_factory = get_session_factory(get_engine(settings)) + + # make request.dbsession available for use in Pyramid + config.add_request_method( + # r.tm is the transaction manager used by pyramid_tm + lambda r: get_tm_session(session_factory, r.tm), + 'dbsession', + reify=True + ) diff --git a/docs/tutorials/wiki2/src/views/tutorial/models/meta.py b/docs/tutorials/wiki2/src/views/tutorial/models/meta.py index 80ececd8c..fc3e8f1dd 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/models/meta.py +++ b/docs/tutorials/wiki2/src/views/tutorial/models/meta.py @@ -1,8 +1,5 @@ -from sqlalchemy import engine_from_config from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker from sqlalchemy.schema import MetaData -import zope.sqlalchemy # Recommended naming convention used by Alembic, as various different database # providers will autogenerate vastly different names making migrations more @@ -17,33 +14,3 @@ NAMING_CONVENTION = { metadata = MetaData(naming_convention=NAMING_CONVENTION) Base = declarative_base(metadata=metadata) - - -def includeme(config): - settings = config.get_settings() - dbmaker = get_dbmaker(get_engine(settings)) - - config.add_request_method( - lambda r: get_session(r.tm, dbmaker), - 'dbsession', - reify=True - ) - - config.include('pyramid_tm') - - -def get_session(transaction_manager, dbmaker): - dbsession = dbmaker() - zope.sqlalchemy.register(dbsession, - transaction_manager=transaction_manager) - return dbsession - - -def get_engine(settings, prefix='sqlalchemy.'): - return engine_from_config(settings, prefix) - - -def get_dbmaker(engine): - dbmaker = sessionmaker() - dbmaker.configure(bind=engine) - return dbmaker diff --git a/docs/tutorials/wiki2/src/views/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/views/tutorial/models/mymodel.py index 45571d78e..b23d0c0d2 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/models/mymodel.py +++ b/docs/tutorials/wiki2/src/views/tutorial/models/mymodel.py @@ -1,10 +1,11 @@ -from .meta import Base from sqlalchemy import ( Column, Integer, Text, ) +from .meta import Base + class Page(Base): """ The SQLAlchemy declarative model class for a Page object. """ diff --git a/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py index 4aac4a848..601a6e73f 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py @@ -7,13 +7,15 @@ from pyramid.paster import ( setup_logging, ) -from ..models.meta import ( - Base, - get_session, +from pyramid.scripts.common import parse_vars + +from ..models.meta import Base +from ..models import ( get_engine, - get_dbmaker, + get_session_factory, + get_tm_session, ) -from ..models.mymodel import Page +from ..models import Page def usage(argv): @@ -27,16 +29,17 @@ 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) + settings = get_appsettings(config_uri, options=options) engine = get_engine(settings) - dbmaker = get_dbmaker(engine) - - dbsession = get_session(transaction.manager, dbmaker) - Base.metadata.create_all(engine) + session_factory = get_session_factory(engine) + with transaction.manager: - model = Page(name='FrontPage', data='This is the front page') - dbsession.add(model) + dbsession = get_tm_session(session_factory, transaction.manager) + + page = Page(name='FrontPage', data='This is the front page') + dbsession.add(page) diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/404.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/404.jinja2 new file mode 100644 index 000000000..1917f83c7 --- /dev/null +++ b/docs/tutorials/wiki2/src/views/tutorial/templates/404.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+

Pyramid Alchemy scaffold

+

404 Page Not Found

+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 index b3aadfc2e..a41d232e5 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 +++ b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 @@ -37,7 +37,7 @@ Editing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %}

You can return to the - FrontPage. + FrontPage.

diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 index 36bb96870..fa09baf70 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 +++ b/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 @@ -43,7 +43,7 @@ Viewing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %}

You can return to the - FrontPage. + FrontPage.

diff --git a/docs/tutorials/wiki2/src/views/tutorial/tests.py b/docs/tutorials/wiki2/src/views/tutorial/tests.py index b947e3bb1..c54945c28 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/views/tutorial/tests.py @@ -13,22 +13,22 @@ class BaseTest(unittest.TestCase): self.config = testing.setUp(settings={ 'sqlalchemy.url': 'sqlite:///:memory:' }) - self.config.include('.models.meta') + self.config.include('.models') settings = self.config.get_settings() - from .models.meta import ( - get_session, + from .models import ( get_engine, - get_dbmaker, + get_session_factory, + get_tm_session, ) self.engine = get_engine(settings) - dbmaker = get_dbmaker(self.engine) + session_factory = get_session_factory(self.engine) - self.session = get_session(transaction.manager, dbmaker) + self.session = get_tm_session(session_factory, transaction.manager) def init_database(self): - from .models.meta import Base + from .models import Base Base.metadata.create_all(self.engine) def tearDown(self): @@ -36,7 +36,7 @@ class BaseTest(unittest.TestCase): testing.tearDown() transaction.abort() - Base.metadata.create_all(self.engine) + Base.metadata.drop_all(self.engine) class TestMyViewSuccessCondition(BaseTest): @@ -45,7 +45,7 @@ class TestMyViewSuccessCondition(BaseTest): super(TestMyViewSuccessCondition, self).setUp() self.init_database() - from .models.mymodel import MyModel + from .models import MyModel model = MyModel(name='one', value=55) self.session.add(model) diff --git a/docs/tutorials/wiki2/src/views/tutorial/views/default.py b/docs/tutorials/wiki2/src/views/tutorial/views/default.py index 3e5c61a72..96df85a97 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/views/tutorial/views/default.py @@ -9,17 +9,17 @@ from pyramid.httpexceptions import ( from pyramid.view import view_config -from ..models.mymodel import Page +from ..models import Page # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") @view_config(route_name='view_wiki') def view_wiki(request): - return HTTPFound(location=request.route_url('view_page', - pagename='FrontPage')) + next_url = request.route_url('view_page', pagename='FrontPage') + return HTTPFound(location=next_url) -@view_config(route_name='view_page', renderer='templates/view.jinja2') +@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() @@ -41,29 +41,28 @@ def view_page(request): edit_url = request.route_url('edit_page', pagename=pagename) return dict(page=page, content=content, edit_url=edit_url) -@view_config(route_name='add_page', renderer='templates/edit.jinja2') +@view_config(route_name='add_page', renderer='../templates/edit.jinja2') def add_page(request): pagename = request.matchdict['pagename'] if 'form.submitted' in request.params: body = request.params['body'] page = Page(name=pagename, data=body) request.dbsession.add(page) - return HTTPFound(location = request.route_url('view_page', - pagename=pagename)) + next_url = request.route_url('view_page', pagename=pagename) + return HTTPFound(location=next_url) save_url = request.route_url('add_page', pagename=pagename) page = Page(name='', data='') return dict(page=page, save_url=save_url) -@view_config(route_name='edit_page', renderer='templates/edit.jinja2') +@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() if 'form.submitted' in request.params: page.data = request.params['body'] - request.dbsession.add(page) - return HTTPFound(location = request.route_url('view_page', - pagename=pagename)) + next_url = request.route_url('view_page', pagename=pagename) + return HTTPFound(location=next_url) return dict( page=page, - save_url = request.route_url('edit_page', pagename=pagename), + save_url=request.route_url('edit_page', pagename=pagename), ) diff --git a/docs/tutorials/wiki2/src/views/tutorial/views/errors.py b/docs/tutorials/wiki2/src/views/tutorial/views/errors.py new file mode 100644 index 000000000..a4b8201f1 --- /dev/null +++ b/docs/tutorials/wiki2/src/views/tutorial/views/errors.py @@ -0,0 +1,5 @@ +from pyramid.view import notfound_view_config + +@notfound_view_config(renderer='../templates/404.jinja2') +def notfound_view(request): + return {} -- cgit v1.2.3 From 14cff75aca9c2858d0575d8e6beba9758eb012d6 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 7 Feb 2016 23:39:33 -0600 Subject: update authorization chapter of wiki2 tutorial --- docs/tutorials/wiki2/src/authorization/MANIFEST.in | 2 +- .../wiki2/src/authorization/production.ini | 2 - .../wiki2/src/authorization/tutorial/__init__.py | 11 ++-- .../src/authorization/tutorial/models/__init__.py | 71 +++++++++++++++++++++- .../src/authorization/tutorial/models/meta.py | 33 ---------- .../src/authorization/tutorial/models/mymodel.py | 19 +++--- .../authorization/tutorial/scripts/initializedb.py | 27 ++++---- .../authorization/tutorial/security/__init__.py | 1 - .../src/authorization/tutorial/security/default.py | 11 +++- .../authorization/tutorial/templates/404.jinja2 | 8 +++ .../authorization/tutorial/templates/edit.jinja2 | 6 +- .../authorization/tutorial/templates/view.jinja2 | 6 +- .../wiki2/src/authorization/tutorial/tests.py | 18 +++--- .../src/authorization/tutorial/views/default.py | 54 +++++++--------- .../src/authorization/tutorial/views/errors.py | 5 ++ 15 files changed, 160 insertions(+), 114 deletions(-) create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/404.jinja2 create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/views/errors.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authorization/MANIFEST.in b/docs/tutorials/wiki2/src/authorization/MANIFEST.in index 81beba1b1..42cd299b5 100644 --- a/docs/tutorials/wiki2/src/authorization/MANIFEST.in +++ b/docs/tutorials/wiki2/src/authorization/MANIFEST.in @@ -1,2 +1,2 @@ include *.txt *.ini *.cfg *.rst -recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml +recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.jinja2 *.pt *.txt *.mak *.mako *.js *.html *.xml diff --git a/docs/tutorials/wiki2/src/authorization/production.ini b/docs/tutorials/wiki2/src/authorization/production.ini index 97acfbd7d..cb1db3211 100644 --- a/docs/tutorials/wiki2/src/authorization/production.ini +++ b/docs/tutorials/wiki2/src/authorization/production.ini @@ -11,8 +11,6 @@ pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.debug_routematch = false pyramid.default_locale_name = en -pyramid.includes = - pyramid_tm sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py index 084fee19f..a62c42378 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py @@ -2,7 +2,8 @@ from pyramid.config import Configurator from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy -from security.default import groupfinder +from .security.default import groupfinder + def main(global_config, **settings): """ This function returns a Pyramid WSGI application. @@ -10,12 +11,12 @@ def main(global_config, **settings): authn_policy = AuthTktAuthenticationPolicy( 'sosecret', callback=groupfinder, hashalg='sha512') authz_policy = ACLAuthorizationPolicy() - config = Configurator(settings=settings, - root_factory='tutorial.models.mymodel.RootFactory') + config = Configurator(settings=settings) + config.include('pyramid_jinja2') + config.include('.models') + config.set_root_factory('.models.mymodel.RootFactory') config.set_authentication_policy(authn_policy) config.set_authorization_policy(authz_policy) - config.include('pyramid_jinja2') - config.include('.models.meta') config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('view_wiki', '/') config.add_route('login', '/login') diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py index 7b1c62867..4810c357a 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py @@ -1,7 +1,72 @@ +from sqlalchemy import engine_from_config +from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import configure_mappers -# import all models classes here for sqlalchemy mappers -# to pick up +import zope.sqlalchemy + +# import or define all models here to ensure they are attached to the +# Base.metadata prior to any initialization routines from .mymodel import Page # flake8: noqa -# run configure mappers to ensure we avoid any race conditions +# run configure_mappers after defining all of the models to ensure +# all relationships can be setup configure_mappers() + + +def get_engine(settings, prefix='sqlalchemy.'): + return engine_from_config(settings, prefix) + + +def get_session_factory(engine): + factory = sessionmaker() + factory.configure(bind=engine) + return factory + + +def get_tm_session(session_factory, transaction_manager): + """ + Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. + + This function will hook the session to the transaction manager which + will take care of committing any changes. + + - When using pyramid_tm it will automatically be committed or aborted + depending on whether an exception is raised. + + - When using scripts you should wrap the session in a manager yourself. + For example:: + + import transaction + + engine = get_engine(settings) + session_factory = get_session_factory(engine) + with transaction.manager: + dbsession = get_tm_session(session_factory, transaction.manager) + + """ + dbsession = session_factory() + zope.sqlalchemy.register( + dbsession, transaction_manager=transaction_manager) + return dbsession + + +def includeme(config): + """ + Initialize the model for a Pyramid app. + + Activate this setup using ``config.include('tutorial.models')``. + + """ + settings = config.get_settings() + + # use pyramid_tm to hook the transaction lifecycle to the request + config.include('pyramid_tm') + + session_factory = get_session_factory(get_engine(settings)) + + # make request.dbsession available for use in Pyramid + config.add_request_method( + # r.tm is the transaction manager used by pyramid_tm + lambda r: get_tm_session(session_factory, r.tm), + 'dbsession', + reify=True + ) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py index 80ececd8c..fc3e8f1dd 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/meta.py @@ -1,8 +1,5 @@ -from sqlalchemy import engine_from_config from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker from sqlalchemy.schema import MetaData -import zope.sqlalchemy # Recommended naming convention used by Alembic, as various different database # providers will autogenerate vastly different names making migrations more @@ -17,33 +14,3 @@ NAMING_CONVENTION = { metadata = MetaData(naming_convention=NAMING_CONVENTION) Base = declarative_base(metadata=metadata) - - -def includeme(config): - settings = config.get_settings() - dbmaker = get_dbmaker(get_engine(settings)) - - config.add_request_method( - lambda r: get_session(r.tm, dbmaker), - 'dbsession', - reify=True - ) - - config.include('pyramid_tm') - - -def get_session(transaction_manager, dbmaker): - dbsession = dbmaker() - zope.sqlalchemy.register(dbsession, - transaction_manager=transaction_manager) - return dbsession - - -def get_engine(settings, prefix='sqlalchemy.'): - return engine_from_config(settings, prefix) - - -def get_dbmaker(engine): - dbmaker = sessionmaker() - dbmaker.configure(bind=engine) - return dbmaker diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py index 03e2f90ca..25209c745 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py @@ -1,15 +1,14 @@ -from .meta import Base - from pyramid.security import ( Allow, Everyone, - ) - +) from sqlalchemy import ( Column, Integer, Text, - ) +) + +from .meta import Base class Page(Base): @@ -19,8 +18,12 @@ class Page(Base): name = Column(Text, unique=True) data = Column(Integer) + class RootFactory(object): - __acl__ = [ (Allow, Everyone, 'view'), - (Allow, 'group:editors', 'edit') ] + __acl__ = [ + (Allow, Everyone, 'view'), + (Allow, 'group:editors', 'edit'), + ] + def __init__(self, request): - pass \ No newline at end of file + pass diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py index 4aac4a848..601a6e73f 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py @@ -7,13 +7,15 @@ from pyramid.paster import ( setup_logging, ) -from ..models.meta import ( - Base, - get_session, +from pyramid.scripts.common import parse_vars + +from ..models.meta import Base +from ..models import ( get_engine, - get_dbmaker, + get_session_factory, + get_tm_session, ) -from ..models.mymodel import Page +from ..models import Page def usage(argv): @@ -27,16 +29,17 @@ 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) + settings = get_appsettings(config_uri, options=options) engine = get_engine(settings) - dbmaker = get_dbmaker(engine) - - dbsession = get_session(transaction.manager, dbmaker) - Base.metadata.create_all(engine) + session_factory = get_session_factory(engine) + with transaction.manager: - model = Page(name='FrontPage', data='This is the front page') - dbsession.add(model) + dbsession = get_tm_session(session_factory, transaction.manager) + + page = Page(name='FrontPage', data='This is the front page') + dbsession.add(page) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py index 5bb534f79..e69de29bb 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py @@ -1 +0,0 @@ -# package diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/security/default.py b/docs/tutorials/wiki2/src/authorization/tutorial/security/default.py index d88c9c71f..7fc1ea7c8 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/security/default.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/security/default.py @@ -1,6 +1,11 @@ -USERS = {'editor':'editor', - 'viewer':'viewer'} -GROUPS = {'editor':['group:editors']} +USERS = { + 'editor': 'editor', + 'viewer': 'viewer', +} + +GROUPS = { + 'editor': ['group:editors'], +} def groupfinder(userid, request): if userid in USERS: diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/404.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/404.jinja2 new file mode 100644 index 000000000..1917f83c7 --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/404.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+

Pyramid Alchemy scaffold

+

404 Page Not Found

+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 index c4f3a2c93..70ce49b73 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 @@ -33,16 +33,16 @@
- {% if logged_in %} + {% if request.authenticated_userid is not None %}

- Logout + Logout

{% endif %}

Editing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %}

You can return to the - FrontPage. + FrontPage.

diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 index a7afc66fc..b12ca5b0c 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 @@ -33,9 +33,9 @@
- {% if logged_in %} + {% if request.authenticated_userid is not None %}

- Logout + Logout

{% endif %}

{{ content|safe }}

@@ -48,7 +48,7 @@ Viewing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %}

You can return to the - FrontPage. + FrontPage.

diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py index b947e3bb1..c54945c28 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/tests.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/tests.py @@ -13,22 +13,22 @@ class BaseTest(unittest.TestCase): self.config = testing.setUp(settings={ 'sqlalchemy.url': 'sqlite:///:memory:' }) - self.config.include('.models.meta') + self.config.include('.models') settings = self.config.get_settings() - from .models.meta import ( - get_session, + from .models import ( get_engine, - get_dbmaker, + get_session_factory, + get_tm_session, ) self.engine = get_engine(settings) - dbmaker = get_dbmaker(self.engine) + session_factory = get_session_factory(self.engine) - self.session = get_session(transaction.manager, dbmaker) + self.session = get_tm_session(session_factory, transaction.manager) def init_database(self): - from .models.meta import Base + from .models import Base Base.metadata.create_all(self.engine) def tearDown(self): @@ -36,7 +36,7 @@ class BaseTest(unittest.TestCase): testing.tearDown() transaction.abort() - Base.metadata.create_all(self.engine) + Base.metadata.drop_all(self.engine) class TestMyViewSuccessCondition(BaseTest): @@ -45,7 +45,7 @@ class TestMyViewSuccessCondition(BaseTest): super(TestMyViewSuccessCondition, self).setUp() self.init_database() - from .models.mymodel import MyModel + from .models import MyModel model = MyModel(name='one', value=55) self.session.add(model) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py index f35f041a4..aa77facd7 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py @@ -6,31 +6,27 @@ from pyramid.httpexceptions import ( HTTPFound, HTTPNotFound, ) - from pyramid.view import ( view_config, forbidden_view_config, ) - from pyramid.security import ( remember, forget, ) +from ..models import Page from ..security.default import USERS -from ..models.mymodel import Page - # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") -@view_config(route_name='view_wiki', - permission='view') +@view_config(route_name='view_wiki', permission='view') def view_wiki(request): - return HTTPFound(location=request.route_url('view_page', - pagename='FrontPage')) + next_url = request.route_url('view_page', pagename='FrontPage') + return HTTPFound(location=next_url) -@view_config(route_name='view_page', renderer='templates/view.jinja2', +@view_config(route_name='view_page', renderer='../templates/view.jinja2', permission='view') def view_page(request): pagename = request.matchdict['pagename'] @@ -51,10 +47,9 @@ def view_page(request): content = publish_parts(page.data, writer_name='html')['html_body'] content = wikiwords.sub(check, content) edit_url = request.route_url('edit_page', pagename=pagename) - return dict(page=page, content=content, edit_url=edit_url, - logged_in=request.authenticated_userid) + return dict(page=page, content=content, edit_url=edit_url) -@view_config(route_name='add_page', renderer='templates/edit.jinja2', +@view_config(route_name='add_page', renderer='../templates/edit.jinja2', permission='edit') def add_page(request): pagename = request.matchdict['pagename'] @@ -62,29 +57,27 @@ def add_page(request): body = request.params['body'] page = Page(name=pagename, data=body) request.dbsession.add(page) - return HTTPFound(location = request.route_url('view_page', - pagename=pagename)) + next_url = request.route_url('view_page', pagename=pagename) + return HTTPFound(location=next_url) save_url = request.route_url('add_page', pagename=pagename) page = Page(name='', data='') - return dict(page=page, save_url=save_url, - logged_in=request.authenticated_userid) + return dict(page=page, save_url=save_url) -@view_config(route_name='edit_page', renderer='templates/edit.jinja2', +@view_config(route_name='edit_page', renderer='../templates/edit.jinja2', permission='edit') def edit_page(request): pagename = request.matchdict['pagename'] page = request.dbsession.query(Page).filter_by(name=pagename).one() if 'form.submitted' in request.params: page.data = request.params['body'] - request.dbsession.add(page) - return HTTPFound(location = request.route_url('view_page', - pagename=pagename)) + next_url = request.route_url('view_page', pagename=pagename) + return HTTPFound(location=next_url) return dict( page=page, - save_url = request.route_url('edit_page', pagename=pagename), - logged_in=request.authenticated_userid + save_url=request.route_url('edit_page', pagename=pagename), ) + @view_config(route_name='login', renderer='templates/login.jinja2') @forbidden_view_config(renderer='templates/login.jinja2') def login(request): @@ -101,20 +94,19 @@ def login(request): password = request.params['password'] if USERS.get(login) == password: headers = remember(request, login) - return HTTPFound(location = came_from, - headers = headers) + return HTTPFound(location=came_from, headers=headers) message = 'Failed login' return dict( - message = message, - url = request.application_url + '/login', - came_from = came_from, - login = login, - password = password, + message=message, + url=request.route_url('login'), + came_from=came_from, + login=login, + password=password, ) @view_config(route_name='logout') def logout(request): headers = forget(request) - return HTTPFound(location = request.route_url('view_wiki'), - headers = headers) + next_url = request.route_url('view_wiki') + return HTTPFound(location=next_url, headers=headers) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/errors.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/errors.py new file mode 100644 index 000000000..a4b8201f1 --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/errors.py @@ -0,0 +1,5 @@ +from pyramid.view import notfound_view_config + +@notfound_view_config(renderer='../templates/404.jinja2') +def notfound_view(request): + return {} -- cgit v1.2.3 From 0b02e46ff9dafcdf9d4c03bac2958c8b20c596f6 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 8 Feb 2016 00:19:31 -0600 Subject: expose the session factory on the registry --- docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py | 1 + docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py | 1 + docs/tutorials/wiki2/src/models/tutorial/models/__init__.py | 1 + 3 files changed, 3 insertions(+) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py index 4810c357a..3d3efe06f 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py @@ -62,6 +62,7 @@ def includeme(config): config.include('pyramid_tm') session_factory = get_session_factory(get_engine(settings)) + config.registry['dbsession_factory'] = session_factory # make request.dbsession available for use in Pyramid config.add_request_method( diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py index a4026fcd6..48a957ecb 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py @@ -62,6 +62,7 @@ def includeme(config): config.include('pyramid_tm') session_factory = get_session_factory(get_engine(settings)) + config.registry['dbsession_factory'] = session_factory # make request.dbsession available for use in Pyramid config.add_request_method( diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py index 4810c357a..3d3efe06f 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py @@ -62,6 +62,7 @@ def includeme(config): config.include('pyramid_tm') session_factory = get_session_factory(get_engine(settings)) + config.registry['dbsession_factory'] = session_factory # make request.dbsession available for use in Pyramid config.add_request_method( -- cgit v1.2.3 From 1c108019dae884e810d6436e10f8648c77bdd181 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 8 Feb 2016 00:43:47 -0600 Subject: [wip] update tests in wiki2 tutorial --- docs/tutorials/wiki2/src/tests/MANIFEST.in | 2 +- docs/tutorials/wiki2/src/tests/production.ini | 2 - docs/tutorials/wiki2/src/tests/setup.py | 1 - .../tutorials/wiki2/src/tests/tutorial/__init__.py | 11 ++-- .../wiki2/src/tests/tutorial/models/__init__.py | 72 +++++++++++++++++++++- .../wiki2/src/tests/tutorial/models/meta.py | 33 ---------- .../wiki2/src/tests/tutorial/models/mymodel.py | 19 +++--- .../src/tests/tutorial/scripts/initializedb.py | 27 ++++---- .../wiki2/src/tests/tutorial/security/__init__.py | 1 - .../wiki2/src/tests/tutorial/security/default.py | 11 +++- .../wiki2/src/tests/tutorial/templates/404.jinja2 | 8 +++ .../wiki2/src/tests/tutorial/templates/edit.jinja2 | 6 +- .../wiki2/src/tests/tutorial/templates/view.jinja2 | 6 +- .../wiki2/src/tests/tutorial/tests/__init__.py | 1 - .../src/tests/tutorial/tests/test_functional.py | 29 ++------- .../wiki2/src/tests/tutorial/tests/test_views.py | 46 +++----------- .../wiki2/src/tests/tutorial/views/default.py | 54 +++++++--------- .../wiki2/src/tests/tutorial/views/errors.py | 5 ++ .../wiki2/src/views/tutorial/models/__init__.py | 1 + 19 files changed, 168 insertions(+), 167 deletions(-) create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/templates/404.jinja2 create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/views/errors.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/tests/MANIFEST.in b/docs/tutorials/wiki2/src/tests/MANIFEST.in index 81beba1b1..42cd299b5 100644 --- a/docs/tutorials/wiki2/src/tests/MANIFEST.in +++ b/docs/tutorials/wiki2/src/tests/MANIFEST.in @@ -1,2 +1,2 @@ include *.txt *.ini *.cfg *.rst -recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml +recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.jinja2 *.pt *.txt *.mak *.mako *.js *.html *.xml diff --git a/docs/tutorials/wiki2/src/tests/production.ini b/docs/tutorials/wiki2/src/tests/production.ini index 97acfbd7d..cb1db3211 100644 --- a/docs/tutorials/wiki2/src/tests/production.ini +++ b/docs/tutorials/wiki2/src/tests/production.ini @@ -11,8 +11,6 @@ pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.debug_routematch = false pyramid.default_locale_name = en -pyramid.includes = - pyramid_tm sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite diff --git a/docs/tutorials/wiki2/src/tests/setup.py b/docs/tutorials/wiki2/src/tests/setup.py index f640b4399..d4e5a4072 100644 --- a/docs/tutorials/wiki2/src/tests/setup.py +++ b/docs/tutorials/wiki2/src/tests/setup.py @@ -18,7 +18,6 @@ requires = [ 'zope.sqlalchemy', 'waitress', 'docutils', - 'WebTest', ] setup(name='tutorial', diff --git a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py index 084fee19f..a62c42378 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py @@ -2,7 +2,8 @@ from pyramid.config import Configurator from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy -from security.default import groupfinder +from .security.default import groupfinder + def main(global_config, **settings): """ This function returns a Pyramid WSGI application. @@ -10,12 +11,12 @@ def main(global_config, **settings): authn_policy = AuthTktAuthenticationPolicy( 'sosecret', callback=groupfinder, hashalg='sha512') authz_policy = ACLAuthorizationPolicy() - config = Configurator(settings=settings, - root_factory='tutorial.models.mymodel.RootFactory') + config = Configurator(settings=settings) + config.include('pyramid_jinja2') + config.include('.models') + config.set_root_factory('.models.mymodel.RootFactory') config.set_authentication_policy(authn_policy) config.set_authorization_policy(authz_policy) - config.include('pyramid_jinja2') - config.include('.models.meta') config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('view_wiki', '/') config.add_route('login', '/login') diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py index 7b1c62867..3d3efe06f 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py @@ -1,7 +1,73 @@ +from sqlalchemy import engine_from_config +from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import configure_mappers -# import all models classes here for sqlalchemy mappers -# to pick up +import zope.sqlalchemy + +# import or define all models here to ensure they are attached to the +# Base.metadata prior to any initialization routines from .mymodel import Page # flake8: noqa -# run configure mappers to ensure we avoid any race conditions +# run configure_mappers after defining all of the models to ensure +# all relationships can be setup configure_mappers() + + +def get_engine(settings, prefix='sqlalchemy.'): + return engine_from_config(settings, prefix) + + +def get_session_factory(engine): + factory = sessionmaker() + factory.configure(bind=engine) + return factory + + +def get_tm_session(session_factory, transaction_manager): + """ + Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. + + This function will hook the session to the transaction manager which + will take care of committing any changes. + + - When using pyramid_tm it will automatically be committed or aborted + depending on whether an exception is raised. + + - When using scripts you should wrap the session in a manager yourself. + For example:: + + import transaction + + engine = get_engine(settings) + session_factory = get_session_factory(engine) + with transaction.manager: + dbsession = get_tm_session(session_factory, transaction.manager) + + """ + dbsession = session_factory() + zope.sqlalchemy.register( + dbsession, transaction_manager=transaction_manager) + return dbsession + + +def includeme(config): + """ + Initialize the model for a Pyramid app. + + Activate this setup using ``config.include('tutorial.models')``. + + """ + settings = config.get_settings() + + # use pyramid_tm to hook the transaction lifecycle to the request + config.include('pyramid_tm') + + session_factory = get_session_factory(get_engine(settings)) + config.registry['dbsession_factory'] = session_factory + + # make request.dbsession available for use in Pyramid + config.add_request_method( + # r.tm is the transaction manager used by pyramid_tm + lambda r: get_tm_session(session_factory, r.tm), + 'dbsession', + reify=True + ) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models/meta.py b/docs/tutorials/wiki2/src/tests/tutorial/models/meta.py index 80ececd8c..fc3e8f1dd 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/models/meta.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/models/meta.py @@ -1,8 +1,5 @@ -from sqlalchemy import engine_from_config from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker from sqlalchemy.schema import MetaData -import zope.sqlalchemy # Recommended naming convention used by Alembic, as various different database # providers will autogenerate vastly different names making migrations more @@ -17,33 +14,3 @@ NAMING_CONVENTION = { metadata = MetaData(naming_convention=NAMING_CONVENTION) Base = declarative_base(metadata=metadata) - - -def includeme(config): - settings = config.get_settings() - dbmaker = get_dbmaker(get_engine(settings)) - - config.add_request_method( - lambda r: get_session(r.tm, dbmaker), - 'dbsession', - reify=True - ) - - config.include('pyramid_tm') - - -def get_session(transaction_manager, dbmaker): - dbsession = dbmaker() - zope.sqlalchemy.register(dbsession, - transaction_manager=transaction_manager) - return dbsession - - -def get_engine(settings, prefix='sqlalchemy.'): - return engine_from_config(settings, prefix) - - -def get_dbmaker(engine): - dbmaker = sessionmaker() - dbmaker.configure(bind=engine) - return dbmaker diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/tests/tutorial/models/mymodel.py index 03e2f90ca..25209c745 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/models/mymodel.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/models/mymodel.py @@ -1,15 +1,14 @@ -from .meta import Base - from pyramid.security import ( Allow, Everyone, - ) - +) from sqlalchemy import ( Column, Integer, Text, - ) +) + +from .meta import Base class Page(Base): @@ -19,8 +18,12 @@ class Page(Base): name = Column(Text, unique=True) data = Column(Integer) + class RootFactory(object): - __acl__ = [ (Allow, Everyone, 'view'), - (Allow, 'group:editors', 'edit') ] + __acl__ = [ + (Allow, Everyone, 'view'), + (Allow, 'group:editors', 'edit'), + ] + def __init__(self, request): - pass \ No newline at end of file + pass diff --git a/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py index 4aac4a848..601a6e73f 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py @@ -7,13 +7,15 @@ from pyramid.paster import ( setup_logging, ) -from ..models.meta import ( - Base, - get_session, +from pyramid.scripts.common import parse_vars + +from ..models.meta import Base +from ..models import ( get_engine, - get_dbmaker, + get_session_factory, + get_tm_session, ) -from ..models.mymodel import Page +from ..models import Page def usage(argv): @@ -27,16 +29,17 @@ 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) + settings = get_appsettings(config_uri, options=options) engine = get_engine(settings) - dbmaker = get_dbmaker(engine) - - dbsession = get_session(transaction.manager, dbmaker) - Base.metadata.create_all(engine) + session_factory = get_session_factory(engine) + with transaction.manager: - model = Page(name='FrontPage', data='This is the front page') - dbsession.add(model) + dbsession = get_tm_session(session_factory, transaction.manager) + + page = Page(name='FrontPage', data='This is the front page') + dbsession.add(page) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py index 5bb534f79..e69de29bb 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py @@ -1 +0,0 @@ -# package diff --git a/docs/tutorials/wiki2/src/tests/tutorial/security/default.py b/docs/tutorials/wiki2/src/tests/tutorial/security/default.py index d88c9c71f..7fc1ea7c8 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/security/default.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/security/default.py @@ -1,6 +1,11 @@ -USERS = {'editor':'editor', - 'viewer':'viewer'} -GROUPS = {'editor':['group:editors']} +USERS = { + 'editor': 'editor', + 'viewer': 'viewer', +} + +GROUPS = { + 'editor': ['group:editors'], +} def groupfinder(userid, request): if userid in USERS: diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/404.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/404.jinja2 new file mode 100644 index 000000000..1917f83c7 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/404.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+

Pyramid Alchemy scaffold

+

404 Page Not Found

+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 index c4f3a2c93..70ce49b73 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 @@ -33,16 +33,16 @@
- {% if logged_in %} + {% if request.authenticated_userid is not None %}

- Logout + Logout

{% endif %}

Editing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %}

You can return to the - FrontPage. + FrontPage.

diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 index a7afc66fc..b12ca5b0c 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 @@ -33,9 +33,9 @@
- {% if logged_in %} + {% if request.authenticated_userid is not None %}

- Logout + Logout

{% endif %}

{{ content|safe }}

@@ -48,7 +48,7 @@ Viewing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %}

You can return to the - FrontPage. + FrontPage.

diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/__init__.py index 8b1378917..e69de29bb 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/tests/__init__.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/__init__.py @@ -1 +0,0 @@ - diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py index 339c60bc2..eda47c064 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py @@ -1,17 +1,5 @@ import unittest -from pyramid import testing - - -def dummy_request(dbsession): - return testing.DummyRequest(dbsession=dbsession) - - -def _register_routes(config): - config.add_route('view_page', '{pagename}') - config.add_route('edit_page', '{pagename}/edit_page') - config.add_route('add_page', 'add_page/{pagename}') - class FunctionalTests(unittest.TestCase): @@ -27,11 +15,8 @@ class FunctionalTests(unittest.TestCase): @staticmethod def setup_database(): import transaction - from tutorial.models.mymodel import Page - from tutorial.models.meta import ( - Base, - ) - import tutorial.models.meta + from tutorial.models import Page + from tutorial.models.meta import Base def initialize_db(dbsession, engine): @@ -40,11 +25,9 @@ class FunctionalTests(unittest.TestCase): model = Page(name='FrontPage', data='This is the front page') dbsession.add(model) - def wrap_get_session(transaction_manager, dbmaker): - dbsession = get_session(transaction_manager, dbmaker) + def wrap_get_tm_session(session_factory, transaction_manager): + dbsession = get_tm_session(session_factory, transaction_manager) initialize_db(dbsession, engine) - tutorial.models.meta.get_session = get_session - tutorial.models.meta.get_engine = get_engine return dbsession def wrap_get_engine(settings): @@ -53,10 +36,10 @@ class FunctionalTests(unittest.TestCase): return engine get_session = tutorial.models.meta.get_session - tutorial.models.meta.get_session = wrap_get_session + tutorial.models.get_tm_session = wrap_get_tm_session get_engine = tutorial.models.meta.get_engine - tutorial.models.meta.get_engine = wrap_get_engine + tutorial.models.get_engine = wrap_get_engine @classmethod def setUpClass(cls): diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py index d70311e38..81d84fa30 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py @@ -10,35 +10,29 @@ def dummy_request(dbsession): def _register_routes(config): config.add_route('view_page', '{pagename}') - config.add_route('edit_page', '{pagename}/edit_page') config.add_route('add_page', 'add_page/{pagename}') + config.add_route('edit_page', '{pagename}/edit_page') class BaseTest(unittest.TestCase): def setUp(self): + from ..models import get_tm_session self.config = testing.setUp(settings={ 'sqlalchemy.url': 'sqlite:///:memory:' }) - self.config.include('..models.meta') - _register_routes(self.config) - settings = self.config.get_settings() + self.config.include('..models') + self.config.include(_register_routes) - from ..models.meta import ( - get_session, - get_engine, - get_dbmaker, - ) - - self.engine = get_engine(settings) - dbmaker = get_dbmaker(self.engine) - - self.session = get_session(transaction.manager, dbmaker) + session_factory = self.config.registry['dbsession_factory'] + self.session = get_tm_session(session_factory, transaction.manager) self.init_database() def init_database(self): from ..models.meta import Base - Base.metadata.create_all(self.engine) + session_factory = self.config.registry['dbsession_factory'] + engine = session_factory.get_bind() + Base.metadata.create_all(engine) def tearDown(self): testing.tearDown() @@ -46,7 +40,6 @@ class BaseTest(unittest.TestCase): class ViewWikiTests(unittest.TestCase): - def setUp(self): self.config = testing.setUp() _register_routes(self.config) @@ -65,13 +58,6 @@ class ViewWikiTests(unittest.TestCase): class ViewPageTests(BaseTest): - def setUp(self): - super(ViewPageTests, self).setUp() - - def tearDown(self): - transaction.abort() - testing.tearDown() - def _callFUT(self, request): from tutorial.views.default import view_page return view_page(request) @@ -102,13 +88,6 @@ class ViewPageTests(BaseTest): class AddPageTests(BaseTest): - def setUp(self): - super(AddPageTests, self).setUp() - - def tearDown(self): - transaction.abort() - testing.tearDown() - def _callFUT(self, request): from tutorial.views.default import add_page return add_page(request) @@ -133,13 +112,6 @@ class AddPageTests(BaseTest): class EditPageTests(BaseTest): - def setUp(self): - super(EditPageTests, self).setUp() - - def tearDown(self): - transaction.abort() - testing.tearDown() - def _callFUT(self, request): from tutorial.views.default import edit_page return edit_page(request) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py index f35f041a4..aa77facd7 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py @@ -6,31 +6,27 @@ from pyramid.httpexceptions import ( HTTPFound, HTTPNotFound, ) - from pyramid.view import ( view_config, forbidden_view_config, ) - from pyramid.security import ( remember, forget, ) +from ..models import Page from ..security.default import USERS -from ..models.mymodel import Page - # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") -@view_config(route_name='view_wiki', - permission='view') +@view_config(route_name='view_wiki', permission='view') def view_wiki(request): - return HTTPFound(location=request.route_url('view_page', - pagename='FrontPage')) + next_url = request.route_url('view_page', pagename='FrontPage') + return HTTPFound(location=next_url) -@view_config(route_name='view_page', renderer='templates/view.jinja2', +@view_config(route_name='view_page', renderer='../templates/view.jinja2', permission='view') def view_page(request): pagename = request.matchdict['pagename'] @@ -51,10 +47,9 @@ def view_page(request): content = publish_parts(page.data, writer_name='html')['html_body'] content = wikiwords.sub(check, content) edit_url = request.route_url('edit_page', pagename=pagename) - return dict(page=page, content=content, edit_url=edit_url, - logged_in=request.authenticated_userid) + return dict(page=page, content=content, edit_url=edit_url) -@view_config(route_name='add_page', renderer='templates/edit.jinja2', +@view_config(route_name='add_page', renderer='../templates/edit.jinja2', permission='edit') def add_page(request): pagename = request.matchdict['pagename'] @@ -62,29 +57,27 @@ def add_page(request): body = request.params['body'] page = Page(name=pagename, data=body) request.dbsession.add(page) - return HTTPFound(location = request.route_url('view_page', - pagename=pagename)) + next_url = request.route_url('view_page', pagename=pagename) + return HTTPFound(location=next_url) save_url = request.route_url('add_page', pagename=pagename) page = Page(name='', data='') - return dict(page=page, save_url=save_url, - logged_in=request.authenticated_userid) + return dict(page=page, save_url=save_url) -@view_config(route_name='edit_page', renderer='templates/edit.jinja2', +@view_config(route_name='edit_page', renderer='../templates/edit.jinja2', permission='edit') def edit_page(request): pagename = request.matchdict['pagename'] page = request.dbsession.query(Page).filter_by(name=pagename).one() if 'form.submitted' in request.params: page.data = request.params['body'] - request.dbsession.add(page) - return HTTPFound(location = request.route_url('view_page', - pagename=pagename)) + next_url = request.route_url('view_page', pagename=pagename) + return HTTPFound(location=next_url) return dict( page=page, - save_url = request.route_url('edit_page', pagename=pagename), - logged_in=request.authenticated_userid + save_url=request.route_url('edit_page', pagename=pagename), ) + @view_config(route_name='login', renderer='templates/login.jinja2') @forbidden_view_config(renderer='templates/login.jinja2') def login(request): @@ -101,20 +94,19 @@ def login(request): password = request.params['password'] if USERS.get(login) == password: headers = remember(request, login) - return HTTPFound(location = came_from, - headers = headers) + return HTTPFound(location=came_from, headers=headers) message = 'Failed login' return dict( - message = message, - url = request.application_url + '/login', - came_from = came_from, - login = login, - password = password, + message=message, + url=request.route_url('login'), + came_from=came_from, + login=login, + password=password, ) @view_config(route_name='logout') def logout(request): headers = forget(request) - return HTTPFound(location = request.route_url('view_wiki'), - headers = headers) + next_url = request.route_url('view_wiki') + return HTTPFound(location=next_url, headers=headers) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/errors.py b/docs/tutorials/wiki2/src/tests/tutorial/views/errors.py new file mode 100644 index 000000000..a4b8201f1 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/views/errors.py @@ -0,0 +1,5 @@ +from pyramid.view import notfound_view_config + +@notfound_view_config(renderer='../templates/404.jinja2') +def notfound_view(request): + return {} diff --git a/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py index 4810c357a..3d3efe06f 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py @@ -62,6 +62,7 @@ def includeme(config): config.include('pyramid_tm') session_factory = get_session_factory(get_engine(settings)) + config.registry['dbsession_factory'] = session_factory # make request.dbsession available for use in Pyramid config.add_request_method( -- cgit v1.2.3 From d6a758e58ef1c4782ecd3fe53c8563284f2496ca Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 8 Feb 2016 22:37:22 -0600 Subject: fix tests to get the bind from dbsession_factory properly --- docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py index 81d84fa30..b2830d070 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py @@ -31,7 +31,7 @@ class BaseTest(unittest.TestCase): def init_database(self): from ..models.meta import Base session_factory = self.config.registry['dbsession_factory'] - engine = session_factory.get_bind() + engine = session_factory.kw['bind'] Base.metadata.create_all(engine) def tearDown(self): -- cgit v1.2.3 From 91ffccabafd2f074ac7620b5b64e52a8eb3cb31a Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 8 Feb 2016 23:00:48 -0600 Subject: fix jinja2 none test --- docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 | 2 +- docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 | 2 +- docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 | 2 +- docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 index 70ce49b73..4d767cfbe 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 @@ -33,7 +33,7 @@
- {% if request.authenticated_userid is not None %} + {% if request.authenticated_userid is not none %}

Logout

diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 index b12ca5b0c..942b8479b 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 @@ -33,7 +33,7 @@
- {% if request.authenticated_userid is not None %} + {% if request.authenticated_userid is not none %}

Logout

diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 index 70ce49b73..4d767cfbe 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 @@ -33,7 +33,7 @@
- {% if request.authenticated_userid is not None %} + {% if request.authenticated_userid is not none %}

Logout

diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 index b12ca5b0c..942b8479b 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 @@ -33,7 +33,7 @@
- {% if request.authenticated_userid is not None %} + {% if request.authenticated_userid is not none %}

Logout

-- cgit v1.2.3 From 62f0411a12a7f86bd7e3060b14f223cfb96322ad Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 8 Feb 2016 23:00:58 -0600 Subject: fix functional tests --- docs/tutorials/wiki2/src/tests/setup.py | 5 ++ .../src/tests/tutorial/tests/test_functional.py | 57 +++++++--------------- 2 files changed, 23 insertions(+), 39 deletions(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/tests/setup.py b/docs/tutorials/wiki2/src/tests/setup.py index d4e5a4072..93195a68c 100644 --- a/docs/tutorials/wiki2/src/tests/setup.py +++ b/docs/tutorials/wiki2/src/tests/setup.py @@ -20,6 +20,10 @@ requires = [ 'docutils', ] +tests_require = [ + 'WebTest', +] + setup(name='tutorial', version='0.0', description='tutorial', @@ -39,6 +43,7 @@ setup(name='tutorial', zip_safe=False, test_suite='tutorial', install_requires=requires, + tests_require=tests_require, entry_points="""\ [paste.app_factory] main = tutorial:main diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py index eda47c064..2c08f9a64 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py @@ -1,4 +1,6 @@ +import transaction import unittest +from webtest import TestApp class FunctionalTests(unittest.TestCase): @@ -10,55 +12,32 @@ class FunctionalTests(unittest.TestCase): editor_login = '/login?login=editor&password=editor' \ '&came_from=FrontPage&form.submitted=Login' - engine = None - - @staticmethod - def setup_database(): - import transaction - from tutorial.models import Page - from tutorial.models.meta import Base - - - def initialize_db(dbsession, engine): - Base.metadata.create_all(engine) - with transaction.manager: - model = Page(name='FrontPage', data='This is the front page') - dbsession.add(model) - - def wrap_get_tm_session(session_factory, transaction_manager): - dbsession = get_tm_session(session_factory, transaction_manager) - initialize_db(dbsession, engine) - return dbsession - - def wrap_get_engine(settings): - global engine - engine = get_engine(settings) - return engine - - get_session = tutorial.models.meta.get_session - tutorial.models.get_tm_session = wrap_get_tm_session - - get_engine = tutorial.models.meta.get_engine - tutorial.models.get_engine = wrap_get_engine - @classmethod def setUpClass(cls): - cls.setup_database() - - from webtest import TestApp + from tutorial.models.meta import Base + from tutorial.models import ( + Page, + get_tm_session, + ) from tutorial import main + settings = {'sqlalchemy.url': 'sqlite://'} app = main({}, **settings) cls.testapp = TestApp(app) + session_factory = app.registry['dbsession_factory'] + cls.engine = session_factory.kw['bind'] + Base.metadata.create_all(bind=cls.engine) + + with transaction.manager: + dbsession = get_tm_session(session_factory, transaction.manager) + model = Page(name='FrontPage', data='This is the front page') + dbsession.add(model) + @classmethod def tearDownClass(cls): from tutorial.models.meta import Base - Base.metadata.drop_all(engine) - - def tearDown(self): - import transaction - transaction.abort() + Base.metadata.drop_all(bind=cls.engine) def test_root(self): res = self.testapp.get('/', status=302) -- cgit v1.2.3 From ff88c1535c65a717a030a480e39723724d53d985 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Wed, 10 Feb 2016 22:28:47 -0600 Subject: split login from forbidden --- .../wiki2/src/tests/tutorial/tests/test_functional.py | 8 ++++---- docs/tutorials/wiki2/src/tests/tutorial/views/default.py | 7 +------ docs/tutorials/wiki2/src/tests/tutorial/views/errors.py | 11 ++++++++++- 3 files changed, 15 insertions(+), 11 deletions(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py index 2c08f9a64..b25d9a332 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py @@ -70,21 +70,21 @@ class FunctionalTests(unittest.TestCase): self.assertTrue(b'Logout' not in res.body) def test_anonymous_user_cannot_edit(self): - res = self.testapp.get('/FrontPage/edit_page', status=200) + res = self.testapp.get('/FrontPage/edit_page', status=302).follow() self.assertTrue(b'Login' in res.body) def test_anonymous_user_cannot_add(self): - res = self.testapp.get('/add_page/NewPage', status=200) + res = self.testapp.get('/add_page/NewPage', status=302).follow() self.assertTrue(b'Login' in res.body) def test_viewer_user_cannot_edit(self): self.testapp.get(self.viewer_login, status=302) - res = self.testapp.get('/FrontPage/edit_page', status=200) + res = self.testapp.get('/FrontPage/edit_page', status=302).follow() self.assertTrue(b'Login' in res.body) def test_viewer_user_cannot_add(self): self.testapp.get(self.viewer_login, status=302) - res = self.testapp.get('/add_page/NewPage', status=200) + res = self.testapp.get('/add_page/NewPage', status=302).follow() self.assertTrue(b'Login' in res.body) def test_editors_member_user_can_edit(self): diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py index aa77facd7..bc8a59fe8 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py @@ -6,14 +6,11 @@ from pyramid.httpexceptions import ( HTTPFound, HTTPNotFound, ) -from pyramid.view import ( - view_config, - forbidden_view_config, - ) from pyramid.security import ( remember, forget, ) +from pyramid.view import view_config from ..models import Page from ..security.default import USERS @@ -77,9 +74,7 @@ def edit_page(request): save_url=request.route_url('edit_page', pagename=pagename), ) - @view_config(route_name='login', renderer='templates/login.jinja2') -@forbidden_view_config(renderer='templates/login.jinja2') def login(request): login_url = request.route_url('login') referrer = request.url diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/errors.py b/docs/tutorials/wiki2/src/tests/tutorial/views/errors.py index a4b8201f1..f8dbe4e05 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/views/errors.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/views/errors.py @@ -1,5 +1,14 @@ -from pyramid.view import notfound_view_config +from pyramid.httpexceptions import HTTPFound +from pyramid.view import ( + forbidden_view_config, + notfound_view_config, +) @notfound_view_config(renderer='../templates/404.jinja2') def notfound_view(request): return {} + +@forbidden_view_config() +def forbidden_view(request): + next_url = request.route_url('login', _query={'came_from': request.url}) + return HTTPFound(location=next_url) -- cgit v1.2.3 From 07d38f5d4c9ebaf267d4ecaf8c0bd4c508f1848f Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Wed, 10 Feb 2016 22:38:38 -0600 Subject: several simple refactorings - move auth from default.py to auth.py - rename errors to notfound - drop basic templates (mytemplate.jinja2, layout.jinja2) --- .../authorization/tutorial/templates/layout.jinja2 | 66 ---------------- .../tutorial/templates/mytemplate.jinja2 | 8 -- .../wiki2/src/authorization/tutorial/views/auth.py | 49 ++++++++++++ .../src/authorization/tutorial/views/default.py | 44 +---------- .../src/authorization/tutorial/views/errors.py | 5 -- .../src/authorization/tutorial/views/notfound.py | 7 ++ .../wiki2/src/basiclayout/tutorial/views/errors.py | 5 -- .../src/basiclayout/tutorial/views/notfound.py | 7 ++ .../wiki2/src/models/tutorial/views/errors.py | 5 -- .../wiki2/src/models/tutorial/views/notfound.py | 7 ++ .../src/tests/tutorial/templates/layout.jinja2 | 66 ---------------- .../src/tests/tutorial/templates/mytemplate.jinja2 | 8 -- .../src/tests/tutorial/tests/test_functional.py | 15 ++-- .../wiki2/src/tests/tutorial/views/auth.py | 49 ++++++++++++ .../wiki2/src/tests/tutorial/views/default.py | 37 --------- .../wiki2/src/tests/tutorial/views/errors.py | 14 ---- .../wiki2/src/tests/tutorial/views/notfound.py | 7 ++ .../wiki2/src/views/tutorial/templates/404.jinja2 | 2 +- .../wiki2/src/views/tutorial/templates/edit.jinja2 | 88 +++++----------------- .../src/views/tutorial/templates/layout.jinja2 | 19 +---- .../src/views/tutorial/templates/mytemplate.jinja2 | 8 -- .../wiki2/src/views/tutorial/templates/view.jinja2 | 82 ++++---------------- .../wiki2/src/views/tutorial/views/errors.py | 5 -- .../wiki2/src/views/tutorial/views/notfound.py | 7 ++ 24 files changed, 184 insertions(+), 426 deletions(-) delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.jinja2 create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/views/errors.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/views/notfound.py delete mode 100644 docs/tutorials/wiki2/src/basiclayout/tutorial/views/errors.py create mode 100644 docs/tutorials/wiki2/src/basiclayout/tutorial/views/notfound.py delete mode 100644 docs/tutorials/wiki2/src/models/tutorial/views/errors.py create mode 100644 docs/tutorials/wiki2/src/models/tutorial/views/notfound.py delete mode 100644 docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 delete mode 100644 docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.jinja2 create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/views/auth.py delete mode 100644 docs/tutorials/wiki2/src/tests/tutorial/views/errors.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/views/notfound.py delete mode 100644 docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.jinja2 delete mode 100644 docs/tutorials/wiki2/src/views/tutorial/views/errors.py create mode 100644 docs/tutorials/wiki2/src/views/tutorial/views/notfound.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 deleted file mode 100644 index ff624c65b..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - Alchemy Scaffold for The Pyramid Web Framework - - - - - - - - - - - - - -
-
-
-
- -
-
- {% block content %} -

No content

- {% endblock content %} -
-
-
- -
-
- -
-
-
- - - - - - - - diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.jinja2 deleted file mode 100644 index bb622bf5a..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.jinja2 +++ /dev/null @@ -1,8 +0,0 @@ -{% extends "layout.jinja2" %} - -{% block content %} -
-

Pyramid Alchemy scaffold

-

Welcome to {{project}}, an application generated by
the Pyramid Web Framework 1.7.dev0.

-
-{% endblock content %} diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py new file mode 100644 index 000000000..08aa2bfad --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py @@ -0,0 +1,49 @@ +from pyramid.httpexceptions import HTTPFound +from pyramid.security import ( + remember, + forget, + ) +from pyramid.view import ( + forbidden_view_config, + view_config, +) + +from ..security.default import USERS + + +@view_config(route_name='login', renderer='templates/login.jinja2') +def login(request): + login_url = request.route_url('login') + referrer = request.url + if referrer == login_url: + referrer = '/' # never use the login form itself as came_from + came_from = request.params.get('came_from', referrer) + message = '' + login = '' + password = '' + if 'form.submitted' in request.params: + login = request.params['login'] + password = request.params['password'] + if USERS.get(login) == password: + headers = remember(request, login) + return HTTPFound(location=came_from, headers=headers) + message = 'Failed login' + + return dict( + message=message, + url=request.route_url('login'), + came_from=came_from, + login=login, + password=password, + ) + +@view_config(route_name='logout') +def logout(request): + headers = forget(request) + next_url = request.route_url('view_wiki') + return HTTPFound(location=next_url, headers=headers) + +@forbidden_view_config() +def forbidden_view(request): + next_url = request.route_url('login', _query={'came_from': request.url}) + return HTTPFound(location=next_url) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py index aa77facd7..6fb3c8744 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py @@ -6,17 +6,9 @@ from pyramid.httpexceptions import ( HTTPFound, HTTPNotFound, ) -from pyramid.view import ( - view_config, - forbidden_view_config, - ) -from pyramid.security import ( - remember, - forget, - ) +from pyramid.view import view_config from ..models import Page -from ..security.default import USERS # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") @@ -76,37 +68,3 @@ def edit_page(request): page=page, save_url=request.route_url('edit_page', pagename=pagename), ) - - -@view_config(route_name='login', renderer='templates/login.jinja2') -@forbidden_view_config(renderer='templates/login.jinja2') -def login(request): - login_url = request.route_url('login') - referrer = request.url - if referrer == login_url: - referrer = '/' # never use the login form itself as came_from - came_from = request.params.get('came_from', referrer) - message = '' - login = '' - password = '' - if 'form.submitted' in request.params: - login = request.params['login'] - password = request.params['password'] - if USERS.get(login) == password: - headers = remember(request, login) - return HTTPFound(location=came_from, headers=headers) - message = 'Failed login' - - return dict( - message=message, - url=request.route_url('login'), - came_from=came_from, - login=login, - password=password, - ) - -@view_config(route_name='logout') -def logout(request): - headers = forget(request) - next_url = request.route_url('view_wiki') - return HTTPFound(location=next_url, headers=headers) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/errors.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/errors.py deleted file mode 100644 index a4b8201f1..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/views/errors.py +++ /dev/null @@ -1,5 +0,0 @@ -from pyramid.view import notfound_view_config - -@notfound_view_config(renderer='../templates/404.jinja2') -def notfound_view(request): - return {} diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/notfound.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/notfound.py new file mode 100644 index 000000000..69d6e2804 --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/notfound.py @@ -0,0 +1,7 @@ +from pyramid.view import notfound_view_config + + +@notfound_view_config(renderer='../templates/404.jinja2') +def notfound_view(request): + request.response.status = 404 + return {} diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/views/errors.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/views/errors.py deleted file mode 100644 index a4b8201f1..000000000 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/views/errors.py +++ /dev/null @@ -1,5 +0,0 @@ -from pyramid.view import notfound_view_config - -@notfound_view_config(renderer='../templates/404.jinja2') -def notfound_view(request): - return {} diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/views/notfound.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/views/notfound.py new file mode 100644 index 000000000..69d6e2804 --- /dev/null +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/views/notfound.py @@ -0,0 +1,7 @@ +from pyramid.view import notfound_view_config + + +@notfound_view_config(renderer='../templates/404.jinja2') +def notfound_view(request): + request.response.status = 404 + return {} diff --git a/docs/tutorials/wiki2/src/models/tutorial/views/errors.py b/docs/tutorials/wiki2/src/models/tutorial/views/errors.py deleted file mode 100644 index a4b8201f1..000000000 --- a/docs/tutorials/wiki2/src/models/tutorial/views/errors.py +++ /dev/null @@ -1,5 +0,0 @@ -from pyramid.view import notfound_view_config - -@notfound_view_config(renderer='../templates/404.jinja2') -def notfound_view(request): - return {} diff --git a/docs/tutorials/wiki2/src/models/tutorial/views/notfound.py b/docs/tutorials/wiki2/src/models/tutorial/views/notfound.py new file mode 100644 index 000000000..69d6e2804 --- /dev/null +++ b/docs/tutorials/wiki2/src/models/tutorial/views/notfound.py @@ -0,0 +1,7 @@ +from pyramid.view import notfound_view_config + + +@notfound_view_config(renderer='../templates/404.jinja2') +def notfound_view(request): + request.response.status = 404 + return {} diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 deleted file mode 100644 index ff624c65b..000000000 --- a/docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - Alchemy Scaffold for The Pyramid Web Framework - - - - - - - - - - - - - -
-
-
-
- -
-
- {% block content %} -

No content

- {% endblock content %} -
-
-
- -
-
- -
-
-
- - - - - - - - diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.jinja2 deleted file mode 100644 index bb622bf5a..000000000 --- a/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.jinja2 +++ /dev/null @@ -1,8 +0,0 @@ -{% extends "layout.jinja2" %} - -{% block content %} -
-

Pyramid Alchemy scaffold

-

Welcome to {{project}}, an application generated by
the Pyramid Web Framework 1.7.dev0.

-
-{% endblock content %} diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py index b25d9a332..c716537ae 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py @@ -5,12 +5,15 @@ from webtest import TestApp class FunctionalTests(unittest.TestCase): - viewer_login = '/login?login=viewer&password=viewer' \ - '&came_from=FrontPage&form.submitted=Login' - viewer_wrong_login = '/login?login=viewer&password=incorrect' \ - '&came_from=FrontPage&form.submitted=Login' - editor_login = '/login?login=editor&password=editor' \ - '&came_from=FrontPage&form.submitted=Login' + viewer_login = ( + '/login?login=viewer&password=viewer' + '&came_from=FrontPage&form.submitted=Login') + viewer_wrong_login = ( + '/login?login=viewer&password=incorrect' + '&came_from=FrontPage&form.submitted=Login') + editor_login = ( + '/login?login=editor&password=editor' + '&came_from=FrontPage&form.submitted=Login') @classmethod def setUpClass(cls): diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/auth.py b/docs/tutorials/wiki2/src/tests/tutorial/views/auth.py new file mode 100644 index 000000000..08aa2bfad --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/views/auth.py @@ -0,0 +1,49 @@ +from pyramid.httpexceptions import HTTPFound +from pyramid.security import ( + remember, + forget, + ) +from pyramid.view import ( + forbidden_view_config, + view_config, +) + +from ..security.default import USERS + + +@view_config(route_name='login', renderer='templates/login.jinja2') +def login(request): + login_url = request.route_url('login') + referrer = request.url + if referrer == login_url: + referrer = '/' # never use the login form itself as came_from + came_from = request.params.get('came_from', referrer) + message = '' + login = '' + password = '' + if 'form.submitted' in request.params: + login = request.params['login'] + password = request.params['password'] + if USERS.get(login) == password: + headers = remember(request, login) + return HTTPFound(location=came_from, headers=headers) + message = 'Failed login' + + return dict( + message=message, + url=request.route_url('login'), + came_from=came_from, + login=login, + password=password, + ) + +@view_config(route_name='logout') +def logout(request): + headers = forget(request) + next_url = request.route_url('view_wiki') + return HTTPFound(location=next_url, headers=headers) + +@forbidden_view_config() +def forbidden_view(request): + next_url = request.route_url('login', _query={'came_from': request.url}) + return HTTPFound(location=next_url) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py index bc8a59fe8..6fb3c8744 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py @@ -6,14 +6,9 @@ from pyramid.httpexceptions import ( HTTPFound, HTTPNotFound, ) -from pyramid.security import ( - remember, - forget, - ) from pyramid.view import view_config from ..models import Page -from ..security.default import USERS # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") @@ -73,35 +68,3 @@ def edit_page(request): page=page, save_url=request.route_url('edit_page', pagename=pagename), ) - -@view_config(route_name='login', renderer='templates/login.jinja2') -def login(request): - login_url = request.route_url('login') - referrer = request.url - if referrer == login_url: - referrer = '/' # never use the login form itself as came_from - came_from = request.params.get('came_from', referrer) - message = '' - login = '' - password = '' - if 'form.submitted' in request.params: - login = request.params['login'] - password = request.params['password'] - if USERS.get(login) == password: - headers = remember(request, login) - return HTTPFound(location=came_from, headers=headers) - message = 'Failed login' - - return dict( - message=message, - url=request.route_url('login'), - came_from=came_from, - login=login, - password=password, - ) - -@view_config(route_name='logout') -def logout(request): - headers = forget(request) - next_url = request.route_url('view_wiki') - return HTTPFound(location=next_url, headers=headers) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/errors.py b/docs/tutorials/wiki2/src/tests/tutorial/views/errors.py deleted file mode 100644 index f8dbe4e05..000000000 --- a/docs/tutorials/wiki2/src/tests/tutorial/views/errors.py +++ /dev/null @@ -1,14 +0,0 @@ -from pyramid.httpexceptions import HTTPFound -from pyramid.view import ( - forbidden_view_config, - notfound_view_config, -) - -@notfound_view_config(renderer='../templates/404.jinja2') -def notfound_view(request): - return {} - -@forbidden_view_config() -def forbidden_view(request): - next_url = request.route_url('login', _query={'came_from': request.url}) - return HTTPFound(location=next_url) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/notfound.py b/docs/tutorials/wiki2/src/tests/tutorial/views/notfound.py new file mode 100644 index 000000000..69d6e2804 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/views/notfound.py @@ -0,0 +1,7 @@ +from pyramid.view import notfound_view_config + + +@notfound_view_config(renderer='../templates/404.jinja2') +def notfound_view(request): + request.response.status = 404 + return {} diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/404.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/404.jinja2 index 1917f83c7..37b0a16b6 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/templates/404.jinja2 +++ b/docs/tutorials/wiki2/src/views/tutorial/templates/404.jinja2 @@ -2,7 +2,7 @@ {% block content %}
-

Pyramid Alchemy scaffold

+

Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)

404 Page Not Found

{% endblock content %} diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 index a41d232e5..e47b3aabf 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 +++ b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 @@ -1,68 +1,20 @@ - - - - - - - - - - - Edit{% if page.name %} {{page.name}}{% endif %} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) - - - - - - - - - - - - - -
-
-
-
- -
-
-
-

- Editing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} -

-

You can return to the - FrontPage. -

- -
- -
-
- -
- -
-
-
-
- -
-
-
- - - - - - - - +{% extends 'layout.jinja2' %} + +{% block title %}Edit {{page.name}} - {% endblock title %} + +{% block content %} +

+Editing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} +

+

You can return to the +FrontPage. +

+
+
+ +
+
+ +
+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2 index ff624c65b..68743e0df 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2 +++ b/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2 @@ -8,7 +8,7 @@ - Alchemy Scaffold for The Pyramid Web Framework + {% block title %}{% if page.name %} {{page.name}} - {% endif %}{% endblock title %}Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) @@ -32,20 +32,9 @@
- {% block content %} -

No content

- {% endblock content %} -
-
-
-
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.jinja2 deleted file mode 100644 index bb622bf5a..000000000 --- a/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.jinja2 +++ /dev/null @@ -1,8 +0,0 @@ -{% extends "layout.jinja2" %} - -{% block content %} -
-

Pyramid Alchemy scaffold

-

Welcome to {{project}}, an application generated by
the Pyramid Web Framework 1.7.dev0.

-
-{% endblock content %} diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 index fa09baf70..c582ce1f9 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 +++ b/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 @@ -1,66 +1,16 @@ - - - - - - - - - - - {{page.name}} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) - - - - - - - - - - - - - -
-
-
-
- -
-
-
-

{{ content|safe }}

-

- - Edit this page - -

-

- Viewing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} -

-

You can return to the - FrontPage. -

-
-
-
-
- -
-
-
- - - - - - - - +{% extends 'layout.jinja2' %} + +{% block content %} +

{{ content|safe }}

+

+ + Edit this page + +

+

+ Viewing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} +

+

You can return to the +FrontPage. +

+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/views/tutorial/views/errors.py b/docs/tutorials/wiki2/src/views/tutorial/views/errors.py deleted file mode 100644 index a4b8201f1..000000000 --- a/docs/tutorials/wiki2/src/views/tutorial/views/errors.py +++ /dev/null @@ -1,5 +0,0 @@ -from pyramid.view import notfound_view_config - -@notfound_view_config(renderer='../templates/404.jinja2') -def notfound_view(request): - return {} diff --git a/docs/tutorials/wiki2/src/views/tutorial/views/notfound.py b/docs/tutorials/wiki2/src/views/tutorial/views/notfound.py new file mode 100644 index 000000000..69d6e2804 --- /dev/null +++ b/docs/tutorials/wiki2/src/views/tutorial/views/notfound.py @@ -0,0 +1,7 @@ +from pyramid.view import notfound_view_config + + +@notfound_view_config(renderer='../templates/404.jinja2') +def notfound_view(request): + request.response.status = 404 + return {} -- cgit v1.2.3 From e01b270c451ce6f23b53181ad79b430666ebe003 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Wed, 10 Feb 2016 23:45:32 -0600 Subject: explain the base layout.jinja2 template and notfound view --- docs/tutorials/wiki2/src/views/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/views/tutorial/views/default.py b/docs/tutorials/wiki2/src/views/tutorial/views/default.py index 96df85a97..ca37f39f5 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/views/tutorial/views/default.py @@ -24,7 +24,7 @@ def view_page(request): pagename = request.matchdict['pagename'] page = request.dbsession.query(Page).filter_by(name=pagename).first() if page is None: - return HTTPNotFound('No such page') + raise HTTPNotFound('No such page') def check(match): word = match.group(1) -- cgit v1.2.3 From 9a7cfe3b4e248451750f5694255450bf1983e848 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Wed, 10 Feb 2016 23:54:51 -0600 Subject: update 404 templates --- docs/tutorials/wiki2/src/authorization/tutorial/templates/404.jinja2 | 2 +- docs/tutorials/wiki2/src/tests/tutorial/templates/404.jinja2 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/404.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/404.jinja2 index 1917f83c7..37b0a16b6 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/404.jinja2 +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/404.jinja2 @@ -2,7 +2,7 @@ {% block content %}
-

Pyramid Alchemy scaffold

+

Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)

404 Page Not Found

{% endblock content %} diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/404.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/404.jinja2 index 1917f83c7..37b0a16b6 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/templates/404.jinja2 +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/404.jinja2 @@ -2,7 +2,7 @@ {% block content %}
-

Pyramid Alchemy scaffold

+

Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)

404 Page Not Found

{% endblock content %} -- cgit v1.2.3 From f2e9c68e8168cfe51f7dc5ed86fea0471968f508 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Wed, 10 Feb 2016 23:55:03 -0600 Subject: move security into one place --- .../wiki2/src/authorization/tutorial/__init__.py | 11 +---- .../src/authorization/tutorial/models/mymodel.py | 14 ------ .../wiki2/src/authorization/tutorial/security.py | 51 ++++++++++++++++++++++ .../authorization/tutorial/security/__init__.py | 0 .../src/authorization/tutorial/security/default.py | 12 ----- 5 files changed, 52 insertions(+), 36 deletions(-) create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/security.py delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/security/default.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py index a62c42378..8eacdee5a 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py @@ -1,22 +1,13 @@ from pyramid.config import Configurator -from pyramid.authentication import AuthTktAuthenticationPolicy -from pyramid.authorization import ACLAuthorizationPolicy - -from .security.default import groupfinder def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ - authn_policy = AuthTktAuthenticationPolicy( - 'sosecret', callback=groupfinder, hashalg='sha512') - authz_policy = ACLAuthorizationPolicy() config = Configurator(settings=settings) config.include('pyramid_jinja2') config.include('.models') - config.set_root_factory('.models.mymodel.RootFactory') - config.set_authentication_policy(authn_policy) - config.set_authorization_policy(authz_policy) + config.include('.security') config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('view_wiki', '/') config.add_route('login', '/login') diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py index 25209c745..b23d0c0d2 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py @@ -1,7 +1,3 @@ -from pyramid.security import ( - Allow, - Everyone, -) from sqlalchemy import ( Column, Integer, @@ -17,13 +13,3 @@ class Page(Base): id = Column(Integer, primary_key=True) name = Column(Text, unique=True) data = Column(Integer) - - -class RootFactory(object): - __acl__ = [ - (Allow, Everyone, 'view'), - (Allow, 'group:editors', 'edit'), - ] - - def __init__(self, request): - pass diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/security.py b/docs/tutorials/wiki2/src/authorization/tutorial/security.py new file mode 100644 index 000000000..7bceabf3f --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/security.py @@ -0,0 +1,51 @@ +from pyramid.authentication import AuthTktAuthenticationPolicy +from pyramid.authorization import ACLAuthorizationPolicy + +from pyramid.security import ( + Allow, + Authenticated, + Everyone, +) + + +USERS = { + 'editor': 'editor', + 'viewer': 'viewer', +} + +GROUPS = { + 'editor': ['group:editors'], +} + +class MyAuthenticationPolicy(AuthTktAuthenticationPolicy): + def authenticated_userid(self, request): + userid = self.unauthenticated_userid(request) + if userid in USERS: + return userid + + def effective_principals(self, request): + principals = [Everyone] + userid = self.authenticated_userid(request) + if userid is not None: + principals.append(Authenticated) + principals.append(userid) + + groups = GROUPS.get(userid, []) + principals.extend(groups) + return principals + +class RootFactory(object): + __acl__ = [ + (Allow, Everyone, 'view'), + (Allow, 'group:editors', 'edit'), + ] + + def __init__(self, request): + pass + +def includeme(config): + authn_policy = MyAuthenticationPolicy('sosecret', hashalg='sha512') + authz_policy = ACLAuthorizationPolicy() + config.set_root_factory(RootFactory) + config.set_authentication_policy(authn_policy) + config.set_authorization_policy(authz_policy) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/security/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/security/default.py b/docs/tutorials/wiki2/src/authorization/tutorial/security/default.py deleted file mode 100644 index 7fc1ea7c8..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/security/default.py +++ /dev/null @@ -1,12 +0,0 @@ -USERS = { - 'editor': 'editor', - 'viewer': 'viewer', -} - -GROUPS = { - 'editor': ['group:editors'], -} - -def groupfinder(userid, request): - if userid in USERS: - return GROUPS.get(userid, []) -- cgit v1.2.3 From cb5a84802171ed22b67958c7733cc0eddc680d34 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 11 Feb 2016 23:01:38 -0600 Subject: copy layout and templates from views to authorization --- .../authorization/tutorial/templates/edit.jinja2 | 93 +++++-------------- .../authorization/tutorial/templates/layout.jinja2 | 60 +++++++++++++ .../authorization/tutorial/templates/login.jinja2 | 100 ++++++--------------- .../authorization/tutorial/templates/view.jinja2 | 87 ++++-------------- .../src/authorization/tutorial/views/default.py | 2 +- .../src/views/tutorial/templates/layout.jinja2 | 5 ++ 6 files changed, 128 insertions(+), 219 deletions(-) create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 index 4d767cfbe..e47b3aabf 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 @@ -1,73 +1,20 @@ - - - - - - - - - - - Edit{% if page.name %} {{page.name}}{% endif %} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) - - - - - - - - - - - - - -
-
-
-
- -
-
-
- {% if request.authenticated_userid is not none %} -

- Logout -

- {% endif %} -

- Editing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} -

-

You can return to the - FrontPage. -

-
-
- -
-
- -
-
-
-
-
-
- -
-
-
- - - - - - - - +{% extends 'layout.jinja2' %} + +{% block title %}Edit {{page.name}} - {% endblock title %} + +{% block content %} +

+Editing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} +

+

You can return to the +FrontPage. +

+
+
+ +
+
+ +
+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 new file mode 100644 index 000000000..82a144abf --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 @@ -0,0 +1,60 @@ + + + + + + + + + + + {% block title %}{% if page.name %} {{page.name}} - {% endif %}{% endblock title %}Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+ {% if request.authenticated_userid is not none %} +

+ Logout +

+ {% endif %} + {% block content %}{% endblock %} +
+
+
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2 index a80a2a165..99d369173 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2 +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2 @@ -1,74 +1,26 @@ - - - - - - - - - - - Login - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) - - - - - - - - - - - - - -
-
-
-
- -
-
-
-

- - Login -
- {{ message }} -

-
- -
- - -
-
- - -
-
- -
-
-
-
-
-
- -
-
-
- - - - - - - - +{% extends 'layout.jinja2' %} + +{% block title %}Login - {% endblock title %} + +{% block content %} +

+ + Login +
+{{ message }} +

+
+ +
+ + +
+
+ + +
+
+ +
+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 index 942b8479b..c582ce1f9 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 @@ -1,71 +1,16 @@ - - - - - - - - - - - {{page.name}} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) - - - - - - - - - - - - - -
-
-
-
- -
-
-
- {% if request.authenticated_userid is not none %} -

- Logout -

- {% endif %} -

{{ content|safe }}

-

- - Edit this page - -

-

- Viewing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} -

-

You can return to the - FrontPage. -

-
-
-
-
- -
-
-
- - - - - - - - +{% extends 'layout.jinja2' %} + +{% block content %} +

{{ content|safe }}

+

+ + Edit this page + +

+

+ Viewing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} +

+

You can return to the +FrontPage. +

+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py index 6fb3c8744..e152e73e0 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py @@ -24,7 +24,7 @@ def view_page(request): pagename = request.matchdict['pagename'] page = request.dbsession.query(Page).filter_by(name=pagename).first() if page is None: - return HTTPNotFound('No such page') + raise HTTPNotFound('No such page') def check(match): word = match.group(1) diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2 index 68743e0df..82a144abf 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2 +++ b/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2 @@ -33,6 +33,11 @@
+ {% if request.authenticated_userid is not none %} +

+ Logout +

+ {% endif %} {% block content %}{% endblock %}
-- cgit v1.2.3 From 81e5989ed5b2bd7ea1a2b843dea9726b253b38ce Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Fri, 12 Feb 2016 00:18:40 -0600 Subject: create an actual user model to prepare for security --- .../src/authorization/tutorial/views/default.py | 3 ++- docs/tutorials/wiki2/src/models/setup.py | 1 + .../wiki2/src/models/tutorial/models/__init__.py | 3 ++- .../wiki2/src/models/tutorial/models/mymodel.py | 15 ------------ .../wiki2/src/models/tutorial/models/page.py | 20 ++++++++++++++++ .../wiki2/src/models/tutorial/models/user.py | 28 ++++++++++++++++++++++ .../src/models/tutorial/scripts/initializedb.py | 16 +++++++++++-- 7 files changed, 67 insertions(+), 19 deletions(-) delete mode 100644 docs/tutorials/wiki2/src/models/tutorial/models/mymodel.py create mode 100644 docs/tutorials/wiki2/src/models/tutorial/models/page.py create mode 100644 docs/tutorials/wiki2/src/models/tutorial/models/user.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py index e152e73e0..f74059be0 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py @@ -10,6 +10,7 @@ from pyramid.view import view_config from ..models import Page + # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") @@ -42,7 +43,7 @@ def view_page(request): return dict(page=page, content=content, edit_url=edit_url) @view_config(route_name='add_page', renderer='../templates/edit.jinja2', - permission='edit') + permission='create') def add_page(request): pagename = request.matchdict['pagename'] if 'form.submitted' in request.params: diff --git a/docs/tutorials/wiki2/src/models/setup.py b/docs/tutorials/wiki2/src/models/setup.py index eb771010f..df9fec4d4 100644 --- a/docs/tutorials/wiki2/src/models/setup.py +++ b/docs/tutorials/wiki2/src/models/setup.py @@ -9,6 +9,7 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: CHANGES = f.read() requires = [ + 'bcrypt', 'pyramid', 'pyramid_jinja2', 'pyramid_debugtoolbar', diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py index 3d3efe06f..a8871f6f5 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py @@ -5,7 +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 .mymodel import Page # flake8: 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/models/mymodel.py b/docs/tutorials/wiki2/src/models/tutorial/models/mymodel.py deleted file mode 100644 index b23d0c0d2..000000000 --- a/docs/tutorials/wiki2/src/models/tutorial/models/mymodel.py +++ /dev/null @@ -1,15 +0,0 @@ -from sqlalchemy import ( - Column, - Integer, - Text, -) - -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, unique=True) - data = Column(Integer) diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/page.py b/docs/tutorials/wiki2/src/models/tutorial/models/page.py new file mode 100644 index 000000000..4dd5b5721 --- /dev/null +++ b/docs/tutorials/wiki2/src/models/tutorial/models/page.py @@ -0,0 +1,20 @@ +from sqlalchemy import ( + Column, + ForeignKey, + Integer, + Text, +) +from sqlalchemy.orm import relationship + +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(Integer, nullable=False) + + creator_id = Column(ForeignKey('users.id'), nullable=False) + creator = relationship('User', backref='created_pages') diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/user.py b/docs/tutorials/wiki2/src/models/tutorial/models/user.py new file mode 100644 index 000000000..6123a3aad --- /dev/null +++ b/docs/tutorials/wiki2/src/models/tutorial/models/user.py @@ -0,0 +1,28 @@ +import bcrypt +from sqlalchemy import ( + Column, + Integer, + Text, +) + +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) + + password_hash = Column(Text) + + def set_password(self, pw, pre_hashed=False): + if pre_hashed: + pwhash = pw + else: + pwhash = bcrypt.hashpw(pw, bcrypt.gensalt()) + self.password_hash = pwhash + + def check_password(self, pw): + return bcrypt.hashpw(pw, self.password_hash) == self.password_hash diff --git a/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py index 601a6e73f..175b7190f 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py @@ -15,7 +15,7 @@ from ..models import ( get_session_factory, get_tm_session, ) -from ..models import Page +from ..models import Page, User def usage(argv): @@ -41,5 +41,17 @@ def main(argv=sys.argv): with transaction.manager: dbsession = get_tm_session(session_factory, transaction.manager) - page = Page(name='FrontPage', data='This is the front page') + editor = User(name='editor', role='editor') + editor.set_password('editor') + dbsession.add(editor) + + viewer = User(name='viewer', role='viewer') + viewer.set_password('viewer') + dbsession.add(viewer) + + page = Page( + name='FrontPage', + creator=editor, + data='This is the front page', + ) dbsession.add(page) -- cgit v1.2.3 From e6e4f655f2abe8d1d5ff63ecd70255094af6de73 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Fri, 12 Feb 2016 01:09:01 -0600 Subject: let's go ahead and bite off more than we can chew by adding object-security we'll allow anyone to create pages, not just editors finally we'll allow page creators of pages to edit their pages even if they are not editors --- docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py index 175b7190f..f3c0a6fef 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py @@ -45,9 +45,9 @@ def main(argv=sys.argv): editor.set_password('editor') dbsession.add(editor) - viewer = User(name='viewer', role='viewer') - viewer.set_password('viewer') - dbsession.add(viewer) + basic = User(name='basic', role='basic') + basic.set_password('basic') + dbsession.add(basic) page = Page( name='FrontPage', -- cgit v1.2.3 From 574ba1aa6d81498220d123d149192eeba81afee7 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Fri, 12 Feb 2016 02:22:48 -0600 Subject: update the models chapter with the new user model --- docs/tutorials/wiki2/src/models/tutorial/models/user.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/user.py b/docs/tutorials/wiki2/src/models/tutorial/models/user.py index 6123a3aad..25b0a8187 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/models/user.py +++ b/docs/tutorials/wiki2/src/models/tutorial/models/user.py @@ -17,12 +17,11 @@ class User(Base): password_hash = Column(Text) - def set_password(self, pw, pre_hashed=False): - if pre_hashed: - pwhash = pw - else: - pwhash = bcrypt.hashpw(pw, bcrypt.gensalt()) + def set_password(self, pw): + pwhash = bcrypt.hashpw(pw, bcrypt.gensalt()) self.password_hash = pwhash def check_password(self, pw): - return bcrypt.hashpw(pw, self.password_hash) == self.password_hash + if self.password_hash is not None: + return bcrypt.hashpw(pw, self.password_hash) == self.password_hash + return False -- cgit v1.2.3 From 4872a1e713f894b383990f62cf82c2b21f810c16 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Fri, 12 Feb 2016 02:48:09 -0600 Subject: forward port changes to models / scripts to later chapters --- docs/tutorials/wiki2/src/authorization/setup.py | 3 ++- .../src/authorization/tutorial/models/__init__.py | 3 ++- .../src/authorization/tutorial/models/mymodel.py | 15 ----------- .../src/authorization/tutorial/models/page.py | 20 +++++++++++++++ .../src/authorization/tutorial/models/user.py | 27 ++++++++++++++++++++ .../authorization/tutorial/scripts/initializedb.py | 16 ++++++++++-- docs/tutorials/wiki2/src/tests/setup.py | 3 ++- .../wiki2/src/tests/tutorial/models/__init__.py | 3 ++- .../wiki2/src/tests/tutorial/models/mymodel.py | 29 ---------------------- .../wiki2/src/tests/tutorial/models/page.py | 20 +++++++++++++++ .../wiki2/src/tests/tutorial/models/user.py | 27 ++++++++++++++++++++ .../src/tests/tutorial/scripts/initializedb.py | 16 ++++++++++-- docs/tutorials/wiki2/src/views/setup.py | 3 ++- .../wiki2/src/views/tutorial/models/__init__.py | 3 ++- .../wiki2/src/views/tutorial/models/mymodel.py | 15 ----------- .../wiki2/src/views/tutorial/models/page.py | 20 +++++++++++++++ .../wiki2/src/views/tutorial/models/user.py | 27 ++++++++++++++++++++ .../src/views/tutorial/scripts/initializedb.py | 16 ++++++++++-- 18 files changed, 195 insertions(+), 71 deletions(-) delete mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/models/page.py create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/models/user.py delete mode 100644 docs/tutorials/wiki2/src/tests/tutorial/models/mymodel.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/models/page.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/models/user.py delete mode 100644 docs/tutorials/wiki2/src/views/tutorial/models/mymodel.py create mode 100644 docs/tutorials/wiki2/src/views/tutorial/models/page.py create mode 100644 docs/tutorials/wiki2/src/views/tutorial/models/user.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authorization/setup.py b/docs/tutorials/wiki2/src/authorization/setup.py index d4e5a4072..c342c1aba 100644 --- a/docs/tutorials/wiki2/src/authorization/setup.py +++ b/docs/tutorials/wiki2/src/authorization/setup.py @@ -9,6 +9,8 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: CHANGES = f.read() requires = [ + 'bcrypt', + 'docutils', 'pyramid', 'pyramid_jinja2', 'pyramid_debugtoolbar', @@ -17,7 +19,6 @@ requires = [ 'transaction', 'zope.sqlalchemy', 'waitress', - 'docutils', ] setup(name='tutorial', diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py index 3d3efe06f..a8871f6f5 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py @@ -5,7 +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 .mymodel import Page # flake8: 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/models/mymodel.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py deleted file mode 100644 index b23d0c0d2..000000000 --- a/docs/tutorials/wiki2/src/authorization/tutorial/models/mymodel.py +++ /dev/null @@ -1,15 +0,0 @@ -from sqlalchemy import ( - Column, - Integer, - Text, -) - -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, unique=True) - data = Column(Integer) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/page.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/page.py new file mode 100644 index 000000000..4dd5b5721 --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/page.py @@ -0,0 +1,20 @@ +from sqlalchemy import ( + Column, + ForeignKey, + Integer, + Text, +) +from sqlalchemy.orm import relationship + +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(Integer, nullable=False) + + creator_id = Column(ForeignKey('users.id'), nullable=False) + creator = relationship('User', backref='created_pages') diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py new file mode 100644 index 000000000..25b0a8187 --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py @@ -0,0 +1,27 @@ +import bcrypt +from sqlalchemy import ( + Column, + Integer, + Text, +) + +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) + + password_hash = Column(Text) + + def set_password(self, pw): + pwhash = bcrypt.hashpw(pw, bcrypt.gensalt()) + self.password_hash = pwhash + + def check_password(self, pw): + if self.password_hash is not None: + return bcrypt.hashpw(pw, self.password_hash) == self.password_hash + return False diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py index 601a6e73f..f3c0a6fef 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py @@ -15,7 +15,7 @@ from ..models import ( get_session_factory, get_tm_session, ) -from ..models import Page +from ..models import Page, User def usage(argv): @@ -41,5 +41,17 @@ def main(argv=sys.argv): with transaction.manager: dbsession = get_tm_session(session_factory, transaction.manager) - page = Page(name='FrontPage', data='This is the front page') + 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/setup.py b/docs/tutorials/wiki2/src/tests/setup.py index 93195a68c..e06aa06e4 100644 --- a/docs/tutorials/wiki2/src/tests/setup.py +++ b/docs/tutorials/wiki2/src/tests/setup.py @@ -9,6 +9,8 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: CHANGES = f.read() requires = [ + 'bcrypt', + 'docutils', 'pyramid', 'pyramid_jinja2', 'pyramid_debugtoolbar', @@ -17,7 +19,6 @@ requires = [ 'transaction', 'zope.sqlalchemy', 'waitress', - 'docutils', ] tests_require = [ diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py index 3d3efe06f..a8871f6f5 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py @@ -5,7 +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 .mymodel import Page # flake8: 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/models/mymodel.py b/docs/tutorials/wiki2/src/tests/tutorial/models/mymodel.py deleted file mode 100644 index 25209c745..000000000 --- a/docs/tutorials/wiki2/src/tests/tutorial/models/mymodel.py +++ /dev/null @@ -1,29 +0,0 @@ -from pyramid.security import ( - Allow, - Everyone, -) -from sqlalchemy import ( - Column, - Integer, - Text, -) - -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, unique=True) - data = Column(Integer) - - -class RootFactory(object): - __acl__ = [ - (Allow, Everyone, 'view'), - (Allow, 'group:editors', 'edit'), - ] - - def __init__(self, request): - pass diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models/page.py b/docs/tutorials/wiki2/src/tests/tutorial/models/page.py new file mode 100644 index 000000000..4dd5b5721 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/models/page.py @@ -0,0 +1,20 @@ +from sqlalchemy import ( + Column, + ForeignKey, + Integer, + Text, +) +from sqlalchemy.orm import relationship + +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(Integer, nullable=False) + + creator_id = Column(ForeignKey('users.id'), nullable=False) + creator = relationship('User', backref='created_pages') diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models/user.py b/docs/tutorials/wiki2/src/tests/tutorial/models/user.py new file mode 100644 index 000000000..25b0a8187 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/models/user.py @@ -0,0 +1,27 @@ +import bcrypt +from sqlalchemy import ( + Column, + Integer, + Text, +) + +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) + + password_hash = Column(Text) + + def set_password(self, pw): + pwhash = bcrypt.hashpw(pw, bcrypt.gensalt()) + self.password_hash = pwhash + + def check_password(self, pw): + if self.password_hash is not None: + return bcrypt.hashpw(pw, self.password_hash) == self.password_hash + return False diff --git a/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py index 601a6e73f..f3c0a6fef 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py @@ -15,7 +15,7 @@ from ..models import ( get_session_factory, get_tm_session, ) -from ..models import Page +from ..models import Page, User def usage(argv): @@ -41,5 +41,17 @@ def main(argv=sys.argv): with transaction.manager: dbsession = get_tm_session(session_factory, transaction.manager) - page = Page(name='FrontPage', data='This is the front page') + 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/setup.py b/docs/tutorials/wiki2/src/views/setup.py index d4e5a4072..c342c1aba 100644 --- a/docs/tutorials/wiki2/src/views/setup.py +++ b/docs/tutorials/wiki2/src/views/setup.py @@ -9,6 +9,8 @@ with open(os.path.join(here, 'CHANGES.txt')) as f: CHANGES = f.read() requires = [ + 'bcrypt', + 'docutils', 'pyramid', 'pyramid_jinja2', 'pyramid_debugtoolbar', @@ -17,7 +19,6 @@ requires = [ 'transaction', 'zope.sqlalchemy', 'waitress', - 'docutils', ] setup(name='tutorial', diff --git a/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py index 3d3efe06f..a8871f6f5 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py @@ -5,7 +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 .mymodel import Page # flake8: 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/models/mymodel.py b/docs/tutorials/wiki2/src/views/tutorial/models/mymodel.py deleted file mode 100644 index b23d0c0d2..000000000 --- a/docs/tutorials/wiki2/src/views/tutorial/models/mymodel.py +++ /dev/null @@ -1,15 +0,0 @@ -from sqlalchemy import ( - Column, - Integer, - Text, -) - -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, unique=True) - data = Column(Integer) diff --git a/docs/tutorials/wiki2/src/views/tutorial/models/page.py b/docs/tutorials/wiki2/src/views/tutorial/models/page.py new file mode 100644 index 000000000..4dd5b5721 --- /dev/null +++ b/docs/tutorials/wiki2/src/views/tutorial/models/page.py @@ -0,0 +1,20 @@ +from sqlalchemy import ( + Column, + ForeignKey, + Integer, + Text, +) +from sqlalchemy.orm import relationship + +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(Integer, nullable=False) + + creator_id = Column(ForeignKey('users.id'), nullable=False) + creator = relationship('User', backref='created_pages') diff --git a/docs/tutorials/wiki2/src/views/tutorial/models/user.py b/docs/tutorials/wiki2/src/views/tutorial/models/user.py new file mode 100644 index 000000000..25b0a8187 --- /dev/null +++ b/docs/tutorials/wiki2/src/views/tutorial/models/user.py @@ -0,0 +1,27 @@ +import bcrypt +from sqlalchemy import ( + Column, + Integer, + Text, +) + +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) + + password_hash = Column(Text) + + def set_password(self, pw): + pwhash = bcrypt.hashpw(pw, bcrypt.gensalt()) + self.password_hash = pwhash + + def check_password(self, pw): + if self.password_hash is not None: + return bcrypt.hashpw(pw, self.password_hash) == self.password_hash + return False diff --git a/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py index 601a6e73f..f3c0a6fef 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py @@ -15,7 +15,7 @@ from ..models import ( get_session_factory, get_tm_session, ) -from ..models import Page +from ..models import Page, User def usage(argv): @@ -41,5 +41,17 @@ def main(argv=sys.argv): with transaction.manager: dbsession = get_tm_session(session_factory, transaction.manager) - page = Page(name='FrontPage', data='This is the front page') + 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) -- cgit v1.2.3 From 60891b844c883d2c9ce864522f2202d9514d8d83 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 13 Feb 2016 13:26:05 -0600 Subject: improve the views section by removing quirks and explaining transactions --- .../wiki2/src/views/tutorial/templates/edit.jinja2 | 6 ++-- .../src/views/tutorial/templates/layout.jinja2 | 7 +---- .../wiki2/src/views/tutorial/templates/view.jinja2 | 4 ++- .../wiki2/src/views/tutorial/views/default.py | 36 ++++++++++++---------- 4 files changed, 26 insertions(+), 27 deletions(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 index e47b3aabf..7db25c674 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 +++ b/docs/tutorials/wiki2/src/views/tutorial/templates/edit.jinja2 @@ -1,17 +1,17 @@ {% extends 'layout.jinja2' %} -{% block title %}Edit {{page.name}} - {% endblock title %} +{% block subtitle %}Edit {{pagename}} - {% endblock subtitle %} {% block content %}

-Editing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} +Editing {{pagename}}

You can return to the FrontPage.

- +
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2 index 82a144abf..71785157f 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2 +++ b/docs/tutorials/wiki2/src/views/tutorial/templates/layout.jinja2 @@ -8,7 +8,7 @@ - {% block title %}{% if page.name %} {{page.name}} - {% endif %}{% endblock title %}Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + {% block subtitle %}{% endblock %}Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) @@ -33,11 +33,6 @@
- {% if request.authenticated_userid is not none %} -

- Logout -

- {% endif %} {% block content %}{% endblock %}
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 index c582ce1f9..94419e228 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 +++ b/docs/tutorials/wiki2/src/views/tutorial/templates/view.jinja2 @@ -1,5 +1,7 @@ {% extends 'layout.jinja2' %} +{% block subtitle %}{{page.name}} - {% endblock subtitle %} + {% block content %}

{{ content|safe }}

@@ -8,7 +10,7 @@

- Viewing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} + Viewing {{page.name}}, created by {{page.creator.name}}.

You can return to the FrontPage. diff --git a/docs/tutorials/wiki2/src/views/tutorial/views/default.py b/docs/tutorials/wiki2/src/views/tutorial/views/default.py index ca37f39f5..7a4073b3f 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 +from ..models import Page, User # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") @@ -26,7 +26,7 @@ def view_page(request): if page is None: raise HTTPNotFound('No such page') - def check(match): + def add_link(match): word = match.group(1) exists = request.dbsession.query(Page).filter_by(name=word).all() if exists: @@ -37,23 +37,10 @@ def view_page(request): return '%s' % (add_url, cgi.escape(word)) content = publish_parts(page.data, writer_name='html')['html_body'] - content = wikiwords.sub(check, content) + content = wikiwords.sub(add_link, content) edit_url = request.route_url('edit_page', pagename=pagename) return dict(page=page, content=content, edit_url=edit_url) -@view_config(route_name='add_page', renderer='../templates/edit.jinja2') -def add_page(request): - pagename = request.matchdict['pagename'] - if 'form.submitted' in request.params: - body = request.params['body'] - page = Page(name=pagename, data=body) - request.dbsession.add(page) - next_url = request.route_url('view_page', pagename=pagename) - return HTTPFound(location=next_url) - save_url = request.route_url('add_page', pagename=pagename) - page = Page(name='', data='') - return dict(page=page, save_url=save_url) - @view_config(route_name='edit_page', renderer='../templates/edit.jinja2') def edit_page(request): pagename = request.matchdict['pagename'] @@ -63,6 +50,21 @@ def edit_page(request): next_url = request.route_url('view_page', pagename=pagename) return HTTPFound(location=next_url) return dict( - page=page, + pagename=page.name, + pagedata=page.data, save_url=request.route_url('edit_page', pagename=pagename), ) + +@view_config(route_name='add_page', renderer='../templates/edit.jinja2') +def add_page(request): + pagename = request.matchdict['pagename'] + if 'form.submitted' in request.params: + body = request.params['body'] + page = Page(name=pagename, data=body) + page.creator = ( + request.dbsession.query(User).filter_by(name='editor').one()) + request.dbsession.add(page) + next_url = request.route_url('view_page', pagename=pagename) + return HTTPFound(location=next_url) + save_url = request.route_url('add_page', pagename=pagename) + return dict(pagename=pagename, pagedata='', save_url=save_url) -- cgit v1.2.3 From 42c93166fbe676341b1d94ec3659ae772dd073d8 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 14 Feb 2016 17:35:01 -0600 Subject: fix unicode issues with check_password --- docs/tutorials/wiki2/src/models/tutorial/models/user.py | 6 ++++-- docs/tutorials/wiki2/src/views/tutorial/models/user.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/user.py b/docs/tutorials/wiki2/src/models/tutorial/models/user.py index 25b0a8187..6fb32a1b2 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/models/user.py +++ b/docs/tutorials/wiki2/src/models/tutorial/models/user.py @@ -18,10 +18,12 @@ class User(Base): password_hash = Column(Text) def set_password(self, pw): - pwhash = bcrypt.hashpw(pw, bcrypt.gensalt()) + pwhash = bcrypt.hashpw(pw.encode('utf8'), bcrypt.gensalt()) self.password_hash = pwhash def check_password(self, pw): if self.password_hash is not None: - return bcrypt.hashpw(pw, self.password_hash) == self.password_hash + expected_hash = self.password_hash.encode('utf8') + actual_hash = bcrypt.hashpw(pw.encode('utf8'), expected_hash) + return expected_hash == actual_hash return False diff --git a/docs/tutorials/wiki2/src/views/tutorial/models/user.py b/docs/tutorials/wiki2/src/views/tutorial/models/user.py index 25b0a8187..6fb32a1b2 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/models/user.py +++ b/docs/tutorials/wiki2/src/views/tutorial/models/user.py @@ -18,10 +18,12 @@ class User(Base): password_hash = Column(Text) def set_password(self, pw): - pwhash = bcrypt.hashpw(pw, bcrypt.gensalt()) + pwhash = bcrypt.hashpw(pw.encode('utf8'), bcrypt.gensalt()) self.password_hash = pwhash def check_password(self, pw): if self.password_hash is not None: - return bcrypt.hashpw(pw, self.password_hash) == self.password_hash + expected_hash = self.password_hash.encode('utf8') + actual_hash = bcrypt.hashpw(pw.encode('utf8'), expected_hash) + return expected_hash == actual_hash return False -- cgit v1.2.3 From da5ebc28c38ea32ad99389b5bc23e2f847af8047 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 14 Feb 2016 17:40:03 -0600 Subject: split routes into a separate module --- docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py | 3 +-- docs/tutorials/wiki2/src/basiclayout/tutorial/routes.py | 3 +++ docs/tutorials/wiki2/src/models/tutorial/__init__.py | 3 +-- docs/tutorials/wiki2/src/models/tutorial/routes.py | 3 +++ docs/tutorials/wiki2/src/views/tutorial/__init__.py | 6 +----- docs/tutorials/wiki2/src/views/tutorial/routes.py | 6 ++++++ 6 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 docs/tutorials/wiki2/src/basiclayout/tutorial/routes.py create mode 100644 docs/tutorials/wiki2/src/models/tutorial/routes.py create mode 100644 docs/tutorials/wiki2/src/views/tutorial/routes.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py index 17763812a..4dab44823 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py @@ -7,7 +7,6 @@ def main(global_config, **settings): config = Configurator(settings=settings) config.include('pyramid_jinja2') config.include('.models') - config.add_static_view('static', 'static', cache_max_age=3600) - config.add_route('home', '/') + config.include('.routes') config.scan() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/routes.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/routes.py new file mode 100644 index 000000000..25504ad4d --- /dev/null +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/routes.py @@ -0,0 +1,3 @@ +def includeme(config): + config.add_static_view('static', 'static', cache_max_age=3600) + config.add_route('home', '/') diff --git a/docs/tutorials/wiki2/src/models/tutorial/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/__init__.py index 17763812a..4dab44823 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/models/tutorial/__init__.py @@ -7,7 +7,6 @@ def main(global_config, **settings): config = Configurator(settings=settings) config.include('pyramid_jinja2') config.include('.models') - config.add_static_view('static', 'static', cache_max_age=3600) - config.add_route('home', '/') + config.include('.routes') config.scan() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/src/models/tutorial/routes.py b/docs/tutorials/wiki2/src/models/tutorial/routes.py new file mode 100644 index 000000000..25504ad4d --- /dev/null +++ b/docs/tutorials/wiki2/src/models/tutorial/routes.py @@ -0,0 +1,3 @@ +def includeme(config): + config.add_static_view('static', 'static', cache_max_age=3600) + config.add_route('home', '/') diff --git a/docs/tutorials/wiki2/src/views/tutorial/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/__init__.py index 5d8c7fba2..4dab44823 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/views/tutorial/__init__.py @@ -7,10 +7,6 @@ def main(global_config, **settings): config = Configurator(settings=settings) config.include('pyramid_jinja2') config.include('.models') - config.add_static_view('static', 'static', cache_max_age=3600) - config.add_route('view_wiki', '/') - config.add_route('view_page', '/{pagename}') - config.add_route('add_page', '/add_page/{pagename}') - config.add_route('edit_page', '/{pagename}/edit_page') + config.include('.routes') config.scan() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/src/views/tutorial/routes.py b/docs/tutorials/wiki2/src/views/tutorial/routes.py new file mode 100644 index 000000000..72df58efe --- /dev/null +++ b/docs/tutorials/wiki2/src/views/tutorial/routes.py @@ -0,0 +1,6 @@ +def includeme(config): + config.add_static_view('static', 'static', cache_max_age=3600) + config.add_route('view_wiki', '/') + config.add_route('view_page', '/{pagename}') + config.add_route('add_page', '/add_page/{pagename}') + config.add_route('edit_page', '/{pagename}/edit_page') -- cgit v1.2.3 From 00b2c691f88fcf42dfc81222aed939833f7f1f05 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 14 Feb 2016 17:47:48 -0600 Subject: implement the authentication example code --- .../tutorials/wiki2/src/authentication/CHANGES.txt | 4 + .../tutorials/wiki2/src/authentication/MANIFEST.in | 2 + docs/tutorials/wiki2/src/authentication/README.txt | 14 ++ .../wiki2/src/authentication/development.ini | 73 ++++++++++ .../wiki2/src/authentication/production.ini | 62 +++++++++ docs/tutorials/wiki2/src/authentication/setup.py | 49 +++++++ .../wiki2/src/authentication/tutorial/__init__.py | 13 ++ .../src/authentication/tutorial/models/__init__.py | 74 ++++++++++ .../src/authentication/tutorial/models/meta.py | 16 +++ .../src/authentication/tutorial/models/page.py | 20 +++ .../src/authentication/tutorial/models/user.py | 29 ++++ .../wiki2/src/authentication/tutorial/routes.py | 8 ++ .../authentication/tutorial/scripts/__init__.py | 1 + .../tutorial/scripts/initializedb.py | 57 ++++++++ .../wiki2/src/authentication/tutorial/security.py | 28 ++++ .../tutorial/static/pyramid-16x16.png | Bin 0 -> 1319 bytes .../src/authentication/tutorial/static/pyramid.png | Bin 0 -> 12901 bytes .../src/authentication/tutorial/static/theme.css | 154 +++++++++++++++++++++ .../authentication/tutorial/static/theme.min.css | 1 + .../authentication/tutorial/templates/404.jinja2 | 8 ++ .../authentication/tutorial/templates/edit.jinja2 | 20 +++ .../tutorial/templates/layout.jinja2 | 64 +++++++++ .../authentication/tutorial/templates/login.jinja2 | 26 ++++ .../authentication/tutorial/templates/view.jinja2 | 18 +++ .../wiki2/src/authentication/tutorial/tests.py | 65 +++++++++ .../src/authentication/tutorial/views/__init__.py | 0 .../src/authentication/tutorial/views/auth.py | 44 ++++++ .../src/authentication/tutorial/views/default.py | 76 ++++++++++ .../src/authentication/tutorial/views/notfound.py | 7 + 29 files changed, 933 insertions(+) create mode 100644 docs/tutorials/wiki2/src/authentication/CHANGES.txt create mode 100644 docs/tutorials/wiki2/src/authentication/MANIFEST.in create mode 100644 docs/tutorials/wiki2/src/authentication/README.txt create mode 100644 docs/tutorials/wiki2/src/authentication/development.ini create mode 100644 docs/tutorials/wiki2/src/authentication/production.ini create mode 100644 docs/tutorials/wiki2/src/authentication/setup.py create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/__init__.py create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/models/__init__.py create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/models/meta.py create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/models/page.py create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/models/user.py create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/routes.py create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/scripts/__init__.py create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/scripts/initializedb.py create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/security.py create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/static/pyramid-16x16.png create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/static/pyramid.png create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/static/theme.css create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/static/theme.min.css create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/templates/404.jinja2 create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/templates/edit.jinja2 create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/templates/layout.jinja2 create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/templates/login.jinja2 create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/templates/view.jinja2 create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/tests.py create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/views/__init__.py create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/views/auth.py create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/views/default.py create mode 100644 docs/tutorials/wiki2/src/authentication/tutorial/views/notfound.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authentication/CHANGES.txt b/docs/tutorials/wiki2/src/authentication/CHANGES.txt new file mode 100644 index 000000000..35a34f332 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/CHANGES.txt @@ -0,0 +1,4 @@ +0.0 +--- + +- Initial version diff --git a/docs/tutorials/wiki2/src/authentication/MANIFEST.in b/docs/tutorials/wiki2/src/authentication/MANIFEST.in new file mode 100644 index 000000000..42cd299b5 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/MANIFEST.in @@ -0,0 +1,2 @@ +include *.txt *.ini *.cfg *.rst +recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.jinja2 *.pt *.txt *.mak *.mako *.js *.html *.xml diff --git a/docs/tutorials/wiki2/src/authentication/README.txt b/docs/tutorials/wiki2/src/authentication/README.txt new file mode 100644 index 000000000..68f430110 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/README.txt @@ -0,0 +1,14 @@ +tutorial README +================== + +Getting Started +--------------- + +- cd + +- $VENV/bin/python setup.py develop + +- $VENV/bin/initialize_tutorial_db development.ini + +- $VENV/bin/pserve development.ini + diff --git a/docs/tutorials/wiki2/src/authentication/development.ini b/docs/tutorials/wiki2/src/authentication/development.ini new file mode 100644 index 000000000..f3079727e --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/development.ini @@ -0,0 +1,73 @@ +### +# app configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +### + +[app:main] +use = egg:tutorial + +pyramid.reload_templates = true +pyramid.debug_authorization = false +pyramid.debug_notfound = false +pyramid.debug_routematch = false +pyramid.default_locale_name = en +pyramid.includes = + pyramid_debugtoolbar + pyramid_tm + +sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite + +auth.secret = seekrit + +# By default, the toolbar only appears for clients from IP addresses +# '127.0.0.1' and '::1'. +# debugtoolbar.hosts = 127.0.0.1 ::1 + +### +# wsgi server configuration +### + +[server:main] +use = egg:waitress#main +host = 127.0.0.1 +port = 6543 + +### +# logging configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html +### + +[loggers] +keys = root, tutorial, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_tutorial] +level = DEBUG +handlers = +qualname = tutorial + +[logger_sqlalchemy] +level = INFO +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/tutorials/wiki2/src/authentication/production.ini b/docs/tutorials/wiki2/src/authentication/production.ini new file mode 100644 index 000000000..686dba48a --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/production.ini @@ -0,0 +1,62 @@ +### +# app configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +### + +[app:main] +use = egg:tutorial + +pyramid.reload_templates = false +pyramid.debug_authorization = false +pyramid.debug_notfound = false +pyramid.debug_routematch = false +pyramid.default_locale_name = en + +sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite + +auth.secret = real-seekrit + +[server:main] +use = egg:waitress#main +host = 0.0.0.0 +port = 6543 + +### +# logging configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html +### + +[loggers] +keys = root, tutorial, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console + +[logger_tutorial] +level = WARN +handlers = +qualname = tutorial + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/tutorials/wiki2/src/authentication/setup.py b/docs/tutorials/wiki2/src/authentication/setup.py new file mode 100644 index 000000000..c342c1aba --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/setup.py @@ -0,0 +1,49 @@ +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 = [ + 'bcrypt', + 'docutils', + 'pyramid', + 'pyramid_jinja2', + 'pyramid_debugtoolbar', + 'pyramid_tm', + 'SQLAlchemy', + 'transaction', + 'zope.sqlalchemy', + 'waitress', + ] + +setup(name='tutorial', + version='0.0', + description='tutorial', + 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 wsgi bfg pylons pyramid', + packages=find_packages(), + include_package_data=True, + zip_safe=False, + test_suite='tutorial', + install_requires=requires, + entry_points="""\ + [paste.app_factory] + main = tutorial:main + [console_scripts] + initialize_tutorial_db = tutorial.scripts.initializedb:main + """, + ) diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/__init__.py b/docs/tutorials/wiki2/src/authentication/tutorial/__init__.py new file mode 100644 index 000000000..f5c033b8b --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/__init__.py @@ -0,0 +1,13 @@ +from pyramid.config import Configurator + + +def main(global_config, **settings): + """ This function returns a Pyramid WSGI application. + """ + config = Configurator(settings=settings) + config.include('pyramid_jinja2') + config.include('.models') + config.include('.routes') + config.include('.security') + config.scan() + return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/authentication/tutorial/models/__init__.py new file mode 100644 index 000000000..a8871f6f5 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/models/__init__.py @@ -0,0 +1,74 @@ +from sqlalchemy import engine_from_config +from sqlalchemy.orm import sessionmaker +from sqlalchemy.orm import configure_mappers +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 # flake8: noqa +from .user import User # flake8: noqa + +# run configure_mappers after defining all of the models to ensure +# all relationships can be setup +configure_mappers() + + +def get_engine(settings, prefix='sqlalchemy.'): + return engine_from_config(settings, prefix) + + +def get_session_factory(engine): + factory = sessionmaker() + factory.configure(bind=engine) + return factory + + +def get_tm_session(session_factory, transaction_manager): + """ + Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. + + This function will hook the session to the transaction manager which + will take care of committing any changes. + + - When using pyramid_tm it will automatically be committed or aborted + depending on whether an exception is raised. + + - When using scripts you should wrap the session in a manager yourself. + For example:: + + import transaction + + engine = get_engine(settings) + session_factory = get_session_factory(engine) + with transaction.manager: + dbsession = get_tm_session(session_factory, transaction.manager) + + """ + dbsession = session_factory() + zope.sqlalchemy.register( + dbsession, transaction_manager=transaction_manager) + return dbsession + + +def includeme(config): + """ + Initialize the model for a Pyramid app. + + Activate this setup using ``config.include('tutorial.models')``. + + """ + settings = config.get_settings() + + # use pyramid_tm to hook the transaction lifecycle to the request + config.include('pyramid_tm') + + session_factory = get_session_factory(get_engine(settings)) + config.registry['dbsession_factory'] = session_factory + + # make request.dbsession available for use in Pyramid + config.add_request_method( + # r.tm is the transaction manager used by pyramid_tm + lambda r: get_tm_session(session_factory, r.tm), + 'dbsession', + reify=True + ) diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/models/meta.py b/docs/tutorials/wiki2/src/authentication/tutorial/models/meta.py new file mode 100644 index 000000000..fc3e8f1dd --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/models/meta.py @@ -0,0 +1,16 @@ +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.schema import MetaData + +# Recommended naming convention used by Alembic, as various different database +# providers will autogenerate vastly different names making migrations more +# difficult. See: http://alembic.readthedocs.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) diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/models/page.py b/docs/tutorials/wiki2/src/authentication/tutorial/models/page.py new file mode 100644 index 000000000..4dd5b5721 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/models/page.py @@ -0,0 +1,20 @@ +from sqlalchemy import ( + Column, + ForeignKey, + Integer, + Text, +) +from sqlalchemy.orm import relationship + +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(Integer, nullable=False) + + creator_id = Column(ForeignKey('users.id'), nullable=False) + creator = relationship('User', backref='created_pages') diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/models/user.py b/docs/tutorials/wiki2/src/authentication/tutorial/models/user.py new file mode 100644 index 000000000..6fb32a1b2 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/models/user.py @@ -0,0 +1,29 @@ +import bcrypt +from sqlalchemy import ( + Column, + Integer, + Text, +) + +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) + + password_hash = Column(Text) + + def set_password(self, pw): + pwhash = bcrypt.hashpw(pw.encode('utf8'), bcrypt.gensalt()) + self.password_hash = pwhash + + def check_password(self, pw): + if self.password_hash is not None: + expected_hash = self.password_hash.encode('utf8') + actual_hash = bcrypt.hashpw(pw.encode('utf8'), expected_hash) + return expected_hash == actual_hash + return False diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/routes.py b/docs/tutorials/wiki2/src/authentication/tutorial/routes.py new file mode 100644 index 000000000..cb747244f --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/routes.py @@ -0,0 +1,8 @@ +def includeme(config): + config.add_static_view('static', 'static', cache_max_age=3600) + config.add_route('view_wiki', '/') + config.add_route('login', '/login') + config.add_route('logout', '/logout') + config.add_route('view_page', '/{pagename}') + config.add_route('add_page', '/add_page/{pagename}') + config.add_route('edit_page', '/{pagename}/edit_page') diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/scripts/__init__.py b/docs/tutorials/wiki2/src/authentication/tutorial/scripts/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/scripts/__init__.py @@ -0,0 +1 @@ +# package diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/authentication/tutorial/scripts/initializedb.py new file mode 100644 index 000000000..f3c0a6fef --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/scripts/initializedb.py @@ -0,0 +1,57 @@ +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 [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/security.py b/docs/tutorials/wiki2/src/authentication/tutorial/security.py new file mode 100644 index 000000000..24035c8b9 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/security.py @@ -0,0 +1,28 @@ +from pyramid.authentication import AuthTktAuthenticationPolicy +from pyramid.authorization import ACLAuthorizationPolicy + +from .models import User + + +class MyAuthenticationPolicy(AuthTktAuthenticationPolicy): + def authenticated_userid(self, request): + user = request.user + if user is not None: + return user.id + +def get_user(request): + user_id = request.unauthenticated_userid + if user_id is not None: + user = request.dbsession.query(User).get(user_id) + return user + +def includeme(config): + settings = config.get_settings() + authn_policy = MyAuthenticationPolicy( + settings['auth.secret'], + hashalg='sha512', + ) + + config.set_authentication_policy(authn_policy) + config.set_authorization_policy(ACLAuthorizationPolicy()) + config.add_request_method(get_user, 'user', reify=True) diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/static/pyramid-16x16.png b/docs/tutorials/wiki2/src/authentication/tutorial/static/pyramid-16x16.png new file mode 100644 index 000000000..979203112 Binary files /dev/null and b/docs/tutorials/wiki2/src/authentication/tutorial/static/pyramid-16x16.png differ diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/static/pyramid.png b/docs/tutorials/wiki2/src/authentication/tutorial/static/pyramid.png new file mode 100644 index 000000000..4ab837be9 Binary files /dev/null and b/docs/tutorials/wiki2/src/authentication/tutorial/static/pyramid.png differ diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/static/theme.css b/docs/tutorials/wiki2/src/authentication/tutorial/static/theme.css new file mode 100644 index 000000000..0f4b1a4d4 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/static/theme.css @@ -0,0 +1,154 @@ +@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700); +body { + font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 300; + color: #ffffff; + background: #bc2131; +} +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 300; +} +p { + font-weight: 300; +} +.font-normal { + font-weight: 400; +} +.font-semi-bold { + font-weight: 600; +} +.font-bold { + font-weight: 700; +} +.starter-template { + margin-top: 250px; +} +.starter-template .content { + margin-left: 10px; +} +.starter-template .content h1 { + margin-top: 10px; + font-size: 60px; +} +.starter-template .content h1 .smaller { + font-size: 40px; + color: #f2b7bd; +} +.starter-template .content .lead { + font-size: 25px; + color: #f2b7bd; +} +.starter-template .content .lead .font-normal { + color: #ffffff; +} +.starter-template .links { + float: right; + right: 0; + margin-top: 125px; +} +.starter-template .links ul { + display: block; + padding: 0; + margin: 0; +} +.starter-template .links ul li { + list-style: none; + display: inline; + margin: 0 10px; +} +.starter-template .links ul li:first-child { + margin-left: 0; +} +.starter-template .links ul li:last-child { + margin-right: 0; +} +.starter-template .links ul li.current-version { + color: #f2b7bd; + font-weight: 400; +} +.starter-template .links ul li a, a { + color: #f2b7bd; + text-decoration: underline; +} +.starter-template .links ul li a:hover, a:hover { + color: #ffffff; + text-decoration: underline; +} +.starter-template .links ul li .icon-muted { + color: #eb8b95; + margin-right: 5px; +} +.starter-template .links ul li:hover .icon-muted { + color: #ffffff; +} +.starter-template .copyright { + margin-top: 10px; + font-size: 0.9em; + color: #f2b7bd; + text-transform: lowercase; + float: right; + right: 0; +} +@media (max-width: 1199px) { + .starter-template .content h1 { + font-size: 45px; + } + .starter-template .content h1 .smaller { + font-size: 30px; + } + .starter-template .content .lead { + font-size: 20px; + } +} +@media (max-width: 991px) { + .starter-template { + margin-top: 0; + } + .starter-template .logo { + margin: 40px auto; + } + .starter-template .content { + margin-left: 0; + text-align: center; + } + .starter-template .content h1 { + margin-bottom: 20px; + } + .starter-template .links { + float: none; + text-align: center; + margin-top: 60px; + } + .starter-template .copyright { + float: none; + text-align: center; + } +} +@media (max-width: 767px) { + .starter-template .content h1 .smaller { + font-size: 25px; + display: block; + } + .starter-template .content .lead { + font-size: 16px; + } + .starter-template .links { + margin-top: 40px; + } + .starter-template .links ul li { + display: block; + margin: 0; + } + .starter-template .links ul li .icon-muted { + display: none; + } + .starter-template .copyright { + margin-top: 20px; + } +} diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/static/theme.min.css b/docs/tutorials/wiki2/src/authentication/tutorial/static/theme.min.css new file mode 100644 index 000000000..0d25de5b6 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/static/theme.min.css @@ -0,0 +1 @@ +@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a,a{color:#f2b7bd;text-decoration:underline}.starter-template .links ul li a:hover,a:hover{color:#fff;text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/templates/404.jinja2 b/docs/tutorials/wiki2/src/authentication/tutorial/templates/404.jinja2 new file mode 100644 index 000000000..37b0a16b6 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/templates/404.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +

+

Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)

+

404 Page Not Found

+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/authentication/tutorial/templates/edit.jinja2 new file mode 100644 index 000000000..7db25c674 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/templates/edit.jinja2 @@ -0,0 +1,20 @@ +{% extends 'layout.jinja2' %} + +{% block subtitle %}Edit {{pagename}} - {% endblock subtitle %} + +{% block content %} +

+Editing {{pagename}} +

+

You can return to the +FrontPage. +

+ +
+ +
+
+ +
+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/authentication/tutorial/templates/layout.jinja2 new file mode 100644 index 000000000..44d14304e --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/templates/layout.jinja2 @@ -0,0 +1,64 @@ + + + + + + + + + + + {% block subtitle %}{% endblock %}Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+ {% if request.user is none %} +

+ Login +

+ {% else %} +

+ {{request.user.name}} Logout +

+ {% endif %} + {% block content %}{% endblock %} +
+
+
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/templates/login.jinja2 b/docs/tutorials/wiki2/src/authentication/tutorial/templates/login.jinja2 new file mode 100644 index 000000000..1806de0ff --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/templates/login.jinja2 @@ -0,0 +1,26 @@ +{% extends 'layout.jinja2' %} + +{% block title %}Login - {% endblock title %} + +{% block content %} +

+ + Login +
+{{ message }} +

+
+ +
+ + +
+
+ + +
+
+ +
+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/authentication/tutorial/templates/view.jinja2 new file mode 100644 index 000000000..94419e228 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/templates/view.jinja2 @@ -0,0 +1,18 @@ +{% extends 'layout.jinja2' %} + +{% block subtitle %}{{page.name}} - {% endblock subtitle %} + +{% block content %} +

{{ content|safe }}

+

+ + Edit this page + +

+

+ Viewing {{page.name}}, created by {{page.creator.name}}. +

+

You can return to the +FrontPage. +

+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/tests.py b/docs/tutorials/wiki2/src/authentication/tutorial/tests.py new file mode 100644 index 000000000..c54945c28 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/tests.py @@ -0,0 +1,65 @@ +import unittest +import transaction + +from pyramid import testing + + +def dummy_request(dbsession): + return testing.DummyRequest(dbsession=dbsession) + + +class BaseTest(unittest.TestCase): + def setUp(self): + self.config = testing.setUp(settings={ + 'sqlalchemy.url': 'sqlite:///:memory:' + }) + self.config.include('.models') + settings = self.config.get_settings() + + from .models import ( + get_engine, + get_session_factory, + get_tm_session, + ) + + self.engine = get_engine(settings) + session_factory = get_session_factory(self.engine) + + self.session = get_tm_session(session_factory, transaction.manager) + + def init_database(self): + from .models import Base + Base.metadata.create_all(self.engine) + + def tearDown(self): + from .models.meta import Base + + testing.tearDown() + transaction.abort() + Base.metadata.drop_all(self.engine) + + +class TestMyViewSuccessCondition(BaseTest): + + def setUp(self): + super(TestMyViewSuccessCondition, self).setUp() + self.init_database() + + from .models import MyModel + + model = MyModel(name='one', value=55) + self.session.add(model) + + def test_passing_view(self): + from .views.default import my_view + info = my_view(dummy_request(self.session)) + self.assertEqual(info['one'].name, 'one') + self.assertEqual(info['project'], 'tutorial') + + +class TestMyViewFailureCondition(BaseTest): + + def test_failing_view(self): + from .views.default import my_view + info = my_view(dummy_request(self.session)) + self.assertEqual(info.status_int, 500) diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/views/__init__.py b/docs/tutorials/wiki2/src/authentication/tutorial/views/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/views/auth.py b/docs/tutorials/wiki2/src/authentication/tutorial/views/auth.py new file mode 100644 index 000000000..d3db34132 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/views/auth.py @@ -0,0 +1,44 @@ +from pyramid.httpexceptions import HTTPFound +from pyramid.security import ( + remember, + forget, + ) +from pyramid.view import ( + forbidden_view_config, + view_config, +) + +from ..models import User + + +@view_config(route_name='login', renderer='../templates/login.jinja2') +def login(request): + next_url = request.params.get('next', request.referrer) + message = '' + login = '' + if 'form.submitted' in request.params: + login = request.params['login'] + password = request.params['password'] + user = request.dbsession.query(User).filter_by(name=login).first() + if user is not None and user.check_password(password): + headers = remember(request, user.id) + return HTTPFound(location=next_url, headers=headers) + message = 'Failed login' + + return dict( + message=message, + url=request.route_url('login'), + next_url=next_url, + login=login, + ) + +@view_config(route_name='logout') +def logout(request): + headers = forget(request) + next_url = request.route_url('view_wiki') + return HTTPFound(location=next_url, headers=headers) + +@forbidden_view_config() +def forbidden_view(request): + next_url = request.route_url('login', _query={'next': request.url}) + return HTTPFound(location=next_url) diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py b/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py new file mode 100644 index 000000000..55aa74d04 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py @@ -0,0 +1,76 @@ +import cgi +import re +from docutils.core import publish_parts + +from pyramid.httpexceptions import ( + HTTPForbidden, + HTTPFound, + HTTPNotFound, + ) + +from pyramid.view import view_config + +from ..models import Page + +# regular expression used to find WikiWords +wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") + +@view_config(route_name='view_wiki') +def view_wiki(request): + next_url = request.route_url('view_page', pagename='FrontPage') + return HTTPFound(location=next_url) + +@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() + 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() + if exists: + view_url = request.route_url('view_page', pagename=word) + return '%s' % (view_url, cgi.escape(word)) + else: + add_url = request.route_url('add_page', pagename=word) + return '%s' % (add_url, cgi.escape(word)) + + content = publish_parts(page.data, writer_name='html')['html_body'] + content = wikiwords.sub(add_link, content) + edit_url = request.route_url('edit_page', pagename=pagename) + return dict(page=page, content=content, edit_url=edit_url) + +@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() + user = request.user + if user is None or (user.role != 'editor' and page.creator != user): + raise HTTPForbidden + if 'form.submitted' in request.params: + page.data = request.params['body'] + next_url = request.route_url('view_page', pagename=pagename) + return HTTPFound(location=next_url) + return dict( + pagename=page.name, + pagedata=page.data, + save_url=request.route_url('edit_page', pagename=pagename), + ) + +@view_config(route_name='add_page', renderer='../templates/edit.jinja2') +def add_page(request): + user = request.user + if user is None or user.role not in ('editor', 'basic'): + raise HTTPForbidden + pagename = request.matchdict['pagename'] + if 'form.submitted' in request.params: + body = request.params['body'] + page = Page(name=pagename, data=body) + page.creator = request.user + request.dbsession.add(page) + next_url = request.route_url('view_page', pagename=pagename) + return HTTPFound(location=next_url) + save_url = request.route_url('add_page', pagename=pagename) + return dict(pagename=pagename, pagedata='', save_url=save_url) diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/views/notfound.py b/docs/tutorials/wiki2/src/authentication/tutorial/views/notfound.py new file mode 100644 index 000000000..69d6e2804 --- /dev/null +++ b/docs/tutorials/wiki2/src/authentication/tutorial/views/notfound.py @@ -0,0 +1,7 @@ +from pyramid.view import notfound_view_config + + +@notfound_view_config(renderer='../templates/404.jinja2') +def notfound_view(request): + request.response.status = 404 + return {} -- cgit v1.2.3 From 38b40761f1ba31773aca64ae600428516c98534c Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Tue, 16 Feb 2016 23:23:08 -0600 Subject: use page.name to prepare for context --- docs/tutorials/wiki2/src/authentication/tutorial/views/default.py | 6 +++--- docs/tutorials/wiki2/src/views/tutorial/views/default.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (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 55aa74d04..ffe967f6e 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py @@ -39,7 +39,7 @@ def view_page(request): content = publish_parts(page.data, writer_name='html')['html_body'] content = wikiwords.sub(add_link, content) - edit_url = request.route_url('edit_page', pagename=pagename) + edit_url = request.route_url('edit_page', pagename=page.name) return dict(page=page, content=content, edit_url=edit_url) @view_config(route_name='edit_page', renderer='../templates/edit.jinja2') @@ -51,12 +51,12 @@ def edit_page(request): raise HTTPForbidden if 'form.submitted' in request.params: page.data = request.params['body'] - next_url = request.route_url('view_page', pagename=pagename) + next_url = request.route_url('view_page', pagename=page.name) return HTTPFound(location=next_url) return dict( pagename=page.name, pagedata=page.data, - save_url=request.route_url('edit_page', pagename=pagename), + save_url=request.route_url('edit_page', pagename=page.name), ) @view_config(route_name='add_page', renderer='../templates/edit.jinja2') diff --git a/docs/tutorials/wiki2/src/views/tutorial/views/default.py b/docs/tutorials/wiki2/src/views/tutorial/views/default.py index 7a4073b3f..c1d402f6a 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/views/tutorial/views/default.py @@ -38,7 +38,7 @@ def view_page(request): content = publish_parts(page.data, writer_name='html')['html_body'] content = wikiwords.sub(add_link, content) - edit_url = request.route_url('edit_page', pagename=pagename) + edit_url = request.route_url('edit_page', pagename=page.name) return dict(page=page, content=content, edit_url=edit_url) @view_config(route_name='edit_page', renderer='../templates/edit.jinja2') @@ -47,12 +47,12 @@ def edit_page(request): page = request.dbsession.query(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=pagename) + next_url = request.route_url('view_page', pagename=page.name) return HTTPFound(location=next_url) return dict( pagename=page.name, pagedata=page.data, - save_url=request.route_url('edit_page', pagename=pagename), + save_url=request.route_url('edit_page', pagename=page.name), ) @view_config(route_name='add_page', renderer='../templates/edit.jinja2') -- cgit v1.2.3 From f2c43689b50152d55ddc98e8f56754ee61f9a8c7 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Tue, 16 Feb 2016 23:23:23 -0600 Subject: remove whitespace --- docs/tutorials/wiki2/src/authentication/tutorial/security.py | 1 - 1 file changed, 1 deletion(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/security.py b/docs/tutorials/wiki2/src/authentication/tutorial/security.py index 24035c8b9..8ea3858d2 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/security.py +++ b/docs/tutorials/wiki2/src/authentication/tutorial/security.py @@ -22,7 +22,6 @@ def includeme(config): settings['auth.secret'], hashalg='sha512', ) - config.set_authentication_policy(authn_policy) config.set_authorization_policy(ACLAuthorizationPolicy()) config.add_request_method(get_user, 'user', reify=True) -- cgit v1.2.3 From 2fa90465bfdd213b6ce51ca8de6eaf9b614c283e Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Tue, 16 Feb 2016 23:42:04 -0600 Subject: add first cut at source for authorization chapter --- .../wiki2/src/authorization/development.ini | 2 + .../wiki2/src/authorization/production.ini | 2 + .../wiki2/src/authorization/tutorial/__init__.py | 8 +--- .../src/authorization/tutorial/models/user.py | 6 ++- .../wiki2/src/authorization/tutorial/routes.py | 50 ++++++++++++++++++++ .../wiki2/src/authorization/tutorial/security.py | 51 ++++++++------------- .../authorization/tutorial/templates/edit.jinja2 | 6 +-- .../authorization/tutorial/templates/layout.jinja2 | 10 ++-- .../authorization/tutorial/templates/login.jinja2 | 4 +- .../authorization/tutorial/templates/view.jinja2 | 4 +- .../wiki2/src/authorization/tutorial/views/auth.py | 23 ++++------ .../src/authorization/tutorial/views/default.py | 53 ++++++++++------------ 12 files changed, 126 insertions(+), 93 deletions(-) create mode 100644 docs/tutorials/wiki2/src/authorization/tutorial/routes.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authorization/development.ini b/docs/tutorials/wiki2/src/authorization/development.ini index 99c4ff0fe..f3079727e 100644 --- a/docs/tutorials/wiki2/src/authorization/development.ini +++ b/docs/tutorials/wiki2/src/authorization/development.ini @@ -17,6 +17,8 @@ pyramid.includes = sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite +auth.secret = seekrit + # By default, the toolbar only appears for clients from IP addresses # '127.0.0.1' and '::1'. # debugtoolbar.hosts = 127.0.0.1 ::1 diff --git a/docs/tutorials/wiki2/src/authorization/production.ini b/docs/tutorials/wiki2/src/authorization/production.ini index cb1db3211..686dba48a 100644 --- a/docs/tutorials/wiki2/src/authorization/production.ini +++ b/docs/tutorials/wiki2/src/authorization/production.ini @@ -14,6 +14,8 @@ pyramid.default_locale_name = en sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite +auth.secret = real-seekrit + [server:main] use = egg:waitress#main host = 0.0.0.0 diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py index 8eacdee5a..f5c033b8b 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py @@ -7,13 +7,7 @@ def main(global_config, **settings): config = Configurator(settings=settings) config.include('pyramid_jinja2') config.include('.models') + config.include('.routes') config.include('.security') - config.add_static_view('static', 'static', cache_max_age=3600) - config.add_route('view_wiki', '/') - config.add_route('login', '/login') - config.add_route('logout', '/logout') - config.add_route('view_page', '/{pagename}') - config.add_route('add_page', '/add_page/{pagename}') - config.add_route('edit_page', '/{pagename}/edit_page') config.scan() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py index 25b0a8187..6fb32a1b2 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py @@ -18,10 +18,12 @@ class User(Base): password_hash = Column(Text) def set_password(self, pw): - pwhash = bcrypt.hashpw(pw, bcrypt.gensalt()) + pwhash = bcrypt.hashpw(pw.encode('utf8'), bcrypt.gensalt()) self.password_hash = pwhash def check_password(self, pw): if self.password_hash is not None: - return bcrypt.hashpw(pw, self.password_hash) == self.password_hash + expected_hash = self.password_hash.encode('utf8') + actual_hash = bcrypt.hashpw(pw.encode('utf8'), expected_hash) + return expected_hash == actual_hash return False diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/routes.py b/docs/tutorials/wiki2/src/authorization/tutorial/routes.py new file mode 100644 index 000000000..c7c3a2120 --- /dev/null +++ b/docs/tutorials/wiki2/src/authorization/tutorial/routes.py @@ -0,0 +1,50 @@ +from pyramid.httpexceptions import HTTPNotFound +from pyramid.security import ( + Allow, + Everyone, +) + +from .models import Page + +def includeme(config): + config.add_static_view('static', 'static', cache_max_age=3600) + config.add_route('view_wiki', '/') + config.add_route('login', '/login') + config.add_route('logout', '/logout') + config.add_route('view_page', '/{pagename}', factory=page_factory) + config.add_route('add_page', '/add_page/{pagename}', + factory=new_page_factory) + config.add_route('edit_page', '/{pagename}/edit_page', + factory=page_factory) + +def new_page_factory(request): + pagename = request.matchdict['pagename'] + return NewPage(pagename) + +class NewPage(object): + def __init__(self, pagename): + self.pagename = pagename + + def __acl__(self): + return [ + (Allow, 'role:editor', 'create'), + (Allow, 'role:basic', 'create'), + ] + +def page_factory(request): + pagename = request.matchdict['pagename'] + page = request.dbsession.query(Page).filter_by(name=pagename).first() + if page is None: + raise HTTPNotFound + return PageResource(page) + +class PageResource(object): + def __init__(self, page): + self.page = page + + def __acl__(self): + return [ + (Allow, Everyone, 'view'), + (Allow, 'role:editor', 'edit'), + (Allow, str(self.page.creator_id), 'edit'), + ] diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/security.py b/docs/tutorials/wiki2/src/authorization/tutorial/security.py index 7bceabf3f..25cff7b05 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/security.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/security.py @@ -1,51 +1,40 @@ from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy - from pyramid.security import ( - Allow, Authenticated, Everyone, ) +from .models import User -USERS = { - 'editor': 'editor', - 'viewer': 'viewer', -} - -GROUPS = { - 'editor': ['group:editors'], -} class MyAuthenticationPolicy(AuthTktAuthenticationPolicy): def authenticated_userid(self, request): - userid = self.unauthenticated_userid(request) - if userid in USERS: - return userid + user = request.user + if user is not None: + return user.id def effective_principals(self, request): principals = [Everyone] - userid = self.authenticated_userid(request) - if userid is not None: + user = request.user + if user is not None: principals.append(Authenticated) - principals.append(userid) - - groups = GROUPS.get(userid, []) - principals.extend(groups) + principals.append(str(user.id)) + principals.append('role:' + user.role) return principals -class RootFactory(object): - __acl__ = [ - (Allow, Everyone, 'view'), - (Allow, 'group:editors', 'edit'), - ] - - def __init__(self, request): - pass +def get_user(request): + user_id = request.unauthenticated_userid + if user_id is not None: + user = request.dbsession.query(User).get(user_id) + return user def includeme(config): - authn_policy = MyAuthenticationPolicy('sosecret', hashalg='sha512') - authz_policy = ACLAuthorizationPolicy() - config.set_root_factory(RootFactory) + settings = config.get_settings() + authn_policy = MyAuthenticationPolicy( + settings['auth.secret'], + hashalg='sha512', + ) config.set_authentication_policy(authn_policy) - config.set_authorization_policy(authz_policy) + config.set_authorization_policy(ACLAuthorizationPolicy()) + config.add_request_method(get_user, 'user', reify=True) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 index e47b3aabf..7db25c674 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/edit.jinja2 @@ -1,17 +1,17 @@ {% extends 'layout.jinja2' %} -{% block title %}Edit {{page.name}} - {% endblock title %} +{% block subtitle %}Edit {{pagename}} - {% endblock subtitle %} {% block content %}

-Editing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} +Editing {{pagename}}

You can return to the FrontPage.

- +
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 index 82a144abf..44d14304e 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/layout.jinja2 @@ -8,7 +8,7 @@ - {% block title %}{% if page.name %} {{page.name}} - {% endif %}{% endblock title %}Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + {% block subtitle %}{% endblock %}Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) @@ -33,9 +33,13 @@
- {% if request.authenticated_userid is not none %} + {% if request.user is none %}

- Logout + Login +

+ {% else %} +

+ {{request.user.name}} Logout

{% endif %} {% block content %}{% endblock %} diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2 index 99d369173..1806de0ff 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2 +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/login.jinja2 @@ -10,14 +10,14 @@ {{ message }}

- +
- +
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 index c582ce1f9..94419e228 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 +++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/view.jinja2 @@ -1,5 +1,7 @@ {% extends 'layout.jinja2' %} +{% block subtitle %}{{page.name}} - {% endblock subtitle %} + {% block content %}

{{ content|safe }}

@@ -8,7 +10,7 @@

- Viewing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} + Viewing {{page.name}}, created by {{page.creator.name}}.

You can return to the FrontPage. diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py index 08aa2bfad..d3db34132 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py @@ -8,33 +8,28 @@ from pyramid.view import ( view_config, ) -from ..security.default import USERS +from ..models import User -@view_config(route_name='login', renderer='templates/login.jinja2') +@view_config(route_name='login', renderer='../templates/login.jinja2') def login(request): - login_url = request.route_url('login') - referrer = request.url - if referrer == login_url: - referrer = '/' # never use the login form itself as came_from - came_from = request.params.get('came_from', referrer) + next_url = request.params.get('next', request.referrer) message = '' login = '' - password = '' if 'form.submitted' in request.params: login = request.params['login'] password = request.params['password'] - if USERS.get(login) == password: - headers = remember(request, login) - return HTTPFound(location=came_from, headers=headers) + user = request.dbsession.query(User).filter_by(name=login).first() + if user is not None and user.check_password(password): + headers = remember(request, user.id) + return HTTPFound(location=next_url, headers=headers) message = 'Failed login' return dict( message=message, url=request.route_url('login'), - came_from=came_from, + next_url=next_url, login=login, - password=password, ) @view_config(route_name='logout') @@ -45,5 +40,5 @@ def logout(request): @forbidden_view_config() def forbidden_view(request): - next_url = request.route_url('login', _query={'came_from': request.url}) + next_url = request.route_url('login', _query={'next': request.url}) return HTTPFound(location=next_url) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py index f74059be0..9358993ea 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/default.py @@ -2,19 +2,15 @@ import cgi import re from docutils.core import publish_parts -from pyramid.httpexceptions import ( - HTTPFound, - HTTPNotFound, - ) +from pyramid.httpexceptions import HTTPFound from pyramid.view import view_config from ..models import Page - # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") -@view_config(route_name='view_wiki', permission='view') +@view_config(route_name='view_wiki') def view_wiki(request): next_url = request.route_url('view_page', pagename='FrontPage') return HTTPFound(location=next_url) @@ -22,12 +18,9 @@ def view_wiki(request): @view_config(route_name='view_page', renderer='../templates/view.jinja2', permission='view') def view_page(request): - pagename = request.matchdict['pagename'] - page = request.dbsession.query(Page).filter_by(name=pagename).first() - if page is None: - raise HTTPNotFound('No such page') + page = request.context.page - def check(match): + def add_link(match): word = match.group(1) exists = request.dbsession.query(Page).filter_by(name=word).all() if exists: @@ -38,34 +31,34 @@ def view_page(request): return '%s' % (add_url, cgi.escape(word)) content = publish_parts(page.data, writer_name='html')['html_body'] - content = wikiwords.sub(check, content) - edit_url = request.route_url('edit_page', pagename=pagename) + content = wikiwords.sub(add_link, content) + edit_url = request.route_url('edit_page', pagename=page.name) return dict(page=page, content=content, edit_url=edit_url) +@view_config(route_name='edit_page', renderer='../templates/edit.jinja2', + permission='edit') +def edit_page(request): + page = request.context.page + if 'form.submitted' in request.params: + page.data = request.params['body'] + next_url = request.route_url('view_page', pagename=page.name) + return HTTPFound(location=next_url) + return dict( + pagename=page.name, + pagedata=page.data, + save_url=request.route_url('edit_page', pagename=page.name), + ) + @view_config(route_name='add_page', renderer='../templates/edit.jinja2', permission='create') def add_page(request): - pagename = request.matchdict['pagename'] + pagename = request.context.pagename if 'form.submitted' in request.params: body = request.params['body'] page = Page(name=pagename, data=body) + page.creator = request.user request.dbsession.add(page) next_url = request.route_url('view_page', pagename=pagename) return HTTPFound(location=next_url) save_url = request.route_url('add_page', pagename=pagename) - page = Page(name='', data='') - return dict(page=page, save_url=save_url) - -@view_config(route_name='edit_page', renderer='../templates/edit.jinja2', - permission='edit') -def edit_page(request): - pagename = request.matchdict['pagename'] - page = request.dbsession.query(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=pagename) - return HTTPFound(location=next_url) - return dict( - page=page, - save_url=request.route_url('edit_page', pagename=pagename), - ) + return dict(pagename=pagename, pagedata='', save_url=save_url) -- cgit v1.2.3 From 91f7ed469664bf71f98b6e55ea096f5bdddae953 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 18 Feb 2016 01:53:49 -0600 Subject: add webtest and tests_require to setup.py --- docs/tutorials/wiki2/src/authentication/setup.py | 5 +++++ docs/tutorials/wiki2/src/authorization/setup.py | 5 +++++ docs/tutorials/wiki2/src/basiclayout/setup.py | 5 +++++ docs/tutorials/wiki2/src/models/setup.py | 5 +++++ docs/tutorials/wiki2/src/views/setup.py | 5 +++++ 5 files changed, 25 insertions(+) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authentication/setup.py b/docs/tutorials/wiki2/src/authentication/setup.py index c342c1aba..57538f2d0 100644 --- a/docs/tutorials/wiki2/src/authentication/setup.py +++ b/docs/tutorials/wiki2/src/authentication/setup.py @@ -21,6 +21,10 @@ requires = [ 'waitress', ] +tests_require = [ + 'WebTest', +] + setup(name='tutorial', version='0.0', description='tutorial', @@ -39,6 +43,7 @@ setup(name='tutorial', include_package_data=True, zip_safe=False, test_suite='tutorial', + tests_require=tests_require, install_requires=requires, entry_points="""\ [paste.app_factory] diff --git a/docs/tutorials/wiki2/src/authorization/setup.py b/docs/tutorials/wiki2/src/authorization/setup.py index c342c1aba..57538f2d0 100644 --- a/docs/tutorials/wiki2/src/authorization/setup.py +++ b/docs/tutorials/wiki2/src/authorization/setup.py @@ -21,6 +21,10 @@ requires = [ 'waitress', ] +tests_require = [ + 'WebTest', +] + setup(name='tutorial', version='0.0', description='tutorial', @@ -39,6 +43,7 @@ setup(name='tutorial', include_package_data=True, zip_safe=False, test_suite='tutorial', + tests_require=tests_require, install_requires=requires, entry_points="""\ [paste.app_factory] diff --git a/docs/tutorials/wiki2/src/basiclayout/setup.py b/docs/tutorials/wiki2/src/basiclayout/setup.py index eb771010f..7bc697730 100644 --- a/docs/tutorials/wiki2/src/basiclayout/setup.py +++ b/docs/tutorials/wiki2/src/basiclayout/setup.py @@ -19,6 +19,10 @@ requires = [ 'waitress', ] +tests_require = [ + 'WebTest', +] + setup(name='tutorial', version='0.0', description='tutorial', @@ -37,6 +41,7 @@ setup(name='tutorial', include_package_data=True, zip_safe=False, test_suite='tutorial', + tests_require=tests_require, install_requires=requires, entry_points="""\ [paste.app_factory] diff --git a/docs/tutorials/wiki2/src/models/setup.py b/docs/tutorials/wiki2/src/models/setup.py index df9fec4d4..bdc9ceed7 100644 --- a/docs/tutorials/wiki2/src/models/setup.py +++ b/docs/tutorials/wiki2/src/models/setup.py @@ -20,6 +20,10 @@ requires = [ 'waitress', ] +tests_require = [ + 'WebTest', +] + setup(name='tutorial', version='0.0', description='tutorial', @@ -38,6 +42,7 @@ setup(name='tutorial', include_package_data=True, zip_safe=False, test_suite='tutorial', + tests_require=tests_require, install_requires=requires, entry_points="""\ [paste.app_factory] diff --git a/docs/tutorials/wiki2/src/views/setup.py b/docs/tutorials/wiki2/src/views/setup.py index c342c1aba..57538f2d0 100644 --- a/docs/tutorials/wiki2/src/views/setup.py +++ b/docs/tutorials/wiki2/src/views/setup.py @@ -21,6 +21,10 @@ requires = [ 'waitress', ] +tests_require = [ + 'WebTest', +] + setup(name='tutorial', version='0.0', description='tutorial', @@ -39,6 +43,7 @@ setup(name='tutorial', include_package_data=True, zip_safe=False, test_suite='tutorial', + tests_require=tests_require, install_requires=requires, entry_points="""\ [paste.app_factory] -- cgit v1.2.3 From 50e08a743d097616ef7f76c9689833eab215cb94 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 18 Feb 2016 02:22:26 -0600 Subject: add fallback for next_url --- docs/tutorials/wiki2/src/authentication/tutorial/views/auth.py | 2 ++ docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py | 2 ++ 2 files changed, 4 insertions(+) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/views/auth.py b/docs/tutorials/wiki2/src/authentication/tutorial/views/auth.py index d3db34132..2b993b430 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/views/auth.py +++ b/docs/tutorials/wiki2/src/authentication/tutorial/views/auth.py @@ -14,6 +14,8 @@ from ..models import User @view_config(route_name='login', renderer='../templates/login.jinja2') def login(request): next_url = request.params.get('next', request.referrer) + if not next_url: + next_url = request.route_url('view_wiki') message = '' login = '' if 'form.submitted' in request.params: diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py b/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py index d3db34132..2b993b430 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/views/auth.py @@ -14,6 +14,8 @@ from ..models import User @view_config(route_name='login', renderer='../templates/login.jinja2') def login(request): next_url = request.params.get('next', request.referrer) + if not next_url: + next_url = request.route_url('view_wiki') message = '' login = '' if 'form.submitted' in request.params: -- cgit v1.2.3 From 66fabb4ac707b5b4289db0094756f1a1af7269cc Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 18 Feb 2016 02:32:08 -0600 Subject: update tests chapter --- docs/tutorials/wiki2/src/tests/development.ini | 2 + docs/tutorials/wiki2/src/tests/production.ini | 2 + docs/tutorials/wiki2/src/tests/setup.py | 2 +- .../tutorials/wiki2/src/tests/tutorial/__init__.py | 19 +--- .../wiki2/src/tests/tutorial/models/user.py | 6 +- docs/tutorials/wiki2/src/tests/tutorial/routes.py | 50 +++++++++++ .../tutorials/wiki2/src/tests/tutorial/security.py | 40 +++++++++ .../wiki2/src/tests/tutorial/security/__init__.py | 0 .../wiki2/src/tests/tutorial/security/default.py | 12 --- .../wiki2/src/tests/tutorial/templates/edit.jinja2 | 93 +++++-------------- .../src/tests/tutorial/templates/layout.jinja2 | 64 +++++++++++++ .../src/tests/tutorial/templates/login.jinja2 | 100 ++++++--------------- .../wiki2/src/tests/tutorial/templates/view.jinja2 | 89 ++++-------------- .../src/tests/tutorial/tests/test_functional.py | 56 +++++++----- .../wiki2/src/tests/tutorial/tests/test_views.py | 66 ++++++++------ .../wiki2/src/tests/tutorial/views/auth.py | 25 +++--- .../wiki2/src/tests/tutorial/views/default.py | 54 +++++------ 17 files changed, 341 insertions(+), 339 deletions(-) create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/routes.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/security.py delete mode 100644 docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py delete mode 100644 docs/tutorials/wiki2/src/tests/tutorial/security/default.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/tests/development.ini b/docs/tutorials/wiki2/src/tests/development.ini index 99c4ff0fe..f3079727e 100644 --- a/docs/tutorials/wiki2/src/tests/development.ini +++ b/docs/tutorials/wiki2/src/tests/development.ini @@ -17,6 +17,8 @@ pyramid.includes = sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite +auth.secret = seekrit + # By default, the toolbar only appears for clients from IP addresses # '127.0.0.1' and '::1'. # debugtoolbar.hosts = 127.0.0.1 ::1 diff --git a/docs/tutorials/wiki2/src/tests/production.ini b/docs/tutorials/wiki2/src/tests/production.ini index cb1db3211..686dba48a 100644 --- a/docs/tutorials/wiki2/src/tests/production.ini +++ b/docs/tutorials/wiki2/src/tests/production.ini @@ -14,6 +14,8 @@ pyramid.default_locale_name = en sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite +auth.secret = real-seekrit + [server:main] use = egg:waitress#main host = 0.0.0.0 diff --git a/docs/tutorials/wiki2/src/tests/setup.py b/docs/tutorials/wiki2/src/tests/setup.py index e06aa06e4..57538f2d0 100644 --- a/docs/tutorials/wiki2/src/tests/setup.py +++ b/docs/tutorials/wiki2/src/tests/setup.py @@ -43,8 +43,8 @@ setup(name='tutorial', include_package_data=True, zip_safe=False, test_suite='tutorial', - install_requires=requires, tests_require=tests_require, + install_requires=requires, entry_points="""\ [paste.app_factory] main = tutorial:main diff --git a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py index a62c42378..f5c033b8b 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py @@ -1,28 +1,13 @@ from pyramid.config import Configurator -from pyramid.authentication import AuthTktAuthenticationPolicy -from pyramid.authorization import ACLAuthorizationPolicy - -from .security.default import groupfinder def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ - authn_policy = AuthTktAuthenticationPolicy( - 'sosecret', callback=groupfinder, hashalg='sha512') - authz_policy = ACLAuthorizationPolicy() config = Configurator(settings=settings) config.include('pyramid_jinja2') config.include('.models') - config.set_root_factory('.models.mymodel.RootFactory') - config.set_authentication_policy(authn_policy) - config.set_authorization_policy(authz_policy) - config.add_static_view('static', 'static', cache_max_age=3600) - config.add_route('view_wiki', '/') - config.add_route('login', '/login') - config.add_route('logout', '/logout') - config.add_route('view_page', '/{pagename}') - config.add_route('add_page', '/add_page/{pagename}') - config.add_route('edit_page', '/{pagename}/edit_page') + config.include('.routes') + config.include('.security') config.scan() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models/user.py b/docs/tutorials/wiki2/src/tests/tutorial/models/user.py index 25b0a8187..6fb32a1b2 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/models/user.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/models/user.py @@ -18,10 +18,12 @@ class User(Base): password_hash = Column(Text) def set_password(self, pw): - pwhash = bcrypt.hashpw(pw, bcrypt.gensalt()) + pwhash = bcrypt.hashpw(pw.encode('utf8'), bcrypt.gensalt()) self.password_hash = pwhash def check_password(self, pw): if self.password_hash is not None: - return bcrypt.hashpw(pw, self.password_hash) == self.password_hash + expected_hash = self.password_hash.encode('utf8') + actual_hash = bcrypt.hashpw(pw.encode('utf8'), expected_hash) + return expected_hash == actual_hash return False diff --git a/docs/tutorials/wiki2/src/tests/tutorial/routes.py b/docs/tutorials/wiki2/src/tests/tutorial/routes.py new file mode 100644 index 000000000..c7c3a2120 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/routes.py @@ -0,0 +1,50 @@ +from pyramid.httpexceptions import HTTPNotFound +from pyramid.security import ( + Allow, + Everyone, +) + +from .models import Page + +def includeme(config): + config.add_static_view('static', 'static', cache_max_age=3600) + config.add_route('view_wiki', '/') + config.add_route('login', '/login') + config.add_route('logout', '/logout') + config.add_route('view_page', '/{pagename}', factory=page_factory) + config.add_route('add_page', '/add_page/{pagename}', + factory=new_page_factory) + config.add_route('edit_page', '/{pagename}/edit_page', + factory=page_factory) + +def new_page_factory(request): + pagename = request.matchdict['pagename'] + return NewPage(pagename) + +class NewPage(object): + def __init__(self, pagename): + self.pagename = pagename + + def __acl__(self): + return [ + (Allow, 'role:editor', 'create'), + (Allow, 'role:basic', 'create'), + ] + +def page_factory(request): + pagename = request.matchdict['pagename'] + page = request.dbsession.query(Page).filter_by(name=pagename).first() + if page is None: + raise HTTPNotFound + return PageResource(page) + +class PageResource(object): + def __init__(self, page): + self.page = page + + def __acl__(self): + return [ + (Allow, Everyone, 'view'), + (Allow, 'role:editor', 'edit'), + (Allow, str(self.page.creator_id), 'edit'), + ] diff --git a/docs/tutorials/wiki2/src/tests/tutorial/security.py b/docs/tutorials/wiki2/src/tests/tutorial/security.py new file mode 100644 index 000000000..25cff7b05 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/security.py @@ -0,0 +1,40 @@ +from pyramid.authentication import AuthTktAuthenticationPolicy +from pyramid.authorization import ACLAuthorizationPolicy +from pyramid.security import ( + Authenticated, + Everyone, +) + +from .models import User + + +class MyAuthenticationPolicy(AuthTktAuthenticationPolicy): + def authenticated_userid(self, request): + user = request.user + if user is not None: + return user.id + + def effective_principals(self, request): + principals = [Everyone] + user = request.user + if user is not None: + principals.append(Authenticated) + principals.append(str(user.id)) + principals.append('role:' + user.role) + return principals + +def get_user(request): + user_id = request.unauthenticated_userid + if user_id is not None: + user = request.dbsession.query(User).get(user_id) + return user + +def includeme(config): + settings = config.get_settings() + authn_policy = MyAuthenticationPolicy( + settings['auth.secret'], + hashalg='sha512', + ) + config.set_authentication_policy(authn_policy) + config.set_authorization_policy(ACLAuthorizationPolicy()) + config.add_request_method(get_user, 'user', reify=True) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/tutorials/wiki2/src/tests/tutorial/security/default.py b/docs/tutorials/wiki2/src/tests/tutorial/security/default.py deleted file mode 100644 index 7fc1ea7c8..000000000 --- a/docs/tutorials/wiki2/src/tests/tutorial/security/default.py +++ /dev/null @@ -1,12 +0,0 @@ -USERS = { - 'editor': 'editor', - 'viewer': 'viewer', -} - -GROUPS = { - 'editor': ['group:editors'], -} - -def groupfinder(userid, request): - if userid in USERS: - return GROUPS.get(userid, []) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 index 4d767cfbe..7db25c674 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 @@ -1,73 +1,20 @@ - - - - - - - - - - - Edit{% if page.name %} {{page.name}}{% endif %} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) - - - - - - - - - - - - - -

-
-
-
- -
-
-
- {% if request.authenticated_userid is not none %} -

- Logout -

- {% endif %} -

- Editing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} -

-

You can return to the - FrontPage. -

- -
- -
-
- -
- -
-
-
-
- -
-
-
- - - - - - - - +{% extends 'layout.jinja2' %} + +{% block subtitle %}Edit {{pagename}} - {% endblock subtitle %} + +{% block content %} +

+Editing {{pagename}} +

+

You can return to the +FrontPage. +

+
+
+ +
+
+ +
+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 new file mode 100644 index 000000000..44d14304e --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 @@ -0,0 +1,64 @@ + + + + + + + + + + + {% block subtitle %}{% endblock %}Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+ {% if request.user is none %} +

+ Login +

+ {% else %} +

+ {{request.user.name}} Logout +

+ {% endif %} + {% block content %}{% endblock %} +
+
+
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/login.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/login.jinja2 index a80a2a165..1806de0ff 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/templates/login.jinja2 +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/login.jinja2 @@ -1,74 +1,26 @@ - - - - - - - - - - - Login - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) - - - - - - - - - - - - - -
-
-
-
- -
-
-
-

- - Login -
- {{ message }} -

-
- -
- - -
-
- - -
-
- -
-
-
-
-
-
- -
-
-
- - - - - - - - +{% extends 'layout.jinja2' %} + +{% block title %}Login - {% endblock title %} + +{% block content %} +

+ + Login +
+{{ message }} +

+
+ +
+ + +
+
+ + +
+
+ +
+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 index 942b8479b..94419e228 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 @@ -1,71 +1,18 @@ - - - - - - - - - - - {{page.name}} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) - - - - - - - - - - - - - -
-
-
-
- -
-
-
- {% if request.authenticated_userid is not none %} -

- Logout -

- {% endif %} -

{{ content|safe }}

-

- - Edit this page - -

-

- Viewing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} -

-

You can return to the - FrontPage. -

-
-
-
-
- -
-
-
- - - - - - - - +{% extends 'layout.jinja2' %} + +{% block subtitle %}{{page.name}} - {% endblock subtitle %} + +{% block content %} +

{{ content|safe }}

+

+ + Edit this page + +

+

+ Viewing {{page.name}}, created by {{page.creator.name}}. +

+

You can return to the +FrontPage. +

+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py index c716537ae..b2c6e0975 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py @@ -5,26 +5,30 @@ from webtest import TestApp class FunctionalTests(unittest.TestCase): - viewer_login = ( - '/login?login=viewer&password=viewer' - '&came_from=FrontPage&form.submitted=Login') - viewer_wrong_login = ( - '/login?login=viewer&password=incorrect' - '&came_from=FrontPage&form.submitted=Login') + basic_login = ( + '/login?login=basic&password=basic' + '&next=FrontPage&form.submitted=Login') + basic_wrong_login = ( + '/login?login=basic&password=incorrect' + '&next=FrontPage&form.submitted=Login') editor_login = ( '/login?login=editor&password=editor' - '&came_from=FrontPage&form.submitted=Login') + '&next=FrontPage&form.submitted=Login') @classmethod def setUpClass(cls): from tutorial.models.meta import Base from tutorial.models import ( + User, Page, get_tm_session, ) from tutorial import main - settings = {'sqlalchemy.url': 'sqlite://'} + settings = { + 'sqlalchemy.url': 'sqlite://', + 'auth.secret': 'seekrit', + } app = main({}, **settings) cls.testapp = TestApp(app) @@ -34,8 +38,15 @@ class FunctionalTests(unittest.TestCase): with transaction.manager: dbsession = get_tm_session(session_factory, transaction.manager) - model = Page(name='FrontPage', data='This is the front page') - dbsession.add(model) + editor = User(name='editor', role='editor') + editor.set_password('editor') + basic = User(name='basic', role='basic') + basic.set_password('basic') + page1 = Page(name='FrontPage', data='This is the front page') + page1.creator = editor + page2 = Page(name='BackPage', data='This is the back page') + page2.creator = basic + dbsession.add_all([basic, editor, page1, page2]) @classmethod def tearDownClass(cls): @@ -54,20 +65,20 @@ class FunctionalTests(unittest.TestCase): self.testapp.get('/SomePage', status=404) def test_successful_log_in(self): - res = self.testapp.get(self.viewer_login, status=302) + res = self.testapp.get(self.basic_login, status=302) self.assertEqual(res.location, 'http://localhost/FrontPage') def test_failed_log_in(self): - res = self.testapp.get(self.viewer_wrong_login, status=200) + res = self.testapp.get(self.basic_wrong_login, status=200) self.assertTrue(b'login' in res.body) def test_logout_link_present_when_logged_in(self): - self.testapp.get(self.viewer_login, status=302) + self.testapp.get(self.basic_login, status=302) res = self.testapp.get('/FrontPage', status=200) self.assertTrue(b'Logout' in res.body) def test_logout_link_not_present_after_logged_out(self): - self.testapp.get(self.viewer_login, status=302) + self.testapp.get(self.basic_login, status=302) self.testapp.get('/FrontPage', status=200) res = self.testapp.get('/logout', status=302) self.assertTrue(b'Logout' not in res.body) @@ -80,15 +91,20 @@ class FunctionalTests(unittest.TestCase): res = self.testapp.get('/add_page/NewPage', status=302).follow() self.assertTrue(b'Login' in res.body) - def test_viewer_user_cannot_edit(self): - self.testapp.get(self.viewer_login, status=302) + def test_basic_user_cannot_edit_front(self): + self.testapp.get(self.basic_login, status=302) res = self.testapp.get('/FrontPage/edit_page', status=302).follow() self.assertTrue(b'Login' in res.body) - def test_viewer_user_cannot_add(self): - self.testapp.get(self.viewer_login, status=302) - res = self.testapp.get('/add_page/NewPage', status=302).follow() - self.assertTrue(b'Login' in res.body) + def test_basic_user_can_edit_back(self): + self.testapp.get(self.basic_login, status=302) + res = self.testapp.get('/BackPage/edit_page', status=200) + self.assertTrue(b'Editing' in res.body) + + def test_basic_user_can_add(self): + self.testapp.get(self.basic_login, status=302) + res = self.testapp.get('/add_page/NewPage', status=200) + self.assertTrue(b'Editing' in res.body) def test_editors_member_user_can_edit(self): self.testapp.get(self.editor_login, status=302) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py index b2830d070..5253183df 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py @@ -8,12 +8,6 @@ def dummy_request(dbsession): return testing.DummyRequest(dbsession=dbsession) -def _register_routes(config): - config.add_route('view_page', '{pagename}') - config.add_route('add_page', 'add_page/{pagename}') - config.add_route('edit_page', '{pagename}/edit_page') - - class BaseTest(unittest.TestCase): def setUp(self): from ..models import get_tm_session @@ -21,7 +15,7 @@ class BaseTest(unittest.TestCase): 'sqlalchemy.url': 'sqlite:///:memory:' }) self.config.include('..models') - self.config.include(_register_routes) + self.config.include('..routes') session_factory = self.config.registry['dbsession_factory'] self.session = get_tm_session(session_factory, transaction.manager) @@ -38,11 +32,21 @@ class BaseTest(unittest.TestCase): testing.tearDown() transaction.abort() + def makeUser(self, name, role, password='dummy'): + from ..models import User + user = User(name=name, role=role) + user.set_password(password) + return user + + def makePage(self, name, data, creator): + from ..models import Page + return Page(name=name, data=data, creator=creator) + class ViewWikiTests(unittest.TestCase): def setUp(self): self.config = testing.setUp() - _register_routes(self.config) + self.config.include('..routes') def tearDown(self): testing.tearDown() @@ -63,14 +67,16 @@ class ViewPageTests(BaseTest): return view_page(request) def test_it(self): + from ..routes import PageResource + # add a page to the db - from ..models.mymodel import Page - page = Page(name='IDoExist', data='Hello CruelWorld IDoExist') - self.session.add(page) + user = self.makeUser('foo', 'editor') + page = self.makePage('IDoExist', 'Hello CruelWorld IDoExist', user) + self.session.add_all([page, user]) # create a request asking for the page we've created request = dummy_request(self.session) - request.matchdict['pagename'] = 'IDoExist' + request.context = PageResource(page) # call the view we're testing and check its behavior info = self._callFUT(request) @@ -93,19 +99,23 @@ class AddPageTests(BaseTest): return add_page(request) def test_it_notsubmitted(self): + from ..routes import NewPage request = dummy_request(self.session) - request.matchdict = {'pagename': 'AnotherPage'} + request.user = self.makeUser('foo', 'editor') + request.context = NewPage('AnotherPage') info = self._callFUT(request) - self.assertEqual(info['page'].data, '') + self.assertEqual(info['pagedata'], '') self.assertEqual(info['save_url'], 'http://example.com/add_page/AnotherPage') def test_it_submitted(self): - from ..models.mymodel import Page + from ..models import Page + from ..routes import NewPage request = testing.DummyRequest({'form.submitted': True, 'body': 'Hello yo!'}, dbsession=self.session) - request.matchdict = {'pagename': 'AnotherPage'} + request.user = self.makeUser('foo', 'editor') + request.context = NewPage('AnotherPage') self._callFUT(request) page = self.session.query(Page).filter_by(name='AnotherPage').one() self.assertEqual(page.data, 'Hello yo!') @@ -116,25 +126,31 @@ class EditPageTests(BaseTest): from tutorial.views.default import edit_page return edit_page(request) + def makeContext(self, page): + from ..routes import PageResource + return PageResource(page) + def test_it_notsubmitted(self): - from ..models.mymodel import Page + user = self.makeUser('foo', 'editor') + page = self.makePage('abc', 'hello', user) + self.session.add_all([page, user]) + request = dummy_request(self.session) - request.matchdict = {'pagename': 'abc'} - page = Page(name='abc', data='hello') - self.session.add(page) + request.context = self.makeContext(page) info = self._callFUT(request) - self.assertEqual(info['page'], page) + self.assertEqual(info['pagename'], 'abc') self.assertEqual(info['save_url'], 'http://example.com/abc/edit_page') def test_it_submitted(self): - from ..models.mymodel import Page + user = self.makeUser('foo', 'editor') + page = self.makePage('abc', 'hello', user) + self.session.add_all([page, user]) + request = testing.DummyRequest({'form.submitted': True, 'body': 'Hello yo!'}, dbsession=self.session) - request.matchdict = {'pagename': 'abc'} - page = Page(name='abc', data='hello') - self.session.add(page) + request.context = self.makeContext(page) response = self._callFUT(request) self.assertEqual(response.location, 'http://example.com/abc') self.assertEqual(page.data, 'Hello yo!') diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/auth.py b/docs/tutorials/wiki2/src/tests/tutorial/views/auth.py index 08aa2bfad..2b993b430 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/views/auth.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/views/auth.py @@ -8,33 +8,30 @@ from pyramid.view import ( view_config, ) -from ..security.default import USERS +from ..models import User -@view_config(route_name='login', renderer='templates/login.jinja2') +@view_config(route_name='login', renderer='../templates/login.jinja2') def login(request): - login_url = request.route_url('login') - referrer = request.url - if referrer == login_url: - referrer = '/' # never use the login form itself as came_from - came_from = request.params.get('came_from', referrer) + next_url = request.params.get('next', request.referrer) + if not next_url: + next_url = request.route_url('view_wiki') message = '' login = '' - password = '' if 'form.submitted' in request.params: login = request.params['login'] password = request.params['password'] - if USERS.get(login) == password: - headers = remember(request, login) - return HTTPFound(location=came_from, headers=headers) + user = request.dbsession.query(User).filter_by(name=login).first() + if user is not None and user.check_password(password): + headers = remember(request, user.id) + return HTTPFound(location=next_url, headers=headers) message = 'Failed login' return dict( message=message, url=request.route_url('login'), - came_from=came_from, + next_url=next_url, login=login, - password=password, ) @view_config(route_name='logout') @@ -45,5 +42,5 @@ def logout(request): @forbidden_view_config() def forbidden_view(request): - next_url = request.route_url('login', _query={'came_from': request.url}) + next_url = request.route_url('login', _query={'next': request.url}) return HTTPFound(location=next_url) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py index 6fb3c8744..9358993ea 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py @@ -2,10 +2,7 @@ import cgi import re from docutils.core import publish_parts -from pyramid.httpexceptions import ( - HTTPFound, - HTTPNotFound, - ) +from pyramid.httpexceptions import HTTPFound from pyramid.view import view_config from ..models import Page @@ -13,7 +10,7 @@ from ..models import Page # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") -@view_config(route_name='view_wiki', permission='view') +@view_config(route_name='view_wiki') def view_wiki(request): next_url = request.route_url('view_page', pagename='FrontPage') return HTTPFound(location=next_url) @@ -21,12 +18,9 @@ def view_wiki(request): @view_config(route_name='view_page', renderer='../templates/view.jinja2', permission='view') def view_page(request): - pagename = request.matchdict['pagename'] - page = request.dbsession.query(Page).filter_by(name=pagename).first() - if page is None: - return HTTPNotFound('No such page') + page = request.context.page - def check(match): + def add_link(match): word = match.group(1) exists = request.dbsession.query(Page).filter_by(name=word).all() if exists: @@ -37,34 +31,34 @@ def view_page(request): return '%s' % (add_url, cgi.escape(word)) content = publish_parts(page.data, writer_name='html')['html_body'] - content = wikiwords.sub(check, content) - edit_url = request.route_url('edit_page', pagename=pagename) + content = wikiwords.sub(add_link, content) + edit_url = request.route_url('edit_page', pagename=page.name) return dict(page=page, content=content, edit_url=edit_url) -@view_config(route_name='add_page', renderer='../templates/edit.jinja2', +@view_config(route_name='edit_page', renderer='../templates/edit.jinja2', permission='edit') +def edit_page(request): + page = request.context.page + if 'form.submitted' in request.params: + page.data = request.params['body'] + next_url = request.route_url('view_page', pagename=page.name) + return HTTPFound(location=next_url) + return dict( + pagename=page.name, + pagedata=page.data, + save_url=request.route_url('edit_page', pagename=page.name), + ) + +@view_config(route_name='add_page', renderer='../templates/edit.jinja2', + permission='create') def add_page(request): - pagename = request.matchdict['pagename'] + pagename = request.context.pagename if 'form.submitted' in request.params: body = request.params['body'] page = Page(name=pagename, data=body) + page.creator = request.user request.dbsession.add(page) next_url = request.route_url('view_page', pagename=pagename) return HTTPFound(location=next_url) save_url = request.route_url('add_page', pagename=pagename) - page = Page(name='', data='') - return dict(page=page, save_url=save_url) - -@view_config(route_name='edit_page', renderer='../templates/edit.jinja2', - permission='edit') -def edit_page(request): - pagename = request.matchdict['pagename'] - page = request.dbsession.query(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=pagename) - return HTTPFound(location=next_url) - return dict( - page=page, - save_url=request.route_url('edit_page', pagename=pagename), - ) + return dict(pagename=pagename, pagedata='', save_url=save_url) -- cgit v1.2.3 From 082d3b2cb9127f8acfd4d081e69c427a37bae91d Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sat, 27 Feb 2016 00:59:17 -0800 Subject: wiki2 authentication bug fix and improvement against timing attack - Bytes type does not have encode method. The expected_hash retrieved from the database is a bytes object. - Use hmac.compare_digest instead of == to avoid timing attacks as a recommended security best practice. See https://www.python.org/dev/peps/pep-0466/ https://bugs.python.org/issue21306 and https://codahale.com/a-lesson-in-timing-attacks/ for details. Note, however, this was not backported to py2.6. For a tutorial, I am OK with stating this will not work on Python 2.6 with a clear warning note at the start of the tutorial and on the authentication step. --- docs/tutorials/wiki2/src/authentication/tutorial/models/user.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/models/user.py b/docs/tutorials/wiki2/src/authentication/tutorial/models/user.py index 6fb32a1b2..6499491b2 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/models/user.py +++ b/docs/tutorials/wiki2/src/authentication/tutorial/models/user.py @@ -1,4 +1,5 @@ import bcrypt +import hmac from sqlalchemy import ( Column, Integer, @@ -23,7 +24,7 @@ class User(Base): def check_password(self, pw): if self.password_hash is not None: - expected_hash = self.password_hash.encode('utf8') + expected_hash = self.password_hash actual_hash = bcrypt.hashpw(pw.encode('utf8'), expected_hash) - return expected_hash == actual_hash + return hmac.compare_digest(expected_hash, actual_hash) return False -- cgit v1.2.3 From ee4050ac0a4a60343eb736a90b47113ebfbb60a3 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 28 Feb 2016 01:22:20 -0800 Subject: wiki2 revert unnecessary hmac stuff --- docs/tutorials/wiki2/src/authentication/tutorial/models/user.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/models/user.py b/docs/tutorials/wiki2/src/authentication/tutorial/models/user.py index 6499491b2..6bd3315d6 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/models/user.py +++ b/docs/tutorials/wiki2/src/authentication/tutorial/models/user.py @@ -1,5 +1,4 @@ import bcrypt -import hmac from sqlalchemy import ( Column, Integer, @@ -26,5 +25,5 @@ class User(Base): if self.password_hash is not None: expected_hash = self.password_hash actual_hash = bcrypt.hashpw(pw.encode('utf8'), expected_hash) - return hmac.compare_digest(expected_hash, actual_hash) + return expected_hash == actual_hash return False -- cgit v1.2.3 From a26a08b92f390aeaa3eb0c39241bb5e76f5a72ea Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 28 Feb 2016 01:29:55 -0800 Subject: apply change to all src/*/user.py --- docs/tutorials/wiki2/src/authorization/tutorial/models/user.py | 2 +- docs/tutorials/wiki2/src/models/tutorial/models/user.py | 2 +- docs/tutorials/wiki2/src/tests/tutorial/models/user.py | 2 +- docs/tutorials/wiki2/src/views/tutorial/models/user.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py index 6fb32a1b2..6bd3315d6 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/user.py @@ -23,7 +23,7 @@ class User(Base): def check_password(self, pw): if self.password_hash is not None: - expected_hash = self.password_hash.encode('utf8') + expected_hash = self.password_hash actual_hash = bcrypt.hashpw(pw.encode('utf8'), expected_hash) return expected_hash == actual_hash return False diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/user.py b/docs/tutorials/wiki2/src/models/tutorial/models/user.py index 6fb32a1b2..6bd3315d6 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/models/user.py +++ b/docs/tutorials/wiki2/src/models/tutorial/models/user.py @@ -23,7 +23,7 @@ class User(Base): def check_password(self, pw): if self.password_hash is not None: - expected_hash = self.password_hash.encode('utf8') + expected_hash = self.password_hash actual_hash = bcrypt.hashpw(pw.encode('utf8'), expected_hash) return expected_hash == actual_hash return False diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models/user.py b/docs/tutorials/wiki2/src/tests/tutorial/models/user.py index 6fb32a1b2..6bd3315d6 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/models/user.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/models/user.py @@ -23,7 +23,7 @@ class User(Base): def check_password(self, pw): if self.password_hash is not None: - expected_hash = self.password_hash.encode('utf8') + expected_hash = self.password_hash actual_hash = bcrypt.hashpw(pw.encode('utf8'), expected_hash) return expected_hash == actual_hash return False diff --git a/docs/tutorials/wiki2/src/views/tutorial/models/user.py b/docs/tutorials/wiki2/src/views/tutorial/models/user.py index 6fb32a1b2..6bd3315d6 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/models/user.py +++ b/docs/tutorials/wiki2/src/views/tutorial/models/user.py @@ -23,7 +23,7 @@ class User(Base): def check_password(self, pw): if self.password_hash is not None: - expected_hash = self.password_hash.encode('utf8') + expected_hash = self.password_hash actual_hash = bcrypt.hashpw(pw.encode('utf8'), expected_hash) return expected_hash == actual_hash return False -- cgit v1.2.3 From 3e30040da7c2d5c38b330727b48d9f6b852956d9 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 28 Feb 2016 22:30:22 -0800 Subject: redirect to edit page when user attempts to add page that already exists - update src/*/views/default.py - update src/*/routes.py - write new test - revise docs, double-checking line counts and highlighting --- .../wiki2/src/authentication/tutorial/views/default.py | 3 +++ docs/tutorials/wiki2/src/authorization/tutorial/routes.py | 8 +++++++- docs/tutorials/wiki2/src/tests/tutorial/routes.py | 8 +++++++- docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py | 12 ++++++++++++ docs/tutorials/wiki2/src/views/tutorial/views/default.py | 3 +++ 5 files changed, 32 insertions(+), 2 deletions(-) (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 ffe967f6e..1b071434c 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/authentication/tutorial/views/default.py @@ -65,6 +65,9 @@ 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: + 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) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/routes.py b/docs/tutorials/wiki2/src/authorization/tutorial/routes.py index c7c3a2120..f0a8b7f96 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/routes.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/routes.py @@ -1,4 +1,7 @@ -from pyramid.httpexceptions import HTTPNotFound +from pyramid.httpexceptions import ( + HTTPNotFound, + HTTPFound, +) from pyramid.security import ( Allow, Everyone, @@ -19,6 +22,9 @@ def includeme(config): def new_page_factory(request): pagename = request.matchdict['pagename'] + if request.dbsession.query(Page).filter_by(name=pagename).count() > 0: + next_url = request.route_url('edit_page', pagename=pagename) + raise HTTPFound(location=next_url) return NewPage(pagename) class NewPage(object): diff --git a/docs/tutorials/wiki2/src/tests/tutorial/routes.py b/docs/tutorials/wiki2/src/tests/tutorial/routes.py index c7c3a2120..f0a8b7f96 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/routes.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/routes.py @@ -1,4 +1,7 @@ -from pyramid.httpexceptions import HTTPNotFound +from pyramid.httpexceptions import ( + HTTPNotFound, + HTTPFound, +) from pyramid.security import ( Allow, Everyone, @@ -19,6 +22,9 @@ def includeme(config): def new_page_factory(request): pagename = request.matchdict['pagename'] + if request.dbsession.query(Page).filter_by(name=pagename).count() > 0: + next_url = request.route_url('edit_page', pagename=pagename) + raise HTTPFound(location=next_url) return NewPage(pagename) class NewPage(object): diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py index 5253183df..2c945ab33 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py @@ -98,6 +98,18 @@ class AddPageTests(BaseTest): from tutorial.views.default import add_page return add_page(request) + def test_it_pageexists(self): + from ..models import Page + from ..routes import NewPage + request = testing.DummyRequest({'form.submitted': True, + 'body': 'Hello yo!'}, + dbsession=self.session) + request.user = self.makeUser('foo', 'editor') + request.context = NewPage('AnotherPage') + self._callFUT(request) + pagecount = self.session.query(Page).filter_by(name='AnotherPage').count() + self.assertGreater(pagecount, 0) + def test_it_notsubmitted(self): from ..routes import NewPage request = dummy_request(self.session) diff --git a/docs/tutorials/wiki2/src/views/tutorial/views/default.py b/docs/tutorials/wiki2/src/views/tutorial/views/default.py index c1d402f6a..bb6300b75 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/views/tutorial/views/default.py @@ -58,6 +58,9 @@ 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: + 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) -- cgit v1.2.3 From b50a19224bbd2b1600e956f83e60bdb5ea908dd4 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sat, 9 Apr 2016 00:52:23 -0700 Subject: add result of installation step in wiki2 tutorial, but using the recently updated scaffold from master and normalize its version to 1.7. See #2104. --- docs/tutorials/wiki2/src/installation/CHANGES.txt | 4 + .../wiki2/src/installation/tutorial/__init__.py | 12 ++ .../src/installation/tutorial/models/__init__.py | 73 ++++++++++ .../wiki2/src/installation/tutorial/models/meta.py | 16 +++ .../src/installation/tutorial/models/mymodel.py | 18 +++ .../wiki2/src/installation/tutorial/routes.py | 3 + .../src/installation/tutorial/scripts/__init__.py | 1 + .../installation/tutorial/scripts/initializedb.py | 45 ++++++ .../installation/tutorial/static/pyramid-16x16.png | Bin 0 -> 1319 bytes .../src/installation/tutorial/static/pyramid.png | Bin 0 -> 12901 bytes .../src/installation/tutorial/static/theme.css | 154 +++++++++++++++++++++ .../src/installation/tutorial/static/theme.min.css | 1 + .../src/installation/tutorial/templates/404.jinja2 | 8 ++ .../installation/tutorial/templates/layout.jinja2 | 66 +++++++++ .../tutorial/templates/mytemplate.jinja2 | 8 ++ .../wiki2/src/installation/tutorial/tests.py | 65 +++++++++ .../src/installation/tutorial/views/__init__.py | 0 .../src/installation/tutorial/views/default.py | 33 +++++ .../src/installation/tutorial/views/notfound.py | 7 + 19 files changed, 514 insertions(+) create mode 100644 docs/tutorials/wiki2/src/installation/CHANGES.txt create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/__init__.py create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/models/__init__.py create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/models/meta.py create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/models/mymodel.py create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/routes.py create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/scripts/__init__.py create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/scripts/initializedb.py create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/static/pyramid-16x16.png create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/static/pyramid.png create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/static/theme.css create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/static/theme.min.css create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/templates/404.jinja2 create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/templates/layout.jinja2 create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/templates/mytemplate.jinja2 create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/tests.py create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/views/__init__.py create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/views/default.py create mode 100644 docs/tutorials/wiki2/src/installation/tutorial/views/notfound.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/installation/CHANGES.txt b/docs/tutorials/wiki2/src/installation/CHANGES.txt new file mode 100644 index 000000000..35a34f332 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/CHANGES.txt @@ -0,0 +1,4 @@ +0.0 +--- + +- Initial version diff --git a/docs/tutorials/wiki2/src/installation/tutorial/__init__.py b/docs/tutorials/wiki2/src/installation/tutorial/__init__.py new file mode 100644 index 000000000..4dab44823 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/__init__.py @@ -0,0 +1,12 @@ +from pyramid.config import Configurator + + +def main(global_config, **settings): + """ This function returns a Pyramid WSGI application. + """ + config = Configurator(settings=settings) + config.include('pyramid_jinja2') + config.include('.models') + config.include('.routes') + config.scan() + return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/src/installation/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/installation/tutorial/models/__init__.py new file mode 100644 index 000000000..48a957ecb --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/models/__init__.py @@ -0,0 +1,73 @@ +from sqlalchemy import engine_from_config +from sqlalchemy.orm import sessionmaker +from sqlalchemy.orm import configure_mappers +import zope.sqlalchemy + +# import or define all models here to ensure they are attached to the +# Base.metadata prior to any initialization routines +from .mymodel import MyModel # flake8: noqa + +# run configure_mappers after defining all of the models to ensure +# all relationships can be setup +configure_mappers() + + +def get_engine(settings, prefix='sqlalchemy.'): + return engine_from_config(settings, prefix) + + +def get_session_factory(engine): + factory = sessionmaker() + factory.configure(bind=engine) + return factory + + +def get_tm_session(session_factory, transaction_manager): + """ + Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. + + This function will hook the session to the transaction manager which + will take care of committing any changes. + + - When using pyramid_tm it will automatically be committed or aborted + depending on whether an exception is raised. + + - When using scripts you should wrap the session in a manager yourself. + For example:: + + import transaction + + engine = get_engine(settings) + session_factory = get_session_factory(engine) + with transaction.manager: + dbsession = get_tm_session(session_factory, transaction.manager) + + """ + dbsession = session_factory() + zope.sqlalchemy.register( + dbsession, transaction_manager=transaction_manager) + return dbsession + + +def includeme(config): + """ + Initialize the model for a Pyramid app. + + Activate this setup using ``config.include('tutorial.models')``. + + """ + settings = config.get_settings() + + # use pyramid_tm to hook the transaction lifecycle to the request + config.include('pyramid_tm') + + session_factory = get_session_factory(get_engine(settings)) + config.registry['dbsession_factory'] = session_factory + + # make request.dbsession available for use in Pyramid + config.add_request_method( + # r.tm is the transaction manager used by pyramid_tm + lambda r: get_tm_session(session_factory, r.tm), + 'dbsession', + reify=True + ) diff --git a/docs/tutorials/wiki2/src/installation/tutorial/models/meta.py b/docs/tutorials/wiki2/src/installation/tutorial/models/meta.py new file mode 100644 index 000000000..fc3e8f1dd --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/models/meta.py @@ -0,0 +1,16 @@ +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.schema import MetaData + +# Recommended naming convention used by Alembic, as various different database +# providers will autogenerate vastly different names making migrations more +# difficult. See: http://alembic.readthedocs.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) diff --git a/docs/tutorials/wiki2/src/installation/tutorial/models/mymodel.py b/docs/tutorials/wiki2/src/installation/tutorial/models/mymodel.py new file mode 100644 index 000000000..d65a01a42 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/models/mymodel.py @@ -0,0 +1,18 @@ +from sqlalchemy import ( + Column, + Index, + Integer, + Text, +) + +from .meta import Base + + +class MyModel(Base): + __tablename__ = 'models' + id = Column(Integer, primary_key=True) + name = Column(Text) + value = Column(Integer) + + +Index('my_index', MyModel.name, unique=True, mysql_length=255) diff --git a/docs/tutorials/wiki2/src/installation/tutorial/routes.py b/docs/tutorials/wiki2/src/installation/tutorial/routes.py new file mode 100644 index 000000000..25504ad4d --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/routes.py @@ -0,0 +1,3 @@ +def includeme(config): + config.add_static_view('static', 'static', cache_max_age=3600) + config.add_route('home', '/') diff --git a/docs/tutorials/wiki2/src/installation/tutorial/scripts/__init__.py b/docs/tutorials/wiki2/src/installation/tutorial/scripts/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/scripts/__init__.py @@ -0,0 +1 @@ +# package diff --git a/docs/tutorials/wiki2/src/installation/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/installation/tutorial/scripts/initializedb.py new file mode 100644 index 000000000..7307ecc5c --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/scripts/initializedb.py @@ -0,0 +1,45 @@ +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 [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/static/pyramid-16x16.png b/docs/tutorials/wiki2/src/installation/tutorial/static/pyramid-16x16.png new file mode 100644 index 000000000..979203112 Binary files /dev/null and b/docs/tutorials/wiki2/src/installation/tutorial/static/pyramid-16x16.png differ diff --git a/docs/tutorials/wiki2/src/installation/tutorial/static/pyramid.png b/docs/tutorials/wiki2/src/installation/tutorial/static/pyramid.png new file mode 100644 index 000000000..4ab837be9 Binary files /dev/null and b/docs/tutorials/wiki2/src/installation/tutorial/static/pyramid.png differ diff --git a/docs/tutorials/wiki2/src/installation/tutorial/static/theme.css b/docs/tutorials/wiki2/src/installation/tutorial/static/theme.css new file mode 100644 index 000000000..0f4b1a4d4 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/static/theme.css @@ -0,0 +1,154 @@ +@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700); +body { + font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 300; + color: #ffffff; + background: #bc2131; +} +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 300; +} +p { + font-weight: 300; +} +.font-normal { + font-weight: 400; +} +.font-semi-bold { + font-weight: 600; +} +.font-bold { + font-weight: 700; +} +.starter-template { + margin-top: 250px; +} +.starter-template .content { + margin-left: 10px; +} +.starter-template .content h1 { + margin-top: 10px; + font-size: 60px; +} +.starter-template .content h1 .smaller { + font-size: 40px; + color: #f2b7bd; +} +.starter-template .content .lead { + font-size: 25px; + color: #f2b7bd; +} +.starter-template .content .lead .font-normal { + color: #ffffff; +} +.starter-template .links { + float: right; + right: 0; + margin-top: 125px; +} +.starter-template .links ul { + display: block; + padding: 0; + margin: 0; +} +.starter-template .links ul li { + list-style: none; + display: inline; + margin: 0 10px; +} +.starter-template .links ul li:first-child { + margin-left: 0; +} +.starter-template .links ul li:last-child { + margin-right: 0; +} +.starter-template .links ul li.current-version { + color: #f2b7bd; + font-weight: 400; +} +.starter-template .links ul li a, a { + color: #f2b7bd; + text-decoration: underline; +} +.starter-template .links ul li a:hover, a:hover { + color: #ffffff; + text-decoration: underline; +} +.starter-template .links ul li .icon-muted { + color: #eb8b95; + margin-right: 5px; +} +.starter-template .links ul li:hover .icon-muted { + color: #ffffff; +} +.starter-template .copyright { + margin-top: 10px; + font-size: 0.9em; + color: #f2b7bd; + text-transform: lowercase; + float: right; + right: 0; +} +@media (max-width: 1199px) { + .starter-template .content h1 { + font-size: 45px; + } + .starter-template .content h1 .smaller { + font-size: 30px; + } + .starter-template .content .lead { + font-size: 20px; + } +} +@media (max-width: 991px) { + .starter-template { + margin-top: 0; + } + .starter-template .logo { + margin: 40px auto; + } + .starter-template .content { + margin-left: 0; + text-align: center; + } + .starter-template .content h1 { + margin-bottom: 20px; + } + .starter-template .links { + float: none; + text-align: center; + margin-top: 60px; + } + .starter-template .copyright { + float: none; + text-align: center; + } +} +@media (max-width: 767px) { + .starter-template .content h1 .smaller { + font-size: 25px; + display: block; + } + .starter-template .content .lead { + font-size: 16px; + } + .starter-template .links { + margin-top: 40px; + } + .starter-template .links ul li { + display: block; + margin: 0; + } + .starter-template .links ul li .icon-muted { + display: none; + } + .starter-template .copyright { + margin-top: 20px; + } +} diff --git a/docs/tutorials/wiki2/src/installation/tutorial/static/theme.min.css b/docs/tutorials/wiki2/src/installation/tutorial/static/theme.min.css new file mode 100644 index 000000000..0d25de5b6 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/static/theme.min.css @@ -0,0 +1 @@ +@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700);body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;color:#fff;background:#bc2131}h1,h2,h3,h4,h5,h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300}p{font-weight:300}.font-normal{font-weight:400}.font-semi-bold{font-weight:600}.font-bold{font-weight:700}.starter-template{margin-top:250px}.starter-template .content{margin-left:10px}.starter-template .content h1{margin-top:10px;font-size:60px}.starter-template .content h1 .smaller{font-size:40px;color:#f2b7bd}.starter-template .content .lead{font-size:25px;color:#f2b7bd}.starter-template .content .lead .font-normal{color:#fff}.starter-template .links{float:right;right:0;margin-top:125px}.starter-template .links ul{display:block;padding:0;margin:0}.starter-template .links ul li{list-style:none;display:inline;margin:0 10px}.starter-template .links ul li:first-child{margin-left:0}.starter-template .links ul li:last-child{margin-right:0}.starter-template .links ul li.current-version{color:#f2b7bd;font-weight:400}.starter-template .links ul li a,a{color:#f2b7bd;text-decoration:underline}.starter-template .links ul li a:hover,a:hover{color:#fff;text-decoration:underline}.starter-template .links ul li .icon-muted{color:#eb8b95;margin-right:5px}.starter-template .links ul li:hover .icon-muted{color:#fff}.starter-template .copyright{margin-top:10px;font-size:.9em;color:#f2b7bd;text-transform:lowercase;float:right;right:0}@media (max-width:1199px){.starter-template .content h1{font-size:45px}.starter-template .content h1 .smaller{font-size:30px}.starter-template .content .lead{font-size:20px}}@media (max-width:991px){.starter-template{margin-top:0}.starter-template .logo{margin:40px auto}.starter-template .content{margin-left:0;text-align:center}.starter-template .content h1{margin-bottom:20px}.starter-template .links{float:none;text-align:center;margin-top:60px}.starter-template .copyright{float:none;text-align:center}}@media (max-width:767px){.starter-template .content h1 .smaller{font-size:25px;display:block}.starter-template .content .lead{font-size:16px}.starter-template .links{margin-top:40px}.starter-template .links ul li{display:block;margin:0}.starter-template .links ul li .icon-muted{display:none}.starter-template .copyright{margin-top:20px}} diff --git a/docs/tutorials/wiki2/src/installation/tutorial/templates/404.jinja2 b/docs/tutorials/wiki2/src/installation/tutorial/templates/404.jinja2 new file mode 100644 index 000000000..1917f83c7 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/templates/404.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+

Pyramid Alchemy scaffold

+

404 Page Not Found

+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/installation/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/installation/tutorial/templates/layout.jinja2 new file mode 100644 index 000000000..ab8c5ea3d --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/templates/layout.jinja2 @@ -0,0 +1,66 @@ + + + + + + + + + + + Alchemy Scaffold for The Pyramid Web Framework + + + + + + + + + + + + + +
+
+
+
+ +
+
+ {% block content %} +

No content

+ {% endblock content %} +
+
+
+ +
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/installation/tutorial/templates/mytemplate.jinja2 b/docs/tutorials/wiki2/src/installation/tutorial/templates/mytemplate.jinja2 new file mode 100644 index 000000000..6b49869c4 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/templates/mytemplate.jinja2 @@ -0,0 +1,8 @@ +{% extends "layout.jinja2" %} + +{% block content %} +
+

Pyramid Alchemy scaffold

+

Welcome to {{project}}, an application generated by
the Pyramid Web Framework 1.7.

+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/installation/tutorial/tests.py b/docs/tutorials/wiki2/src/installation/tutorial/tests.py new file mode 100644 index 000000000..99e95efd3 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/tests.py @@ -0,0 +1,65 @@ +import unittest +import transaction + +from pyramid import testing + + +def dummy_request(dbsession): + return testing.DummyRequest(dbsession=dbsession) + + +class BaseTest(unittest.TestCase): + def setUp(self): + self.config = testing.setUp(settings={ + 'sqlalchemy.url': 'sqlite:///:memory:' + }) + self.config.include('.models') + settings = self.config.get_settings() + + from .models import ( + get_engine, + get_session_factory, + get_tm_session, + ) + + self.engine = get_engine(settings) + session_factory = get_session_factory(self.engine) + + self.session = get_tm_session(session_factory, transaction.manager) + + def init_database(self): + from .models.meta import Base + Base.metadata.create_all(self.engine) + + def tearDown(self): + from .models.meta import Base + + testing.tearDown() + transaction.abort() + Base.metadata.drop_all(self.engine) + + +class TestMyViewSuccessCondition(BaseTest): + + def setUp(self): + super(TestMyViewSuccessCondition, self).setUp() + self.init_database() + + from .models import MyModel + + model = MyModel(name='one', value=55) + self.session.add(model) + + def test_passing_view(self): + from .views.default import my_view + info = my_view(dummy_request(self.session)) + self.assertEqual(info['one'].name, 'one') + self.assertEqual(info['project'], 'tutorial') + + +class TestMyViewFailureCondition(BaseTest): + + def test_failing_view(self): + from .views.default import my_view + info = my_view(dummy_request(self.session)) + self.assertEqual(info.status_int, 500) diff --git a/docs/tutorials/wiki2/src/installation/tutorial/views/__init__.py b/docs/tutorials/wiki2/src/installation/tutorial/views/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/docs/tutorials/wiki2/src/installation/tutorial/views/default.py b/docs/tutorials/wiki2/src/installation/tutorial/views/default.py new file mode 100644 index 000000000..ad0c728d7 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/views/default.py @@ -0,0 +1,33 @@ +from pyramid.response import Response +from pyramid.view import view_config + +from sqlalchemy.exc import DBAPIError + +from ..models import MyModel + + +@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() + except DBAPIError: + return Response(db_err_msg, content_type='text/plain', status=500) + return {'one': one, 'project': 'tutorial'} + + +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. + +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. + +After you fix the problem, please restart the Pyramid application to +try it again. +""" diff --git a/docs/tutorials/wiki2/src/installation/tutorial/views/notfound.py b/docs/tutorials/wiki2/src/installation/tutorial/views/notfound.py new file mode 100644 index 000000000..69d6e2804 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/tutorial/views/notfound.py @@ -0,0 +1,7 @@ +from pyramid.view import notfound_view_config + + +@notfound_view_config(renderer='../templates/404.jinja2') +def notfound_view(request): + request.response.status = 404 + return {} -- cgit v1.2.3 From 458d513ba5ffd054323be1da9306ad0d37d0c10f Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sat, 9 Apr 2016 01:11:43 -0700 Subject: - add missing files --- docs/tutorials/wiki2/src/installation/MANIFEST.in | 2 + docs/tutorials/wiki2/src/installation/README.txt | 14 +++++ .../wiki2/src/installation/development.ini | 71 ++++++++++++++++++++++ .../wiki2/src/installation/production.ini | 60 ++++++++++++++++++ docs/tutorials/wiki2/src/installation/setup.py | 55 +++++++++++++++++ 5 files changed, 202 insertions(+) create mode 100644 docs/tutorials/wiki2/src/installation/MANIFEST.in create mode 100644 docs/tutorials/wiki2/src/installation/README.txt create mode 100644 docs/tutorials/wiki2/src/installation/development.ini create mode 100644 docs/tutorials/wiki2/src/installation/production.ini create mode 100644 docs/tutorials/wiki2/src/installation/setup.py (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/installation/MANIFEST.in b/docs/tutorials/wiki2/src/installation/MANIFEST.in new file mode 100644 index 000000000..42cd299b5 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/MANIFEST.in @@ -0,0 +1,2 @@ +include *.txt *.ini *.cfg *.rst +recursive-include tutorial *.ico *.png *.css *.gif *.jpg *.jinja2 *.pt *.txt *.mak *.mako *.js *.html *.xml diff --git a/docs/tutorials/wiki2/src/installation/README.txt b/docs/tutorials/wiki2/src/installation/README.txt new file mode 100644 index 000000000..68f430110 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/README.txt @@ -0,0 +1,14 @@ +tutorial README +================== + +Getting Started +--------------- + +- cd + +- $VENV/bin/python setup.py develop + +- $VENV/bin/initialize_tutorial_db development.ini + +- $VENV/bin/pserve development.ini + diff --git a/docs/tutorials/wiki2/src/installation/development.ini b/docs/tutorials/wiki2/src/installation/development.ini new file mode 100644 index 000000000..22b733e10 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/development.ini @@ -0,0 +1,71 @@ +### +# app configuration +# http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/environment.html +### + +[app:main] +use = egg:tutorial + +pyramid.reload_templates = true +pyramid.debug_authorization = false +pyramid.debug_notfound = false +pyramid.debug_routematch = false +pyramid.default_locale_name = en +pyramid.includes = + pyramid_debugtoolbar + pyramid_tm + +sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite + +# By default, the toolbar only appears for clients from IP addresses +# '127.0.0.1' and '::1'. +# debugtoolbar.hosts = 127.0.0.1 ::1 + +### +# wsgi server configuration +### + +[server:main] +use = egg:waitress#main +host = 127.0.0.1 +port = 6543 + +### +# logging configuration +# http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/logging.html +### + +[loggers] +keys = root, tutorial, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_tutorial] +level = DEBUG +handlers = +qualname = tutorial + +[logger_sqlalchemy] +level = INFO +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/tutorials/wiki2/src/installation/production.ini b/docs/tutorials/wiki2/src/installation/production.ini new file mode 100644 index 000000000..d2ecfe22a --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/production.ini @@ -0,0 +1,60 @@ +### +# app configuration +# http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/environment.html +### + +[app:main] +use = egg:tutorial + +pyramid.reload_templates = false +pyramid.debug_authorization = false +pyramid.debug_notfound = false +pyramid.debug_routematch = false +pyramid.default_locale_name = en + +sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite + +[server:main] +use = egg:waitress#main +host = 0.0.0.0 +port = 6543 + +### +# logging configuration +# http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/logging.html +### + +[loggers] +keys = root, tutorial, sqlalchemy + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console + +[logger_tutorial] +level = WARN +handlers = +qualname = tutorial + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine +# "level = INFO" logs SQL queries. +# "level = DEBUG" logs SQL queries and results. +# "level = WARN" logs neither. (Recommended for production systems.) + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/docs/tutorials/wiki2/src/installation/setup.py b/docs/tutorials/wiki2/src/installation/setup.py new file mode 100644 index 000000000..c82c880c9 --- /dev/null +++ b/docs/tutorials/wiki2/src/installation/setup.py @@ -0,0 +1,55 @@ +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 = [ + 'pyramid', + 'pyramid_jinja2', + 'pyramid_debugtoolbar', + 'pyramid_tm', + 'SQLAlchemy', + 'transaction', + 'zope.sqlalchemy', + 'waitress', + ] + +testing_extras = [ + 'WebTest >= 1.3.1', # py3 compat + 'pytest', # includes virtualenv + 'pytest-cov', + ] + +setup(name='tutorial', + version='0.0', + description='tutorial', + 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 wsgi bfg pylons pyramid', + packages=find_packages(), + include_package_data=True, + zip_safe=False, + extras_require={ + 'testing': testing_extras, + }, + install_requires=requires, + entry_points="""\ + [paste.app_factory] + main = tutorial:main + [console_scripts] + initialize_tutorial_db = tutorial.scripts.initializedb:main + """, + ) -- cgit v1.2.3 From b93b011f1a4904963f453c2583f4c0d3aa50ffe5 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sat, 9 Apr 2016 02:10:54 -0700 Subject: - update wiki2/src/basiclayout files --- .../wiki2/src/basiclayout/development.ini | 4 ++-- .../tutorials/wiki2/src/basiclayout/production.ini | 4 ++-- docs/tutorials/wiki2/src/basiclayout/setup.py | 23 ++++++++++++---------- .../basiclayout/tutorial/templates/layout.jinja2 | 2 +- .../tutorial/templates/mytemplate.jinja2 | 2 +- .../wiki2/src/basiclayout/tutorial/tests.py | 2 +- 6 files changed, 20 insertions(+), 17 deletions(-) (limited to 'docs/tutorials/wiki2/src') diff --git a/docs/tutorials/wiki2/src/basiclayout/development.ini b/docs/tutorials/wiki2/src/basiclayout/development.ini index 99c4ff0fe..22b733e10 100644 --- a/docs/tutorials/wiki2/src/basiclayout/development.ini +++ b/docs/tutorials/wiki2/src/basiclayout/development.ini @@ -1,6 +1,6 @@ ### # app configuration -# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +# http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/environment.html ### [app:main] @@ -32,7 +32,7 @@ port = 6543 ### # logging configuration -# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html +# http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/logging.html ### [loggers] diff --git a/docs/tutorials/wiki2/src/basiclayout/production.ini b/docs/tutorials/wiki2/src/basiclayout/production.ini index cb1db3211..d2ecfe22a 100644 --- a/docs/tutorials/wiki2/src/basiclayout/production.ini +++ b/docs/tutorials/wiki2/src/basiclayout/production.ini @@ -1,6 +1,6 @@ ### # app configuration -# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +# http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/environment.html ### [app:main] @@ -21,7 +21,7 @@ port = 6543 ### # logging configuration -# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html +# http://docs.pylonsproject.org/projects/pyramid/en/1.7-branch/narr/logging.html ### [loggers] diff --git a/docs/tutorials/wiki2/src/basiclayout/setup.py b/docs/tutorials/wiki2/src/basiclayout/setup.py index 7bc697730..c82c880c9 100644 --- a/docs/tutorials/wiki2/src/basiclayout/setup.py +++ b/docs/tutorials/wiki2/src/basiclayout/setup.py @@ -19,20 +19,22 @@ requires = [ 'waitress', ] -tests_require = [ - 'WebTest', -] +testing_extras = [ + 'WebTest >= 1.3.1', # py3 compat + 'pytest', # includes virtualenv + 'pytest-cov', + ] setup(name='tutorial', version='0.0', description='tutorial', long_description=README + '\n\n' + CHANGES, classifiers=[ - "Programming Language :: Python", - "Framework :: Pyramid", - "Topic :: Internet :: WWW/HTTP", - "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", - ], + "Programming Language :: Python", + "Framework :: Pyramid", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", + ], author='', author_email='', url='', @@ -40,8 +42,9 @@ setup(name='tutorial', packages=find_packages(), include_package_data=True, zip_safe=False, - test_suite='tutorial', - tests_require=tests_require, + extras_require={ + 'testing': testing_extras, + }, install_requires=requires, entry_points="""\ [paste.app_factory] diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/layout.jinja2 index ff624c65b..ab8c5ea3d 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/layout.jinja2 +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/layout.jinja2 @@ -40,7 +40,7 @@