This is insanely helpful - thank you Michael. I'm going to pass the dbsession into the class method for now but look into a service layer.
On Monday, January 6, 2020 at 2:56:28 PM UTC-6, Michael Merickel wrote: > > This is a super common complaint from people and it doesn't have a single > definitive solution. There are a couple handy tricks: > > 1. If your classmethod receives any managed objects, you can get a > reference to the session from the object itself. For example > ``sqlalchemy.orm.object_session(user)``. > > 2. The easy answer when the above doesn't work is to pass the dbsession > into the class method. > > After this - if still not satisfied - are some hacky things you can do: > > 1. Stop using classmethod approaches and instead put in a real service > layer. This has tons of benefits, see something like pyramid_services for > more information about this. This approach will allow you to bind the whole > service to a dbsession, and no longer need to pass it around at all. > > 2. Define a helper to grab the session from the request threadlocal. > Something like ``get_current_dbsession = lambda: > get_current_request().dbsession``. Then import that and use it via > ``dbsession = get_current_dbsession()``. > > The threadlocal approach above has some downsides but is still better than > using a scoped session. Allowing the request to seep into and infect your > data model is a little wonky and makes your model code less reusable > outside of Pyramid. Even if you are okay with that, something like > ``get_current_dbsession().query()`` is going to be better than > ``scoped_session.query()`` because it's more explicitly a value that > changes over time versus the magic scoped session object proxy. > > In my own apps I use all the approaches mentioned above except the > threadlocal one. > > - Michael > > On Mon, Jan 6, 2020 at 2:37 PM Kate Boelhauf <ka...@katimari.com > <javascript:>> wrote: > >> A little background - I'm updating my entire site and used a cookiecutter >> as referenced in Pyramids docs to do so. I am using sqlalchemy. >> >> It has a db session as a request method. This works perfectly for me in >> all of my views. However, I have class methods in my models that need to >> access a session. I can't figure out a way to access the session that the >> cookiecutter set up in models.py so I reverted to creating a separate >> session using >> scoped_session(sessionmaker(extension=ZopeTransactionExtension())) and >> importing it into my model and using the request session in my views. >> >> This just seems wrong - am I approaching this wrong? >> >> Below is the model.py file that the cookiecutter created: >> >> 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() >> settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager' >> >> # use pyramid_tm to hook the transaction lifecycle to the request >> config.include('pyramid_tm') >> >> # use pyramid_retry to retry a request when transient exceptions occur >> config.include('pyramid_retry') >> >> 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 >> ) >> >> >> >> >> >> >> >> >> >> >> -- >> You received this message because you are subscribed to the Google Groups >> "pylons-discuss" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to pylons-...@googlegroups.com <javascript:>. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/pylons-discuss/c60ba3e3-d3ea-4f59-9c94-460c7964c865%40googlegroups.com >> >> <https://groups.google.com/d/msgid/pylons-discuss/c60ba3e3-d3ea-4f59-9c94-460c7964c865%40googlegroups.com?utm_medium=email&utm_source=footer> >> . >> > > > -- > > Michael > -- You received this message because you are subscribed to the Google Groups "pylons-discuss" group. To unsubscribe from this group and stop receiving emails from it, send an email to pylons-discuss+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/pylons-discuss/f1e08a09-0d14-49a5-8652-e47027965234%40googlegroups.com.