Class-based views

Group related views into a class for easier management.

coaster.views.classview.rulejoin(class_rule, method_rule)[source]

Join class and method rules. Used internally by ClassView to combine rules from the route() decorators on the class and on the individual view handler methods:

>>> rulejoin('/', '')
'/'
>>> rulejoin('/', 'first')
'/first'
>>> rulejoin('/first', '/second')
'/second'
>>> rulejoin('/first', 'second')
'/first/second'
>>> rulejoin('/first/', 'second')
'/first/second'
>>> rulejoin('/first/<second>', '')
'/first/<second>'
>>> rulejoin('/first/<second>', 'third')
'/first/<second>/third'
coaster.views.classview.current_view = None

A proxy object that holds the currently executing ClassView instance, for use in templates as context. Exposed to templates by coaster.app.init_app(). Note that the current view handler method within the class is named current_handler, so to examine it, use current_view.current_handler.

class coaster.views.classview.ClassView[source]

Base class for defining a collection of views that are related to each other. Subclasses may define methods decorated with route(). When init_app() is called, these will be added as routes to the app.

Typical use:

@route('/')
class IndexView(ClassView):
    @viewdata(title="Homepage")
    @route('')
    def index():
        return render_template('index.html.jinja2')

    @route('about')
    @viewdata(title="About us")
    def about():
        return render_template('about.html.jinja2')

IndexView.init_app(app)

The route() decorator on the class specifies the base rule, which is prefixed to the rule specified on each view method. This example produces two view handlers, for / and /about. Multiple route() decorators may be used in both places.

The viewdata() decorator can be used to specify additional data, and may appear either before or after the route() decorator, but only adjacent to it. Data specified here is available as the data attribute on the view handler, or at runtime in templates as current_view.current_handler.data.

A rudimentary CRUD view collection can be assembled like this:

@route('/doc/<name>')
class DocumentView(ClassView):
    @route('')
    @render_with('mydocument.html.jinja2', json=True)
    def view(self, name):
        document = MyDocument.query.filter_by(name=name).first_or_404()
        return document.current_access()

    @route('edit', methods=['POST'])
    @requestform('title', 'content')
    def edit(self, name, title, content):
        document = MyDocument.query.filter_by(name=name).first_or_404()
        document.title = title
        document.content = content
        return 'edited!'

DocumentView.init_app(app)

See ModelView for a better way to build views around a model.

classmethod add_route_for(_name, rule, **options)[source]

Add a route for an existing method or view. Useful for modifying routes that a subclass inherits from a base class:

class BaseView(ClassView):
    def latent_view(self):
        return 'latent-view'

    @route('other')
    def other_view(self):
        return 'other-view'

@route('/path')
class SubView(BaseView):
    pass

SubView.add_route_for('latent_view', 'latent')
SubView.add_route_for('other_view', 'another')
SubView.init_app(app)

# Created routes:
# /path/latent -> SubView.latent (added)
# /path/other -> SubView.other (inherited)
# /path/another -> SubView.other (added)
Parameters:
  • _name – Name of the method or view on the class
  • rule – URL rule to be added
  • options – Additional options for add_url_rule()
after_request(response)[source]

This method is called with the response from the view handler method. It must return a valid response object. Subclasses and mixin classes may override this to perform any necessary post-processing:

class MyView(ClassView):
    ...
    def after_request(self, response):
        response = super().after_request(response)
        ...  # Process here
        return response
Parameters:response – Response from the view handler method
Returns:Response object
before_request()[source]

This method is called after the app’s before_request handlers, and before the class’s view method. Subclasses and mixin classes may define their own before_request() to pre-process requests. This method receives context via self, in particular via current_handler and view_args.

current_handler = None

When a view is called, this will point to the current view handler, an instance of ViewHandler.

dispatch_request(view, view_args)[source]

View dispatcher that calls before_request, the view, and then after_request. Subclasses may override this to provide a custom flow. ModelView does this to insert a model loading phase.

Parameters:
  • view – View method wrapped in specified decorators. The dispatcher must call this
  • view_args (dict) – View arguments, to be passed on to the view method
classmethod init_app(app, callback=None)[source]

Register views on an app. If callback is specified, it will be called after app.add_url_rule(), with the same parameters.

is_always_available = False

Indicates whether meth:is_available should simply return True without conducting a test. Subclasses should not set this flag. It will be set by init_app() if any view handler is missing an is_available method, as it implies that view is always available.

is_available()[source]

Returns True if any view handler in the class is currently available via its is_available method.

view_args = None

When a view is called, this will be replaced with a dictionary of arguments to the view.

class coaster.views.classview.ModelView(obj=None)[source]

Base class for constructing views around a model. Functionality is provided via mixin classes that must precede ModelView in base class order. Two mixins are provided: UrlForView and InstanceLoader. Sample use:

@route('/doc/<document>')
class DocumentView(UrlForView, InstanceLoader, ModelView):
    model = Document
    route_model_map = {
        'document': 'name'
        }

    @route('')
    @render_with(json=True)
    def view(self):
        return self.obj.current_access()

Document.views.main = DocumentView
DocumentView.init_app(app)

Views will not receive view arguments, unlike in ClassView. If necessary, they are available as self.view_args.

dispatch_request(view, view_args)[source]

View dispatcher that calls before_request(), loader(), after_loader(), the view, and then after_request().

Parameters:
  • view – View method wrapped in specified decorators.
  • view_args (dict) – View arguments, to be passed on to the view method
loader(**view_args)[source]

Subclasses or mixin classes may override this method to provide a model instance loader. The return value of this method will be placed at self.obj.

Returns:Object instance loaded from database
model = None

The model that this view class represents, to be specified by subclasses.

query = None

A base query to use if the model needs special handling.

route_model_map = {}

A mapping of URL rule variables to attributes on the model. For example, if the URL rule is /<parent>/<document>, the attribute map can be:

model = MyModel
route_model_map = {
    'document': 'name',       # Map 'document' in URL to MyModel.name
    'parent': 'parent.name',  # Map 'parent' to MyModel.parent.name
    }

The InstanceLoader mixin class will convert this mapping into SQLAlchemy attribute references to load the instance object.

coaster.views.classview.route(rule, **options)[source]

Decorator for defining routes on a ClassView and its methods. Accepts the same parameters that Flask’s app.route() accepts. See ClassView for usage notes.

coaster.views.classview.viewdata(**kwargs)[source]

Decorator for adding additional data to a view method, to be used alongside route(). This data is accessible as the data attribute on the view handler.

coaster.views.classview.url_change_check(f)[source]

View method decorator that checks the URL of the loaded object in self.obj against the URL in the request (using self.obj.url_for(__name__)). If the URLs do not match, and the request is a GET, it issues a redirect to the correct URL. Usage:

@route('/doc/<document>')
class MyModelView(UrlForView, InstanceLoader, ModelView):
    model = MyModel
    route_model_map = {'document': 'url_id_name'}

    @route('')
    @url_change_check
    @render_with(json=True)
    def view(self):
        return self.obj.current_access()

If the decorator is required for all view handlers in the class, use UrlChangeCheck.

This decorator will only consider the URLs to be different if:

  • Schemes differ (http vs https etc)
  • Hostnames differ (apart from a case difference, as user agents use lowercase)
  • Paths differ

The current URL’s query will be copied to the redirect URL. The URL fragment (#target_id) is not available to the server and will be lost.

coaster.views.classview.requires_roles(roles)[source]

Decorator for ModelView views that limits access to the specified roles.

class coaster.views.classview.UrlChangeCheck[source]

Mixin class for ModelView and UrlForMixin that applies the url_change_check() decorator to all view handler methods. Subclasses UrlForView, which it depends on to register the view with the model so that URLs can be generated. Usage:

@route('/doc/<document>')
class MyModelView(UrlChangeCheck, InstanceLoader, ModelView):
    model = MyModel
    route_model_map = {'document': 'url_id_name'}

    @route('')
    @render_with(json=True)
    def view(self):
        return self.obj.current_access()
class coaster.views.classview.UrlForView[source]

Mixin class for ModelView that registers view handler methods with UrlForMixin’s is_url_for().

class coaster.views.classview.InstanceLoader[source]

Mixin class for ModelView that provides a loader() that attempts to load an instance of the model based on attributes in the route_model_map dictionary.

InstanceLoader will traverse relationships (many-to-one or one-to-one) and perform a SQL JOIN with the target class.