diff options
9 files changed, 417 insertions, 81 deletions
diff --git a/docs/quick_tour.rst b/docs/quick_tour.rst index be53a3344..add25f1d2 100644 --- a/docs/quick_tour.rst +++ b/docs/quick_tour.rst @@ -938,7 +938,7 @@ system, can then easily get at the data thanks to SQLAlchemy: .. literalinclude:: quick_tour/sqla_demo/sqla_demo/views/default.py :language: python :lineno-match: - :lines: 13 + :pyobject: my_view .. seealso:: See also: :ref:`Quick Tutorial Databases <qtut_databases>`, `SQLAlchemy diff --git a/docs/quick_tour/sqla_demo/sqla_demo/models/__init__.py b/docs/quick_tour/sqla_demo/sqla_demo/models/__init__.py index 71f9f36b5..b00b8de9e 100644 --- a/docs/quick_tour/sqla_demo/sqla_demo/models/__init__.py +++ b/docs/quick_tour/sqla_demo/sqla_demo/models/__init__.py @@ -3,12 +3,12 @@ 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 +# 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 +# Run ``configure_mappers`` after defining all of the models to ensure +# all relationships can be setup. configure_mappers() @@ -22,7 +22,7 @@ def get_session_factory(engine): return factory -def get_tm_session(session_factory, transaction_manager): +def get_tm_session(session_factory, transaction_manager, request=None): """ Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. @@ -33,7 +33,9 @@ def get_tm_session(session_factory, transaction_manager): depending on whether an exception is raised. - When using scripts you should wrap the session in a manager yourself. - For example:: + For example: + + .. code-block:: python import transaction @@ -42,10 +44,43 @@ def get_tm_session(session_factory, transaction_manager): with transaction.manager: dbsession = get_tm_session(session_factory, transaction.manager) + This function may be invoked with a ``request`` kwarg, such as when invoked + by the reified ``.dbsession`` Pyramid request attribute which is configured + via the ``includeme`` function below. The default value, for backwards + compatibility, is ``None``. + + The ``request`` kwarg is used to populate the ``sqlalchemy.orm.Session``'s + "info" dict. The "info" dict is the official namespace for developers to + stash session-specific information. For more information, please see the + SQLAlchemy docs: + https://docs.sqlalchemy.org/en/stable/orm/session_api.html#sqlalchemy.orm.session.Session.params.info + + By placing the active ``request`` in the "info" dict, developers will be + able to access the active Pyramid request from an instance of an SQLAlchemy + object in one of two ways: + + - Classic SQLAlchemy. This uses the ``Session``'s utility class method: + + .. code-block:: python + + from sqlalchemy.orm.session import Session as sa_Session + + dbsession = sa_Session.object_session(dbObject) + request = dbsession.info["request"] + + - Modern SQLAlchemy. This uses the "Runtime Inspection API": + + .. code-block:: python + + from sqlalchemy import inspect as sa_inspect + + dbsession = sa_inspect(dbObject).session + request = dbsession.info["request"] """ - dbsession = session_factory() + dbsession = session_factory(info={"request": request}) zope.sqlalchemy.register( - dbsession, transaction_manager=transaction_manager) + dbsession, transaction_manager=transaction_manager + ) return dbsession @@ -59,7 +94,12 @@ def includeme(config): settings = config.get_settings() settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager' - # use pyramid_tm to hook the transaction lifecycle to the request + # Use ``pyramid_tm`` to hook the transaction lifecycle to the request. + # Note: the packages ``pyramid_tm`` and ``transaction`` work together to + # automatically close the active database session after every request. + # If your project migrates away from ``pyramid_tm``, you may need to use a + # Pyramid callback function to close the database session after each + # request. config.include('pyramid_tm') # use pyramid_retry to retry a request when transient exceptions occur @@ -79,7 +119,9 @@ def includeme(config): dbsession = request.environ.get('app.dbsession') if dbsession is None: # request.tm is the transaction manager used by pyramid_tm - dbsession = get_tm_session(session_factory, request.tm) + dbsession = get_tm_session( + session_factory, request.tm, request=request + ) return dbsession config.add_request_method(dbsession, reify=True) diff --git a/docs/tutorials/wiki2/src/authentication/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/authentication/tutorial/models/__init__.py index 47d77ef01..6f9f6586a 100644 --- a/docs/tutorials/wiki2/src/authentication/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/authentication/tutorial/models/__init__.py @@ -3,13 +3,13 @@ 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 +# 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 +# Run ``configure_mappers`` after defining all of the models to ensure +# all relationships can be setup. configure_mappers() @@ -23,7 +23,7 @@ def get_session_factory(engine): return factory -def get_tm_session(session_factory, transaction_manager): +def get_tm_session(session_factory, transaction_manager, request=None): """ Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. @@ -34,7 +34,9 @@ def get_tm_session(session_factory, transaction_manager): depending on whether an exception is raised. - When using scripts you should wrap the session in a manager yourself. - For example:: + For example: + + .. code-block:: python import transaction @@ -43,10 +45,43 @@ def get_tm_session(session_factory, transaction_manager): with transaction.manager: dbsession = get_tm_session(session_factory, transaction.manager) + This function may be invoked with a ``request`` kwarg, such as when invoked + by the reified ``.dbsession`` Pyramid request attribute which is configured + via the ``includeme`` function below. The default value, for backwards + compatibility, is ``None``. + + The ``request`` kwarg is used to populate the ``sqlalchemy.orm.Session``'s + "info" dict. The "info" dict is the official namespace for developers to + stash session-specific information. For more information, please see the + SQLAlchemy docs: + https://docs.sqlalchemy.org/en/stable/orm/session_api.html#sqlalchemy.orm.session.Session.params.info + + By placing the active ``request`` in the "info" dict, developers will be + able to access the active Pyramid request from an instance of an SQLAlchemy + object in one of two ways: + + - Classic SQLAlchemy. This uses the ``Session``'s utility class method: + + .. code-block:: python + + from sqlalchemy.orm.session import Session as sa_Session + + dbsession = sa_Session.object_session(dbObject) + request = dbsession.info["request"] + + - Modern SQLAlchemy. This uses the "Runtime Inspection API": + + .. code-block:: python + + from sqlalchemy import inspect as sa_inspect + + dbsession = sa_inspect(dbObject).session + request = dbsession.info["request"] """ - dbsession = session_factory() + dbsession = session_factory(info={"request": request}) zope.sqlalchemy.register( - dbsession, transaction_manager=transaction_manager) + dbsession, transaction_manager=transaction_manager + ) return dbsession @@ -60,7 +95,12 @@ def includeme(config): settings = config.get_settings() settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager' - # use pyramid_tm to hook the transaction lifecycle to the request + # Use ``pyramid_tm`` to hook the transaction lifecycle to the request. + # Note: the packages ``pyramid_tm`` and ``transaction`` work together to + # automatically close the active database session after every request. + # If your project migrates away from ``pyramid_tm``, you may need to use a + # Pyramid callback function to close the database session after each + # request. config.include('pyramid_tm') # use pyramid_retry to retry a request when transient exceptions occur @@ -80,7 +120,9 @@ def includeme(config): dbsession = request.environ.get('app.dbsession') if dbsession is None: # request.tm is the transaction manager used by pyramid_tm - dbsession = get_tm_session(session_factory, request.tm) + dbsession = get_tm_session( + session_factory, request.tm, request=request + ) return dbsession config.add_request_method(dbsession, reify=True) diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py index 47d77ef01..6f9f6586a 100644 --- a/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/authorization/tutorial/models/__init__.py @@ -3,13 +3,13 @@ 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 +# 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 +# Run ``configure_mappers`` after defining all of the models to ensure +# all relationships can be setup. configure_mappers() @@ -23,7 +23,7 @@ def get_session_factory(engine): return factory -def get_tm_session(session_factory, transaction_manager): +def get_tm_session(session_factory, transaction_manager, request=None): """ Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. @@ -34,7 +34,9 @@ def get_tm_session(session_factory, transaction_manager): depending on whether an exception is raised. - When using scripts you should wrap the session in a manager yourself. - For example:: + For example: + + .. code-block:: python import transaction @@ -43,10 +45,43 @@ def get_tm_session(session_factory, transaction_manager): with transaction.manager: dbsession = get_tm_session(session_factory, transaction.manager) + This function may be invoked with a ``request`` kwarg, such as when invoked + by the reified ``.dbsession`` Pyramid request attribute which is configured + via the ``includeme`` function below. The default value, for backwards + compatibility, is ``None``. + + The ``request`` kwarg is used to populate the ``sqlalchemy.orm.Session``'s + "info" dict. The "info" dict is the official namespace for developers to + stash session-specific information. For more information, please see the + SQLAlchemy docs: + https://docs.sqlalchemy.org/en/stable/orm/session_api.html#sqlalchemy.orm.session.Session.params.info + + By placing the active ``request`` in the "info" dict, developers will be + able to access the active Pyramid request from an instance of an SQLAlchemy + object in one of two ways: + + - Classic SQLAlchemy. This uses the ``Session``'s utility class method: + + .. code-block:: python + + from sqlalchemy.orm.session import Session as sa_Session + + dbsession = sa_Session.object_session(dbObject) + request = dbsession.info["request"] + + - Modern SQLAlchemy. This uses the "Runtime Inspection API": + + .. code-block:: python + + from sqlalchemy import inspect as sa_inspect + + dbsession = sa_inspect(dbObject).session + request = dbsession.info["request"] """ - dbsession = session_factory() + dbsession = session_factory(info={"request": request}) zope.sqlalchemy.register( - dbsession, transaction_manager=transaction_manager) + dbsession, transaction_manager=transaction_manager + ) return dbsession @@ -60,7 +95,12 @@ def includeme(config): settings = config.get_settings() settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager' - # use pyramid_tm to hook the transaction lifecycle to the request + # Use ``pyramid_tm`` to hook the transaction lifecycle to the request. + # Note: the packages ``pyramid_tm`` and ``transaction`` work together to + # automatically close the active database session after every request. + # If your project migrates away from ``pyramid_tm``, you may need to use a + # Pyramid callback function to close the database session after each + # request. config.include('pyramid_tm') # use pyramid_retry to retry a request when transient exceptions occur @@ -80,7 +120,9 @@ def includeme(config): dbsession = request.environ.get('app.dbsession') if dbsession is None: # request.tm is the transaction manager used by pyramid_tm - dbsession = get_tm_session(session_factory, request.tm) + dbsession = get_tm_session( + session_factory, request.tm, request=request + ) return dbsession config.add_request_method(dbsession, reify=True) diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py index 1c3ec5ee8..c12abdc85 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models/__init__.py @@ -3,12 +3,12 @@ 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 +# 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 +# Run ``configure_mappers`` after defining all of the models to ensure +# all relationships can be setup. configure_mappers() @@ -22,7 +22,7 @@ def get_session_factory(engine): return factory -def get_tm_session(session_factory, transaction_manager): +def get_tm_session(session_factory, transaction_manager, request=None): """ Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. @@ -33,7 +33,9 @@ def get_tm_session(session_factory, transaction_manager): depending on whether an exception is raised. - When using scripts you should wrap the session in a manager yourself. - For example:: + For example: + + .. code-block:: python import transaction @@ -42,10 +44,43 @@ def get_tm_session(session_factory, transaction_manager): with transaction.manager: dbsession = get_tm_session(session_factory, transaction.manager) + This function may be invoked with a ``request`` kwarg, such as when invoked + by the reified ``.dbsession`` Pyramid request attribute which is configured + via the ``includeme`` function below. The default value, for backwards + compatibility, is ``None``. + + The ``request`` kwarg is used to populate the ``sqlalchemy.orm.Session``'s + "info" dict. The "info" dict is the official namespace for developers to + stash session-specific information. For more information, please see the + SQLAlchemy docs: + https://docs.sqlalchemy.org/en/stable/orm/session_api.html#sqlalchemy.orm.session.Session.params.info + + By placing the active ``request`` in the "info" dict, developers will be + able to access the active Pyramid request from an instance of an SQLAlchemy + object in one of two ways: + + - Classic SQLAlchemy. This uses the ``Session``'s utility class method: + + .. code-block:: python + + from sqlalchemy.orm.session import Session as sa_Session + + dbsession = sa_Session.object_session(dbObject) + request = dbsession.info["request"] + + - Modern SQLAlchemy. This uses the "Runtime Inspection API": + + .. code-block:: python + + from sqlalchemy import inspect as sa_inspect + + dbsession = sa_inspect(dbObject).session + request = dbsession.info["request"] """ - dbsession = session_factory() + dbsession = session_factory(info={"request": request}) zope.sqlalchemy.register( - dbsession, transaction_manager=transaction_manager) + dbsession, transaction_manager=transaction_manager + ) return dbsession @@ -59,7 +94,12 @@ def includeme(config): settings = config.get_settings() settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager' - # use pyramid_tm to hook the transaction lifecycle to the request + # Use ``pyramid_tm`` to hook the transaction lifecycle to the request. + # Note: the packages ``pyramid_tm`` and ``transaction`` work together to + # automatically close the active database session after every request. + # If your project migrates away from ``pyramid_tm``, you may need to use a + # Pyramid callback function to close the database session after each + # request. config.include('pyramid_tm') # use pyramid_retry to retry a request when transient exceptions occur @@ -79,7 +119,9 @@ def includeme(config): dbsession = request.environ.get('app.dbsession') if dbsession is None: # request.tm is the transaction manager used by pyramid_tm - dbsession = get_tm_session(session_factory, request.tm) + dbsession = get_tm_session( + session_factory, request.tm, request=request + ) return dbsession config.add_request_method(dbsession, reify=True) diff --git a/docs/tutorials/wiki2/src/installation/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/installation/tutorial/models/__init__.py index 1c3ec5ee8..c12abdc85 100644 --- a/docs/tutorials/wiki2/src/installation/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/installation/tutorial/models/__init__.py @@ -3,12 +3,12 @@ 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 +# 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 +# Run ``configure_mappers`` after defining all of the models to ensure +# all relationships can be setup. configure_mappers() @@ -22,7 +22,7 @@ def get_session_factory(engine): return factory -def get_tm_session(session_factory, transaction_manager): +def get_tm_session(session_factory, transaction_manager, request=None): """ Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. @@ -33,7 +33,9 @@ def get_tm_session(session_factory, transaction_manager): depending on whether an exception is raised. - When using scripts you should wrap the session in a manager yourself. - For example:: + For example: + + .. code-block:: python import transaction @@ -42,10 +44,43 @@ def get_tm_session(session_factory, transaction_manager): with transaction.manager: dbsession = get_tm_session(session_factory, transaction.manager) + This function may be invoked with a ``request`` kwarg, such as when invoked + by the reified ``.dbsession`` Pyramid request attribute which is configured + via the ``includeme`` function below. The default value, for backwards + compatibility, is ``None``. + + The ``request`` kwarg is used to populate the ``sqlalchemy.orm.Session``'s + "info" dict. The "info" dict is the official namespace for developers to + stash session-specific information. For more information, please see the + SQLAlchemy docs: + https://docs.sqlalchemy.org/en/stable/orm/session_api.html#sqlalchemy.orm.session.Session.params.info + + By placing the active ``request`` in the "info" dict, developers will be + able to access the active Pyramid request from an instance of an SQLAlchemy + object in one of two ways: + + - Classic SQLAlchemy. This uses the ``Session``'s utility class method: + + .. code-block:: python + + from sqlalchemy.orm.session import Session as sa_Session + + dbsession = sa_Session.object_session(dbObject) + request = dbsession.info["request"] + + - Modern SQLAlchemy. This uses the "Runtime Inspection API": + + .. code-block:: python + + from sqlalchemy import inspect as sa_inspect + + dbsession = sa_inspect(dbObject).session + request = dbsession.info["request"] """ - dbsession = session_factory() + dbsession = session_factory(info={"request": request}) zope.sqlalchemy.register( - dbsession, transaction_manager=transaction_manager) + dbsession, transaction_manager=transaction_manager + ) return dbsession @@ -59,7 +94,12 @@ def includeme(config): settings = config.get_settings() settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager' - # use pyramid_tm to hook the transaction lifecycle to the request + # Use ``pyramid_tm`` to hook the transaction lifecycle to the request. + # Note: the packages ``pyramid_tm`` and ``transaction`` work together to + # automatically close the active database session after every request. + # If your project migrates away from ``pyramid_tm``, you may need to use a + # Pyramid callback function to close the database session after each + # request. config.include('pyramid_tm') # use pyramid_retry to retry a request when transient exceptions occur @@ -79,7 +119,9 @@ def includeme(config): dbsession = request.environ.get('app.dbsession') if dbsession is None: # request.tm is the transaction manager used by pyramid_tm - dbsession = get_tm_session(session_factory, request.tm) + dbsession = get_tm_session( + session_factory, request.tm, request=request + ) return dbsession config.add_request_method(dbsession, reify=True) diff --git a/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py index 47d77ef01..6f9f6586a 100644 --- a/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/models/tutorial/models/__init__.py @@ -3,13 +3,13 @@ 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 +# 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 +# Run ``configure_mappers`` after defining all of the models to ensure +# all relationships can be setup. configure_mappers() @@ -23,7 +23,7 @@ def get_session_factory(engine): return factory -def get_tm_session(session_factory, transaction_manager): +def get_tm_session(session_factory, transaction_manager, request=None): """ Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. @@ -34,7 +34,9 @@ def get_tm_session(session_factory, transaction_manager): depending on whether an exception is raised. - When using scripts you should wrap the session in a manager yourself. - For example:: + For example: + + .. code-block:: python import transaction @@ -43,10 +45,43 @@ def get_tm_session(session_factory, transaction_manager): with transaction.manager: dbsession = get_tm_session(session_factory, transaction.manager) + This function may be invoked with a ``request`` kwarg, such as when invoked + by the reified ``.dbsession`` Pyramid request attribute which is configured + via the ``includeme`` function below. The default value, for backwards + compatibility, is ``None``. + + The ``request`` kwarg is used to populate the ``sqlalchemy.orm.Session``'s + "info" dict. The "info" dict is the official namespace for developers to + stash session-specific information. For more information, please see the + SQLAlchemy docs: + https://docs.sqlalchemy.org/en/stable/orm/session_api.html#sqlalchemy.orm.session.Session.params.info + + By placing the active ``request`` in the "info" dict, developers will be + able to access the active Pyramid request from an instance of an SQLAlchemy + object in one of two ways: + + - Classic SQLAlchemy. This uses the ``Session``'s utility class method: + + .. code-block:: python + + from sqlalchemy.orm.session import Session as sa_Session + + dbsession = sa_Session.object_session(dbObject) + request = dbsession.info["request"] + + - Modern SQLAlchemy. This uses the "Runtime Inspection API": + + .. code-block:: python + + from sqlalchemy import inspect as sa_inspect + + dbsession = sa_inspect(dbObject).session + request = dbsession.info["request"] """ - dbsession = session_factory() + dbsession = session_factory(info={"request": request}) zope.sqlalchemy.register( - dbsession, transaction_manager=transaction_manager) + dbsession, transaction_manager=transaction_manager + ) return dbsession @@ -60,7 +95,12 @@ def includeme(config): settings = config.get_settings() settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager' - # use pyramid_tm to hook the transaction lifecycle to the request + # Use ``pyramid_tm`` to hook the transaction lifecycle to the request. + # Note: the packages ``pyramid_tm`` and ``transaction`` work together to + # automatically close the active database session after every request. + # If your project migrates away from ``pyramid_tm``, you may need to use a + # Pyramid callback function to close the database session after each + # request. config.include('pyramid_tm') # use pyramid_retry to retry a request when transient exceptions occur @@ -80,7 +120,9 @@ def includeme(config): dbsession = request.environ.get('app.dbsession') if dbsession is None: # request.tm is the transaction manager used by pyramid_tm - dbsession = get_tm_session(session_factory, request.tm) + dbsession = get_tm_session( + session_factory, request.tm, request=request + ) return dbsession config.add_request_method(dbsession, reify=True) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py index 47d77ef01..6f9f6586a 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/models/__init__.py @@ -3,13 +3,13 @@ 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 +# 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 +# Run ``configure_mappers`` after defining all of the models to ensure +# all relationships can be setup. configure_mappers() @@ -23,7 +23,7 @@ def get_session_factory(engine): return factory -def get_tm_session(session_factory, transaction_manager): +def get_tm_session(session_factory, transaction_manager, request=None): """ Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. @@ -34,7 +34,9 @@ def get_tm_session(session_factory, transaction_manager): depending on whether an exception is raised. - When using scripts you should wrap the session in a manager yourself. - For example:: + For example: + + .. code-block:: python import transaction @@ -43,10 +45,43 @@ def get_tm_session(session_factory, transaction_manager): with transaction.manager: dbsession = get_tm_session(session_factory, transaction.manager) + This function may be invoked with a ``request`` kwarg, such as when invoked + by the reified ``.dbsession`` Pyramid request attribute which is configured + via the ``includeme`` function below. The default value, for backwards + compatibility, is ``None``. + + The ``request`` kwarg is used to populate the ``sqlalchemy.orm.Session``'s + "info" dict. The "info" dict is the official namespace for developers to + stash session-specific information. For more information, please see the + SQLAlchemy docs: + https://docs.sqlalchemy.org/en/stable/orm/session_api.html#sqlalchemy.orm.session.Session.params.info + + By placing the active ``request`` in the "info" dict, developers will be + able to access the active Pyramid request from an instance of an SQLAlchemy + object in one of two ways: + + - Classic SQLAlchemy. This uses the ``Session``'s utility class method: + + .. code-block:: python + + from sqlalchemy.orm.session import Session as sa_Session + + dbsession = sa_Session.object_session(dbObject) + request = dbsession.info["request"] + + - Modern SQLAlchemy. This uses the "Runtime Inspection API": + + .. code-block:: python + + from sqlalchemy import inspect as sa_inspect + + dbsession = sa_inspect(dbObject).session + request = dbsession.info["request"] """ - dbsession = session_factory() + dbsession = session_factory(info={"request": request}) zope.sqlalchemy.register( - dbsession, transaction_manager=transaction_manager) + dbsession, transaction_manager=transaction_manager + ) return dbsession @@ -60,7 +95,12 @@ def includeme(config): settings = config.get_settings() settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager' - # use pyramid_tm to hook the transaction lifecycle to the request + # Use ``pyramid_tm`` to hook the transaction lifecycle to the request. + # Note: the packages ``pyramid_tm`` and ``transaction`` work together to + # automatically close the active database session after every request. + # If your project migrates away from ``pyramid_tm``, you may need to use a + # Pyramid callback function to close the database session after each + # request. config.include('pyramid_tm') # use pyramid_retry to retry a request when transient exceptions occur @@ -80,7 +120,9 @@ def includeme(config): dbsession = request.environ.get('app.dbsession') if dbsession is None: # request.tm is the transaction manager used by pyramid_tm - dbsession = get_tm_session(session_factory, request.tm) + dbsession = get_tm_session( + session_factory, request.tm, request=request + ) return dbsession config.add_request_method(dbsession, reify=True) diff --git a/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py index 47d77ef01..6f9f6586a 100644 --- a/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py +++ b/docs/tutorials/wiki2/src/views/tutorial/models/__init__.py @@ -3,13 +3,13 @@ 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 +# 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 +# Run ``configure_mappers`` after defining all of the models to ensure +# all relationships can be setup. configure_mappers() @@ -23,7 +23,7 @@ def get_session_factory(engine): return factory -def get_tm_session(session_factory, transaction_manager): +def get_tm_session(session_factory, transaction_manager, request=None): """ Get a ``sqlalchemy.orm.Session`` instance backed by a transaction. @@ -34,7 +34,9 @@ def get_tm_session(session_factory, transaction_manager): depending on whether an exception is raised. - When using scripts you should wrap the session in a manager yourself. - For example:: + For example: + + .. code-block:: python import transaction @@ -43,10 +45,43 @@ def get_tm_session(session_factory, transaction_manager): with transaction.manager: dbsession = get_tm_session(session_factory, transaction.manager) + This function may be invoked with a ``request`` kwarg, such as when invoked + by the reified ``.dbsession`` Pyramid request attribute which is configured + via the ``includeme`` function below. The default value, for backwards + compatibility, is ``None``. + + The ``request`` kwarg is used to populate the ``sqlalchemy.orm.Session``'s + "info" dict. The "info" dict is the official namespace for developers to + stash session-specific information. For more information, please see the + SQLAlchemy docs: + https://docs.sqlalchemy.org/en/stable/orm/session_api.html#sqlalchemy.orm.session.Session.params.info + + By placing the active ``request`` in the "info" dict, developers will be + able to access the active Pyramid request from an instance of an SQLAlchemy + object in one of two ways: + + - Classic SQLAlchemy. This uses the ``Session``'s utility class method: + + .. code-block:: python + + from sqlalchemy.orm.session import Session as sa_Session + + dbsession = sa_Session.object_session(dbObject) + request = dbsession.info["request"] + + - Modern SQLAlchemy. This uses the "Runtime Inspection API": + + .. code-block:: python + + from sqlalchemy import inspect as sa_inspect + + dbsession = sa_inspect(dbObject).session + request = dbsession.info["request"] """ - dbsession = session_factory() + dbsession = session_factory(info={"request": request}) zope.sqlalchemy.register( - dbsession, transaction_manager=transaction_manager) + dbsession, transaction_manager=transaction_manager + ) return dbsession @@ -60,7 +95,12 @@ def includeme(config): settings = config.get_settings() settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager' - # use pyramid_tm to hook the transaction lifecycle to the request + # Use ``pyramid_tm`` to hook the transaction lifecycle to the request. + # Note: the packages ``pyramid_tm`` and ``transaction`` work together to + # automatically close the active database session after every request. + # If your project migrates away from ``pyramid_tm``, you may need to use a + # Pyramid callback function to close the database session after each + # request. config.include('pyramid_tm') # use pyramid_retry to retry a request when transient exceptions occur @@ -80,7 +120,9 @@ def includeme(config): dbsession = request.environ.get('app.dbsession') if dbsession is None: # request.tm is the transaction manager used by pyramid_tm - dbsession = get_tm_session(session_factory, request.tm) + dbsession = get_tm_session( + session_factory, request.tm, request=request + ) return dbsession config.add_request_method(dbsession, reify=True) |
