def load_models(*chain, **kwargs):
    """
    Decorator to load a chain of models from the given parameters. This works just like
    :func:`load_model` and accepts the same parameters, with some small differences.

    :param chain: The chain is a list of tuples of (``model``, ``attributes``, ``parameter``).
        Lists and tuples can be used interchangeably. All retrieved instances are passed as
        parameters to the decorated function

    :param permission: Same as in :func:`load_model`, except
        :meth:`~coaster.sqlalchemy.PermissionMixin.permissions` is called on every instance
        in the chain and the retrieved permissions are passed as the second parameter to the
        next instance in the chain. This allows later instances to revoke permissions granted
        by earlier instances. As an example, if a URL represents a hierarchy such as
        ``/<page>/<comment>``, the ``page`` can assign ``edit`` and ``delete`` permissions,
        while the ``comment`` can revoke ``edit`` and retain ``delete`` if the current user
        owns the page but not the comment

    In the following example, load_models loads a Folder with a name matching the name in the
    URL, then loads a Page with a matching name and with the just-loaded Folder as parent.
    If the Page provides a 'view' permission to the current user, the decorated
    function is called::

        @app.route('/<folder_name>/<page_name>')
        @load_models(
            (Folder, {'name': 'folder_name'}, 'folder'),
            (Page, {'name': 'page_name', 'parent': 'folder'}, 'page'),
            permission='view')
        def show_page(folder, page):
            return render_template('page.html', folder=folder, page=page)
    """
    def inner(f):
        @wraps(f)
        def decorated_function(*args, **kw):
            permissions = None
            permission_required = kwargs.get('permission')
            url_check_attributes = kwargs.get('urlcheck', [])
            if isinstance(permission_required, six.string_types):
                permission_required = set([permission_required])
            elif permission_required is not None:
                permission_required = set(permission_required)
            result = {}
            for models, attributes, parameter in chain:
                if not isinstance(models, (list, tuple)):
                    models = (models,)
                item = None
                for model in models:
                    query = model.query
                    url_check = False
                    url_check_paramvalues = {}
                    for k, v in attributes.items():
                        if callable(v):
                            query = query.filter_by(**{k: v(result, kw)})
                        else:
                            if '.' in v:
                                first, attrs = v.split('.', 1)
                                val = result.get(first)
                                for attr in attrs.split('.'):
                                    val = getattr(val, attr)
                            else:
                                val = result.get(v, kw.get(v))
                            query = query.filter_by(**{k: val})
                        if k in url_check_attributes:
                            url_check = True
                            url_check_paramvalues[k] = (v, val)
                    item = query.first()
                    if item is not None:
                        # We found it, so don't look in additional models
                        break
                if item is None:
                    abort(404)

                if hasattr(item, 'redirect_view_args'):
                    # This item is a redirect object. Redirect to destination
                    view_args = dict(request.view_args)
                    view_args.update(item.redirect_view_args())
                    location = url_for(request.endpoint, **view_args)
                    if request.query_string:
                        location = location + u'?' + request.query_string.decode()
                    return redirect(location, code=307)

                if permission_required:
                    permissions = item.permissions(current_auth.actor, inherited=permissions)
                    addlperms = kwargs.get('addlperms') or []
                    if callable(addlperms):
                        addlperms = addlperms() or []
                    permissions.update(addlperms)
                if g:  # XXX: Deprecated
                    g.permissions = permissions
                if request:
                    add_auth_attribute('permissions', permissions)
                if url_check and request.method == 'GET':  # Only do urlcheck redirects on GET requests
                    url_redirect = False
                    view_args = None
                    for k, v in url_check_paramvalues.items():
                        uparam, uvalue = v
                        if getattr(item, k) != uvalue:
                            url_redirect = True
                            if view_args is None:
                                view_args = dict(request.view_args)
                            view_args[uparam] = getattr(item, k)
                    if url_redirect:
                        location = url_for(request.endpoint, **view_args)
                        if request.query_string:
                            location = location + u'?' + request.query_string.decode()
                        return redirect(location, code=302)
                if parameter.startswith('g.'):
                    parameter = parameter[2:]
                    setattr(g, parameter, item)
                result[parameter] = item
            if permission_required and not permission_required & permissions:
                abort(403)
            if kwargs.get('kwargs'):
                return f(*args, kwargs=kw, **result)
            else:
                return f(*args, **result)
        return decorated_function
    return inner
Decorator to load a chain of models from the given parameters. This works just like
    :func:`load_model` and accepts the same parameters, with some small differences.

    :param chain: The chain is a list of tuples of (``model``, ``attributes``, ``parameter``).
        Lists and tuples can be used interchangeably. All retrieved instances are passed as
        parameters to the decorated function

    :param permission: Same as in :func:`load_model`, except
        :meth:`~coaster.sqlalchemy.PermissionMixin.permissions` is called on every instance
        in the chain and the retrieved permissions are passed as the second parameter to the
        next instance in the chain. This allows later instances to revoke permissions granted
        by earlier instances. As an example, if a URL represents a hierarchy such as
        ``/<page>/<comment>``, the ``page`` can assign ``edit`` and ``delete`` permissions,
        while the ``comment`` can revoke ``edit`` and retain ``delete`` if the current user
        owns the page but not the comment

    In the following example, load_models loads a Folder with a name matching the name in the
    URL, then loads a Page with a matching name and with the just-loaded Folder as parent.
    If the Page provides a 'view' permission to the current user, the decorated
    function is called::

        @app.route('/<folder_name>/<page_name>')
        @load_models(
            (Folder, {'name': 'folder_name'}, 'folder'),
            (Page, {'name': 'page_name', 'parent': 'folder'}, 'page'),
            permission='view')
        def show_page(folder, page):
            return render_template('page.html', folder=folder, page=page)