Discussion:
[pylons-discuss] is it possible to execute code at a certain point in accessing a view?
Jonathan Vanasco
2018-05-06 20:27:56 UTC
Permalink
I'm doing some housekeeping on an app that has been a bit too lax on
keeping to it's own coding standards.

There have been a bunch of updates over the past few years to the views
systems, so I'm hoping something may work for our needs...

In a handful of sections, it utilize class based views that rely on
inheritance for setup routines.

for example...


class Foo(object):
def __init__(self, request):
self.request = request
... common setup ...

@view_config(route_name="bar",)
def bar(self):
pass

I was wondering if it is possible to hook into pyramid after the Foo() is
instantiated, but before `Foo.bar` is called.

What I want to accomplish, in case someone has a better suggestion:

* The views i'm dealing with generally handle form processing on an API.
* There are a handful of common setup and form validation routines that
happen on these
* I'd like to define and trigger the common validation in a parent class,
to ensure it runs. a handful of views were not calling the correct
validation routines, because people make easy mistakes like that.
* I could integrate this processing into __init__, but error reporting
would be much easier if it occurs after __init__, so I can utilize the
class instance.
--
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+***@googlegroups.com.
To post to this group, send email to pylons-***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/pylons-discuss/ee79f36e-f7f1-4f75-abb3-d4e31ca879f0%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Bert JW Regeer
2018-05-06 21:00:55 UTC
Permalink
I'm doing some housekeeping on an app that has been a bit too lax on keeping to it's own coding standards.
There have been a bunch of updates over the past few years to the views systems, so I'm hoping something may work for our needs...
In a handful of sections, it utilize class based views that rely on inheritance for setup routines.
for example...
self.request = request
... common setup ...
@view_config(route_name="bar",)
pass
I was wondering if it is possible to hook into pyramid after the Foo() is instantiated, but before `Foo.bar` is called.
* The views i'm dealing with generally handle form processing on an API.
* There are a handful of common setup and form validation routines that happen on these
* I'd like to define and trigger the common validation in a parent class, to ensure it runs. a handful of views were not calling the correct validation routines, because people make easy mistakes like that.
* I could integrate this processing into __init__, but error reporting would be much easier if it occurs after __init__, so I can utilize the class instance.
If you want to do this you'd have to replace the default view mapper to do the extra work here:

https://github.com/Pylons/pyramid/blob/master/pyramid/viewderivers.py#L114-L139 <https://github.com/Pylons/pyramid/blob/master/pyramid/viewderivers.py#L114-L139>

This deals with instantiating the class and calling the view (either the view attribute or __call__ on the instantiated class). I'd recommend going with a view deriver...
--
You received this message because you are subscribed to the Google Groups "pylons-discuss" group.
To view this discussion on the web visit https://groups.google.com/d/msgid/pylons-discuss/ee79f36e-f7f1-4f75-abb3-d4e31ca879f0%40googlegroups.com <https://groups.google.com/d/msgid/pylons-discuss/ee79f36e-f7f1-4f75-abb3-d4e31ca879f0%40googlegroups.com?utm_medium=email&utm_source=footer>.
For more options, visit https://groups.google.com/d/optout <https://groups.google.com/d/optout>.
For example:

import logging

from pyramid.path import DottedNameResolver

from marshmallow import Schema

From . import ValidationFailure

log = logging.getLogger(__name__)

# request.cstruct validation

def validation_view(view, info):
if info.options.get('validator'):
name_resolver = DottedNameResolver(info.package)
validator = name_resolver.maybe_resolve(info.options.get('validator'))

# If the validator is a subclass of Schema, we assume we need to create
# it and that it has a load function that returns a tuple of
# data/errors
if issubclass(validator, Schema):
def wrapped_validator(jstruct):
(data, errors) = validator().load(jstruct)

# The validator should probably raise by itself...
if errors:
log.debug(errors)
raise ValidationFailure(errors)

return data

validator_func = wrapped_validator
else:
validator_func = validator

def wrapper_view(context, request):
try:
jstruct = request.json_body
except ValueError as e:
log.debug(e)
raise HTTPBadRequest({'error': 'Expecting JSON. Didn\'t work out.'})

if jstruct:
request.appstruct = validator_func(jstruct)

return view(context, request)
return wrapper_view
return view

validation_view.options = ('validator',)

Add it to your config:

# Add the validator view deriver
config.add_view_deriver('.validation.validation_view')

And here is a sample view:

@view_config(
name='forgot',
request_method='POST',
validator='myproject.validation.user.Email',
)
def forgot(self):
request = self.request
dbsession = request.dbsession
appstruct = request.appstruct

user = dbsession.query(m.User)\
.filter(m.User.email == appstruct['email'])\
.one_or_none()

if user is not None:
request.notify(e.UserForgotPassword(request, user))

return HTTPNoContent()

In my case I also allow you to pass a function that validates the input instead of a Marshmallow schema, but the schema works really well for most of my API endpoints. You could off course replace it with Colander or any other validator.

Currently the view deriver just returns the normal view if there is no validator specified, but you could also raise a ConfigError or something similar, you may also require multiple options so you can for example turn off validation for a particular view.

Do note that ValidationFailure takes the marshmallow errors/or your validation function's output and I have a special view that handles ValidationFailures by turning them into HTTPBadRequest's with extra information on what part of the validation failed/why.

Anyway, you get the basic gist of it and hopefully this helps you find a solution to your problem.

Bert JW Regeer
--
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+***@googlegroups.com.
To post to this group, send email to pylons-***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/pylons-discuss/DC8B90FA-EDE8-4A72-B272-F114758E34BA%400x58.com.
For more options, visit https://groups.google.com/d/optout.
Jonathan Vanasco
2018-05-06 21:28:42 UTC
Permalink
Thanks a ton, Bert!

This is exactly the sort of functionality I was hoping for. I had seen the
L114-L139
<https://www.google.com/url?q=https%3A%2F%2Fgithub.com%2FPylons%2Fpyramid%2Fblob%2Fmaster%2Fpyramid%2Fviewderivers.py%23L114-L139&sa=D&sntz=1&usg=AFQjCNGeS7StG0ctOeKc7OrpecKnECRbFQ> reference
you mentioned, so was hoping there was another way. Looks like all these
new pyramid upgrades over the last few years are what I didn't know i
needed.
`
--
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+***@googlegroups.com.
To post to this group, send email to pylons-***@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/pylons-discuss/dc70eb78-d69b-49f9-b698-426d99349d41%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Loading...