Skip to content

Instantly share code, notes, and snippets.

@shazow
Last active December 14, 2015 17:58
Show Gist options
  • Save shazow/5125541 to your computer and use it in GitHub Desktop.
Save shazow/5125541 to your computer and use it in GitHub Desktop.
My Pyramid class-based view base class.
# A sample view from my upcoming meta-framework.
# You probably won't have things like `request.features` or `api` or `expose_api` etc. Coming soon.
@expose_api('account.create')
def account_create(request):
if request.features.get('invite_required'):
raise APIControllerError("Method not permitted: %s" % account_create.exposed_name)
email, password, password_confirm = get_many(request.params, required=['email'], optional=['password', 'password_confirm'])
if password != password_confirm:
raise APIControllerError('Password confirmation does not match.')
if password and len(password) < 3:
raise APIControllerError('Please choose a longer password.')
return {'user': api.account.create(email=email, password=password)}
class AccountController(Controller):
def create(self):
if self.request.features.get('invite_required'):
raise HTTPNotFound()
self.title = 'Create an account'
user_id = api.account.get_user_id(self.request)
if user_id:
return self._redirect(location=self.next)
method = self.request.params.get('method')
if not method:
# Default behaviour, no form submitted.
return self._render('account/create.mako')
try:
# Form submitted, delegate the self.request to the API controller.
r = api_controller(self.request)
except APIControllerError, e:
# API Controller rejected the self.request, re-render the form with the error.
self.session.flash(e.message)
return self._render('account/create.mako')
# Submission processed successfully, login and redirect to the next page.
api.account.login_user_id(self.request, r['user'].id)
self.session.flash('Welcome.')
return self._redirect(location=self.next)
from pyramid.httpexceptions import HTTPSeeOther
from pyramid.renderers import render_to_response, render
class Context(dict):
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
class Controller(object):
"""
This base class can be used in two ways:
1. You can use it as a handler base class as defined in pyramid_handlers.
2. You can use it as a singular route handler if you provide a __call__
definition with the route logic which returns a response.
"""
DEFAULT_NEXT = '/'
title = None
def __init__(self, request):
self.request = request
self.session = request.session
self.context = self.c = Context()
self.previous_url = self.request.referer
self.current_path = self.request.path_qs
self.next = request.params.get('next')
# Prevent cross-site forwards (possible exploit vector).
if not self.next or self.next.startswith('//') or '://' in self.next: # Can we do this better?
self.next = self.DEFAULT_NEXT
def _default_render_values(self):
return {
'c': self.context,
'features': self.request.features,
'request': self.request,
'session': self.session,
'settings': self.request.registry.settings,
'title': self.title,
'is_logged_in': 'user_id' in self.session,
'current_path': self.current_path,
'previous_url': self.previous_url,
'next': self.next,
}
def _get_render_values(self, extra_values=None):
value = self._default_render_values()
if extra_values:
value.update(extra_values)
return value
def _render(self, renderer_name, extra_values=None, package=None):
value = self._get_render_values(extra_values=extra_values)
return render_to_response(renderer_name, value=value, request=self.request, package=package)
def _render_template(self, renderer_name, extra_values=None, package=None):
value = self._get_render_values(extra_values=extra_values)
return render(renderer_name, value=value, request=self.request, package=package)
def _redirect(self, *args, **kw):
"Shortcut for returning pyramid.exceptions.HTTPFound"
return HTTPSeeOther(*args, **kw)
def _respond(self):
"Override this to implement the view's response behavior."
raise NotImplemented('View response not implemented: %s' % self.__class__.__name__)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment