from functools import partial from collections import Sequence from flask import Blueprint, g, abort, Response, jsonify, request, \ render_template, escape from werkzeug.utils import cached_property from flask.ext.introspect import Tree, TreeRootView, NOTEXIST def setup_tree( objects, root_class, roots=(), leaf_classes=(), template_base=None, xhr_template_base=None, tree_class=Tree, ): if callable(objects): objects = objects() if not isinstance(objects, root_class.__type__): abort(500, 'Root objects type (%s) does not matches with %s.__type___ (%s)' % (escape(type(objects)), root_class.__name__, escape(root_class.__type__))) if roots: g.tree = tree_class(objects, roots, root_class=root_class, leaf_classes=leaf_classes) if template_base: g.template_base = template_base if xhr_template_base: g.xhr_template_base = xhr_template_base def path(path=None): if path is None: if not hasattr(g.tree.root, 'view'): abort(404) g.item = g.tree.root else: (treeitem, tail) = g.tree.get(path) if treeitem is NOTEXIST: abort(404) if not hasattr(treeitem, 'view'): abort(404) g.item = treeitem result = g.item.view().dispatch_request(g.item) if result is None: return '' if isinstance(result, (Response, basestring)): return result if not (isinstance(result, Sequence) and len(result) == 2): if isinstance(result, Sequence): return jsonify(*result) else: return jsonify(result) (template, context) = result # view methods can override this g attributes to change base template if request.is_xhr: base = getattr(g, 'xhr_template_base', None) else: base = getattr(g, 'template_base', None) if base: return render_template(base, template=template, **context) else: return render_template(template, **context) def blueprint( name, import_name, objects, setup=setup_tree, path=path, methods=['get', 'post', 'put', 'patch', 'delete', 'head'], super_root_class=TreeRootView, **kwargs ): bp = Blueprint(name, import_name) bp.before_request(partial(setup_tree, objects, super_root_class, **kwargs)) if hasattr(super_root_class, 'view'): bp.route('/', methods=methods)(path) bp.route('/<path:path>', methods=methods)(path) return bp