Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Object persistence issue #3137

Closed
TWry opened this issue Jul 25, 2017 · 8 comments
Closed

Object persistence issue #3137

TWry opened this issue Jul 25, 2017 · 8 comments
Labels

Comments

@TWry
Copy link

TWry commented Jul 25, 2017

See code below. I'd expect that the add_event method would automatically persist new events. That is not the case. I have to add a call to transaction.commit in order for this to work. The project has been set up from the cookiecutter template, pyramid version is 1.9.1. Any ideas what's going wrong here, please?

from repoze.folder import Folder


class FooRoot(Folder):
    __name__ = None
    __parent__ = None

    def add_event(self, **kwargs):
        title = kwargs.get('title')
        event = Event(**kwargs)
        self[title] = event
        return event

    
class Event(Folder):
    def __init__(self, **kwargs):
        super().__init__()
        for key, val in kwargs.items():
            setattr(self, key, val)


def appmaker(zodb_root):
    if 'app_root' not in zodb_root:
        app_root = FooRoot()
        zodb_root['app_root'] = app_root
        import transaction
        transaction.commit()
    return zodb_root['app_root']
@tseaver
Copy link
Member

tseaver commented Jul 25, 2017

I haven't used the cookiecutter, but it looks as though its development.ini is lacking the zodb / tm configuration of the older scaffold.

@tseaver
Copy link
Member

tseaver commented Jul 25, 2017

Never mind, those packages get pulled in via config.include.

@TWry
Copy link
Author

TWry commented Jul 25, 2017

Right, Here's the __init__.py:

from pyramid.config import Configurator
from pyramid_zodbconn import get_connection
from .models import appmaker


def root_factory(request):
    conn = get_connection(request)
    return appmaker(conn.root())


def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """
    config = Configurator(root_factory=root_factory, settings=settings)
    settings = config.get_settings()
    settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager'
    config.include('pyramid_chameleon')
    config.include('pyramid_tm')
    config.include('pyramid_retry')
    config.include('pyramid_zodbconn')
    config.add_static_view('static', 'static', cache_max_age=3600)
    config.scan()
    return config.make_wsgi_app()

@tseaver
Copy link
Member

tseaver commented Jul 25, 2017

That configuration looks right: thepyramid_tm tween starts a transaction when the request begins, and commits it when it ends unless the view raises an exception, in which case it aborts the transaction.

Can you show the view code which invokes your FooRoot.add_event method?

@TWry
Copy link
Author

TWry commented Jul 25, 2017

Sure thing:

@view_defaults(renderer='json')
class BaseView:
    def __init__(self, context, request):
        self.context = context
        self.request = request

    @view_config(context=FooRoot)
    def events(self):
        '''GET all events'''
        return self.context

    @view_config(context=FooRoot, request_method='POST')
    def post_event(self):
        'POST a new event'
        return self.context.add_event(**self.request.json_body)

The object is getting created ok as far as I can see. But it is never persisted. Changing _p_changed on it or the root does also not help. (Note: Event init code in my first post corrected)

@mmerickel
Copy link
Member

mmerickel commented Jul 25, 2017

One thing to note is that the scaffold is not using the threadlocal transaction manager so anytime you import transaction; transaction.commit() this is effectively a no-op. You need to grab request.tm and use it instead. As far as your add_event issue it's still not clear what the issue is but presumably the self[title] = event should be enough to persist it if the FooRoot itself is being persisted - which is probably not the case. My guess would be that the scaffold has not taken the necessary steps to attach the connection to the appropriate transaction manager - a quick look at pyramid_zodbconn indicates there is no such way to do it easily right now. If you drop the settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager' line from your main I expect things to work.

edit it looks like pyramid_zodbconn would need support added to use an explicit transaction manager via db.open(request.tm) [1]

[1] http://www.zodb.org/en/latest/guide/transactions-and-threading.html#explicit-transaction-managers

@TWry
Copy link
Author

TWry commented Jul 25, 2017

I confirm that it's working without the settings['tm.manager_hook'] = 'pyramid_tm.explicit_manager' line and without calling transaction.commit in the add_event method. Thanks for the help!

@mmerickel mmerickel added the bugs label Jul 25, 2017
@mmerickel
Copy link
Member

Thanks @TWry - we should be able to release an updated version of pyramid_zodbconn that works with the non-threadlocal transaction managers but until then you'll need to use the global one.

I'm moved this issue into Pylons/pyramid_zodbconn#6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants