Make Service decorators work with Class Based Views
Opened this issue · 2 comments
Currently it is impossible to use Cornice decorators on CBV's (except @resource decorator),
This is the feedback I got from pyramid developers on what we can do to improve the situation, leaving here for reference.
yeah if I had to guess cornice is only registering a venusian scan for the service
then cornice itself calls config.add_route and config.add_view directly
the decorator is just handled in the service directly
that is my impression... so then it's up to cornice to deal with it itself
if you want to keep things structured that way at least internally...
instead of making them venusian decorators on the methods
for example @foo.get() could be a venusian decorator that under the hood calls
config.add_resource(foo, type='get', ...)
which would be an action that executes before add_view/add_route
this is how you'd restructure the config into pyramid actions at least
but atm cornice does all the config itself at import-time and then just
registers with pyramid at scan-time... cornice doesn't register its own
fine-grained actions that to all the work on the service
Side note: BIG GOTCHA to be aware of:
Pyramid IRC folks confirmed it.
I added the auto-wiring of factory
instance to support pyramidic ACL to @resource
decorated classes and came to discover something interesting about that (#456)
The following construct will cause BaseResource.__init__(self, request)
to be called "twice". Once for the instance of the factory
and the other for the instance of the view
. Pyramid won't care but the unaware coder (me) of a Resource
would be very confused especially if __init__
does some setup that's meant to happen only once.
@resource(collection_path='/items', path='/items/{uuid}')
class Items(BaseResource):
@view(permission='edit')
def get(self):
logging.error("GET")
...
class BaseResource:
def __acl__(self, context=None):
...
def __init__(self, request):
logging.error("BaseResource %r %r ", self, request) # TWO CALLS => 2 INSTANCES!!!
self.request = request
...
Output will be the following:
default.py 54 ERROR BaseResource <xserver.views.default.Items object at 0x1116a8d68> <Request at 0x1116a8278 GET http://localhost/items/0D057D01-B3DE-450A-9A68-9BB8AC3E4DC3>
default.py 46 ERROR __acl__ <xserver.views.default.Items object at 0x1116a8d68> <Request at 0x1116a8278 GET http://localhost/items/0D057D01-B3DE-450A-9A68-9BB8AC3E4DC3>
default.py 54 ERROR BaseResource <xserver.views.default.Items object at 0x11168db70> <Request at 0x1116a8278 GET http://localhost/items/0D057D01-B3DE-450A-9A68-9BB8AC3E4DC3>
default.py 183 ERROR GET <Request at 0x1116a8278 GET http://localhost/items/0D057D01-B3DE-450A-9A68-9BB8AC3E4DC3>
can confirm with pattern like:
class Root(object):
__name__ = 'Root'
__parent__ = None
def __acl__(self):
return [
(Allow, Authenticated, MINIMAL),
...
]
@resource(collection_path='/items', path='/items/{id}',
factory=Root)
class ItemView(object):
def __init__(self, request, context=None):
self.request = request