docs: 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