[ADD] openerp.http reference doc
* fix some docstrings so they can be autodoc'd * intersphinx mapping (and links to) werkzeug and python
This commit is contained in:
parent
afcb89ab3a
commit
cccd3c888f
12
doc/conf.py
12
doc/conf.py
|
@ -18,7 +18,13 @@ needs_sphinx = '1.1'
|
|||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.todo', 'sphinx.ext.autodoc', 'odoodoc', 'patchqueue']
|
||||
extensions = [
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.intersphinx',
|
||||
'odoodoc',
|
||||
'patchqueue'
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
@ -152,3 +158,7 @@ html_sidebars = {
|
|||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
intersphinx_mapping = {
|
||||
'python': ('https://docs.python.org/2/', None),
|
||||
'werkzeug': ('http://werkzeug.pocoo.org/docs/0.9/', None),
|
||||
}
|
||||
|
|
|
@ -12,16 +12,72 @@ Routing
|
|||
Request
|
||||
=======
|
||||
|
||||
The request object is automatically set on :data:`openerp.http.request` at
|
||||
the start of the request
|
||||
|
||||
.. autoclass:: openerp.http.WebRequest
|
||||
:members:
|
||||
:member-order: bysource
|
||||
.. autoclass:: openerp.http.HttpRequest
|
||||
:members:
|
||||
.. autoclass:: openerp.http.JsonRequest
|
||||
:members:
|
||||
|
||||
Response
|
||||
========
|
||||
|
||||
JSON-RPC
|
||||
========
|
||||
.. autoclass:: openerp.http.Response
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
.. maybe set this to document all the fine methods on Werkzeug's Response
|
||||
object? (it works)
|
||||
:inherited-members:
|
||||
|
||||
.. _reference/http/controllers:
|
||||
|
||||
Extension: controllers
|
||||
======================
|
||||
Controllers
|
||||
===========
|
||||
|
||||
.. this should be about inheritance/extension, do web controllers still do
|
||||
anything else nowadays?
|
||||
Controllers need to provide extensibility, much like
|
||||
:class:`~openerp.models.Model`, but can't use the same mechanism as the
|
||||
pre-requisites (a database with loaded modules) may not be available yet (e.g.
|
||||
no database created, or no database selected).
|
||||
|
||||
Controllers thus provide their own extension mechanism, separate from that of
|
||||
models:
|
||||
|
||||
Controllers are created by :ref:`inheriting <python:tut-inheritance>` from
|
||||
|
||||
.. autoclass:: openerp.http.Controller
|
||||
|
||||
and defining methods decorated with :func:`~openerp.http.route`::
|
||||
|
||||
class MyController(openerp.http.Controller):
|
||||
@route('/some_url', auth='public')
|
||||
def handler(self):
|
||||
return stuff()
|
||||
|
||||
To *override* a controller, :ref:`inherit <python:tut-inheritance>` from its
|
||||
class and override relevant methods::
|
||||
|
||||
class Extension(MyController):
|
||||
@route()
|
||||
def handler(self):
|
||||
do_before()
|
||||
return super(Extension, self).handler()
|
||||
|
||||
* decorating with :func:`~openerp.http.route` is necessary to keep the method
|
||||
(and route) visible: if the method is redefined without decorating, it
|
||||
will be "unpublished"
|
||||
* the decorators of all methods are combined, if the overriding method's
|
||||
decorator has no argument all previous ones will be kept, any provided
|
||||
argument will override previously defined ones e.g.::
|
||||
|
||||
class Restrict(MyController):
|
||||
@route(auth='user')
|
||||
def handler(self):
|
||||
return super(Restrict, self).handler()
|
||||
|
||||
will change ``/some_url`` from public authentication to user (requiring a
|
||||
log-in)
|
||||
|
|
211
openerp/http.py
211
openerp/http.py
|
@ -142,7 +142,7 @@ def redirect_with_hash(url, code=303):
|
|||
return "<html><head><script>window.location = '%s' + location.hash;</script></head></html>" % url
|
||||
|
||||
class WebRequest(object):
|
||||
""" Parent class for all OpenERP Web request types, mostly deals with
|
||||
""" Parent class for all Odoo Web request types, mostly deals with
|
||||
initialization and setup of the request object (the dispatching itself has
|
||||
to be handled by the subclasses)
|
||||
|
||||
|
@ -154,60 +154,20 @@ class WebRequest(object):
|
|||
the original :class:`werkzeug.wrappers.Request` object provided to the
|
||||
request
|
||||
|
||||
.. attribute:: httpsession
|
||||
|
||||
.. deprecated:: 8.0
|
||||
|
||||
Use :attr:`session` instead.
|
||||
|
||||
.. attribute:: params
|
||||
|
||||
:class:`~collections.Mapping` of request parameters, not generally
|
||||
useful as they're provided directly to the handler method as keyword
|
||||
arguments
|
||||
|
||||
.. attribute:: session_id
|
||||
|
||||
opaque identifier for the :class:`OpenERPSession` instance of
|
||||
the current request
|
||||
|
||||
.. attribute:: session
|
||||
|
||||
a :class:`OpenERPSession` holding the HTTP session data for the
|
||||
current http session
|
||||
|
||||
.. attribute:: context
|
||||
|
||||
:class:`~collections.Mapping` of context values for the current
|
||||
request
|
||||
|
||||
.. attribute:: db
|
||||
|
||||
``str``, the name of the database linked to the current request. Can
|
||||
be ``None`` if the current request uses the ``none`` authentication
|
||||
in ``web`` module's controllers.
|
||||
|
||||
.. attribute:: uid
|
||||
|
||||
``int``, the id of the user related to the current request. Can be
|
||||
``None`` if the current request uses the ``none`` authentication.
|
||||
|
||||
.. attribute:: env
|
||||
|
||||
an :class:`openerp.api.Environment` bound to the current
|
||||
request's ``cr``, ``uid`` and ``context``
|
||||
"""
|
||||
def __init__(self, httprequest):
|
||||
self.httprequest = httprequest
|
||||
self.httpresponse = None
|
||||
self.httpsession = httprequest.session
|
||||
self.session = httprequest.session
|
||||
self.session_id = httprequest.session.sid
|
||||
self.disable_db = False
|
||||
self.uid = None
|
||||
self.endpoint = None
|
||||
self.auth_method = None
|
||||
self._cr_cm = None
|
||||
self._cr = None
|
||||
|
||||
# prevents transaction commit, use when you catch an exception during handling
|
||||
|
@ -219,44 +179,49 @@ class WebRequest(object):
|
|||
threading.current_thread().dbname = self.db
|
||||
if self.session.uid:
|
||||
threading.current_thread().uid = self.session.uid
|
||||
self.context = dict(self.session.context)
|
||||
self.lang = self.context["lang"]
|
||||
|
||||
@property
|
||||
def registry(self):
|
||||
"""
|
||||
The registry to the database linked to this request. Can be ``None``
|
||||
if the current request uses the ``none`` authentication.
|
||||
"""
|
||||
return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None
|
||||
|
||||
@property
|
||||
def db(self):
|
||||
"""
|
||||
The database linked to this request. Can be ``None``
|
||||
if the current request uses the ``none`` authentication.
|
||||
"""
|
||||
return self.session.db if not self.disable_db else None
|
||||
|
||||
@property
|
||||
def cr(self):
|
||||
"""
|
||||
The cursor initialized for the current method call. If the current
|
||||
request uses the ``none`` authentication trying to access this
|
||||
property will raise an exception.
|
||||
"""
|
||||
# some magic to lazy create the cr
|
||||
if not self._cr:
|
||||
self._cr = self.registry.cursor()
|
||||
return self._cr
|
||||
|
||||
@lazy_property
|
||||
def env(self):
|
||||
"""
|
||||
The Environment bound to current request.
|
||||
The :class:`~openerp.api.Environment` bound to current request.
|
||||
"""
|
||||
return openerp.api.Environment(self.cr, self.uid, self.context)
|
||||
|
||||
@lazy_property
|
||||
def context(self):
|
||||
"""
|
||||
:class:`~collections.Mapping` of context values for the current
|
||||
request
|
||||
"""
|
||||
return dict(self.session.context)
|
||||
|
||||
@lazy_property
|
||||
def lang(self):
|
||||
return self.context["lang"]
|
||||
|
||||
@lazy_property
|
||||
def session(self):
|
||||
"""
|
||||
a :class:`OpenERPSession` holding the HTTP session data for the
|
||||
current http session
|
||||
"""
|
||||
return self.httprequest.session
|
||||
|
||||
@property
|
||||
def cr(self):
|
||||
"""
|
||||
:class:`~openerp.sql_db.Cursor` initialized for the current method
|
||||
call.
|
||||
|
||||
Accessing the cursor when the current request uses the ``none``
|
||||
authentication will raise an exception.
|
||||
"""
|
||||
# can not be a lazy_property because manual rollback in _call_function
|
||||
# if already set (?)
|
||||
if not self._cr:
|
||||
self._cr = self.registry.cursor()
|
||||
return self._cr
|
||||
|
||||
def __enter__(self):
|
||||
_request_stack.push(self)
|
||||
return self
|
||||
|
@ -316,6 +281,8 @@ class WebRequest(object):
|
|||
|
||||
@property
|
||||
def debug(self):
|
||||
""" Indicates whether the current request is in "debug" mode
|
||||
"""
|
||||
return 'debug' in self.httprequest.args
|
||||
|
||||
@contextlib.contextmanager
|
||||
|
@ -323,6 +290,48 @@ class WebRequest(object):
|
|||
warnings.warn('please use request.registry and request.cr directly', DeprecationWarning)
|
||||
yield (self.registry, self.cr)
|
||||
|
||||
@lazy_property
|
||||
def session_id(self):
|
||||
"""
|
||||
opaque identifier for the :class:`OpenERPSession` instance of
|
||||
the current request
|
||||
|
||||
.. deprecated:: 8.0
|
||||
|
||||
Use the ``id`` attribute on :attr:`.session`
|
||||
"""
|
||||
return self.session.id
|
||||
|
||||
@property
|
||||
def registry(self):
|
||||
"""
|
||||
The registry to the database linked to this request. Can be ``None``
|
||||
if the current request uses the ``none`` authentication.
|
||||
|
||||
.. deprecated:: 8.0
|
||||
|
||||
use :attr:`.env`
|
||||
"""
|
||||
return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None
|
||||
|
||||
@property
|
||||
def db(self):
|
||||
"""
|
||||
The database linked to this request. Can be ``None``
|
||||
if the current request uses the ``none`` authentication.
|
||||
"""
|
||||
return self.session.db if not self.disable_db else None
|
||||
|
||||
@lazy_property
|
||||
def httpsession(self):
|
||||
""" HTTP session data
|
||||
|
||||
.. deprecated:: 8.0
|
||||
|
||||
Use :attr:`.session` instead.
|
||||
"""
|
||||
return self.session
|
||||
|
||||
def route(route=None, **kw):
|
||||
"""
|
||||
Decorator marking the decorated method as being a handler for
|
||||
|
@ -378,7 +387,15 @@ def route(route=None, **kw):
|
|||
return decorator
|
||||
|
||||
class JsonRequest(WebRequest):
|
||||
""" JSON-RPC2 over HTTP.
|
||||
""" Request handler for `JSON-RPC 2
|
||||
<http://www.jsonrpc.org/specification>`_ over HTTP
|
||||
|
||||
* ``method`` is ignored
|
||||
* ``params`` must be a JSON object (not an array) and is passed as keyword
|
||||
arguments to the handler method
|
||||
* the handler method's result is returned as JSON-RPC ``result`` and
|
||||
wrapped in the `JSON-RPC Response
|
||||
<http://www.jsonrpc.org/specification#response_object>`_
|
||||
|
||||
Sucessful request::
|
||||
|
||||
|
@ -490,8 +507,6 @@ class JsonRequest(WebRequest):
|
|||
return self._json_response(error=error)
|
||||
|
||||
def dispatch(self):
|
||||
""" Calls the method asked for by the JSON-RPC2 or JSONP request
|
||||
"""
|
||||
if self.jsonp_handler:
|
||||
return self.jsonp_handler()
|
||||
try:
|
||||
|
@ -541,7 +556,23 @@ def jsonrequest(f):
|
|||
return route([base, base + "/<path:_ignored_path>"], type="json", auth="user", combine=True)(f)
|
||||
|
||||
class HttpRequest(WebRequest):
|
||||
""" Regular GET/POST request
|
||||
""" Handler for the ``http`` request type.
|
||||
|
||||
matched routing parameters, query string parameters, form_ parameters
|
||||
and files are passed to the handler method as keyword arguments.
|
||||
|
||||
In case of name conflict, routing parameters have priority.
|
||||
|
||||
The handler method's result can be:
|
||||
|
||||
* a falsy value, in which case the HTTP response will be an
|
||||
`HTTP 204`_ (No Content)
|
||||
* a werkzeug Response object, which is returned as-is
|
||||
* a ``str`` or ``unicode``, will be wrapped in a Response object and
|
||||
interpreted as HTML
|
||||
|
||||
.. _form: http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2
|
||||
.. _HTTP 204: http://tools.ietf.org/html/rfc7231#section-6.3.5
|
||||
"""
|
||||
_request_type = "http"
|
||||
|
||||
|
@ -596,7 +627,7 @@ class HttpRequest(WebRequest):
|
|||
return response
|
||||
|
||||
def render(self, template, qcontext=None, lazy=True, **kw):
|
||||
""" Lazy render of QWeb template.
|
||||
""" Lazy render of a QWeb template.
|
||||
|
||||
The actual rendering of the given template will occur at then end of
|
||||
the dispatching. Meanwhile, the template and/or qcontext can be
|
||||
|
@ -604,7 +635,9 @@ class HttpRequest(WebRequest):
|
|||
|
||||
:param basestring template: template to render
|
||||
:param dict qcontext: Rendering context to use
|
||||
:param dict lazy: Lazy rendering is processed later in wsgi response layer (default True)
|
||||
:param bool lazy: whether the template rendering should be deferred
|
||||
until the last possible moment
|
||||
:param kw: forwarded to werkzeug's Response object
|
||||
"""
|
||||
response = Response(template=template, qcontext=qcontext, **kw)
|
||||
if not lazy:
|
||||
|
@ -612,7 +645,9 @@ class HttpRequest(WebRequest):
|
|||
return response
|
||||
|
||||
def not_found(self, description=None):
|
||||
""" Helper for 404 response, return its result from the method
|
||||
""" Shortcut for a `HTTP 404
|
||||
<http://tools.ietf.org/html/rfc7231#section-6.5.4>`_ (Not Found)
|
||||
response
|
||||
"""
|
||||
return werkzeug.exceptions.NotFound(description)
|
||||
|
||||
|
@ -1073,13 +1108,20 @@ class Retry(RuntimeError):
|
|||
class Response(werkzeug.wrappers.Response):
|
||||
""" Response object passed through controller route chain.
|
||||
|
||||
In addition to the werkzeug.wrappers.Response parameters, this
|
||||
classe's constructor can take the following additional parameters
|
||||
In addition to the :class:`werkzeug.wrappers.Response` parameters, this
|
||||
class's constructor can take the following additional parameters
|
||||
for QWeb Lazy Rendering.
|
||||
|
||||
:param basestring template: template to render
|
||||
:param dict qcontext: Rendering context to use
|
||||
:param int uid: User id to use for the ir.ui.view render call
|
||||
:param int uid: User id to use for the ir.ui.view render call,
|
||||
``None`` to use the request's user (the default)
|
||||
|
||||
these attributes are available as parameters on the Response object and
|
||||
can be altered at any time before rendering
|
||||
|
||||
Also exposes all the attributes and methods of
|
||||
:class:`werkzeug.wrappers.Response`.
|
||||
"""
|
||||
default_mimetype = 'text/html'
|
||||
def __init__(self, *args, **kw):
|
||||
|
@ -1108,6 +1150,8 @@ class Response(werkzeug.wrappers.Response):
|
|||
return self.template is not None
|
||||
|
||||
def render(self):
|
||||
""" Renders the Response's template, returns the result
|
||||
"""
|
||||
view_obj = request.registry["ir.ui.view"]
|
||||
uid = self.uid or request.uid or openerp.SUPERUSER_ID
|
||||
while True:
|
||||
|
@ -1119,6 +1163,9 @@ class Response(werkzeug.wrappers.Response):
|
|||
self.qcontext.update(e.updates)
|
||||
|
||||
def flatten(self):
|
||||
""" Forces the rendering of the response's template, sets the result
|
||||
as response body and unsets :attr:`.template`
|
||||
"""
|
||||
self.response.append(self.render())
|
||||
self.template = None
|
||||
|
||||
|
|
|
@ -42,6 +42,10 @@ class lazy_property(object):
|
|||
setattr(obj, self.fget.__name__, value)
|
||||
return value
|
||||
|
||||
@property
|
||||
def __doc__(self):
|
||||
return self.fget.__doc__
|
||||
|
||||
@staticmethod
|
||||
def reset_all(obj):
|
||||
""" Reset all lazy properties on the instance `obj`. """
|
||||
|
|
Loading…
Reference in New Issue