[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
|
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
# 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.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['_templates']
|
templates_path = ['_templates']
|
||||||
|
@ -152,3 +158,7 @@ html_sidebars = {
|
||||||
# base URL from which the finished HTML is served.
|
# base URL from which the finished HTML is served.
|
||||||
#html_use_opensearch = ''
|
#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
|
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
|
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:
|
.. _reference/http/controllers:
|
||||||
|
|
||||||
Extension: controllers
|
Controllers
|
||||||
======================
|
===========
|
||||||
|
|
||||||
.. this should be about inheritance/extension, do web controllers still do
|
Controllers need to provide extensibility, much like
|
||||||
anything else nowadays?
|
: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
|
return "<html><head><script>window.location = '%s' + location.hash;</script></head></html>" % url
|
||||||
|
|
||||||
class WebRequest(object):
|
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
|
initialization and setup of the request object (the dispatching itself has
|
||||||
to be handled by the subclasses)
|
to be handled by the subclasses)
|
||||||
|
|
||||||
|
@ -154,60 +154,20 @@ class WebRequest(object):
|
||||||
the original :class:`werkzeug.wrappers.Request` object provided to the
|
the original :class:`werkzeug.wrappers.Request` object provided to the
|
||||||
request
|
request
|
||||||
|
|
||||||
.. attribute:: httpsession
|
|
||||||
|
|
||||||
.. deprecated:: 8.0
|
|
||||||
|
|
||||||
Use :attr:`session` instead.
|
|
||||||
|
|
||||||
.. attribute:: params
|
.. attribute:: params
|
||||||
|
|
||||||
:class:`~collections.Mapping` of request parameters, not generally
|
:class:`~collections.Mapping` of request parameters, not generally
|
||||||
useful as they're provided directly to the handler method as keyword
|
useful as they're provided directly to the handler method as keyword
|
||||||
arguments
|
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):
|
def __init__(self, httprequest):
|
||||||
self.httprequest = httprequest
|
self.httprequest = httprequest
|
||||||
self.httpresponse = None
|
self.httpresponse = None
|
||||||
self.httpsession = httprequest.session
|
self.httpsession = httprequest.session
|
||||||
self.session = httprequest.session
|
|
||||||
self.session_id = httprequest.session.sid
|
|
||||||
self.disable_db = False
|
self.disable_db = False
|
||||||
self.uid = None
|
self.uid = None
|
||||||
self.endpoint = None
|
self.endpoint = None
|
||||||
self.auth_method = None
|
self.auth_method = None
|
||||||
self._cr_cm = None
|
|
||||||
self._cr = None
|
self._cr = None
|
||||||
|
|
||||||
# prevents transaction commit, use when you catch an exception during handling
|
# prevents transaction commit, use when you catch an exception during handling
|
||||||
|
@ -219,44 +179,49 @@ class WebRequest(object):
|
||||||
threading.current_thread().dbname = self.db
|
threading.current_thread().dbname = self.db
|
||||||
if self.session.uid:
|
if self.session.uid:
|
||||||
threading.current_thread().uid = 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
|
@lazy_property
|
||||||
def env(self):
|
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)
|
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):
|
def __enter__(self):
|
||||||
_request_stack.push(self)
|
_request_stack.push(self)
|
||||||
return self
|
return self
|
||||||
|
@ -316,6 +281,8 @@ class WebRequest(object):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def debug(self):
|
def debug(self):
|
||||||
|
""" Indicates whether the current request is in "debug" mode
|
||||||
|
"""
|
||||||
return 'debug' in self.httprequest.args
|
return 'debug' in self.httprequest.args
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
|
@ -323,6 +290,48 @@ class WebRequest(object):
|
||||||
warnings.warn('please use request.registry and request.cr directly', DeprecationWarning)
|
warnings.warn('please use request.registry and request.cr directly', DeprecationWarning)
|
||||||
yield (self.registry, self.cr)
|
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):
|
def route(route=None, **kw):
|
||||||
"""
|
"""
|
||||||
Decorator marking the decorated method as being a handler for
|
Decorator marking the decorated method as being a handler for
|
||||||
|
@ -378,7 +387,15 @@ def route(route=None, **kw):
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
class JsonRequest(WebRequest):
|
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::
|
Sucessful request::
|
||||||
|
|
||||||
|
@ -490,8 +507,6 @@ class JsonRequest(WebRequest):
|
||||||
return self._json_response(error=error)
|
return self._json_response(error=error)
|
||||||
|
|
||||||
def dispatch(self):
|
def dispatch(self):
|
||||||
""" Calls the method asked for by the JSON-RPC2 or JSONP request
|
|
||||||
"""
|
|
||||||
if self.jsonp_handler:
|
if self.jsonp_handler:
|
||||||
return self.jsonp_handler()
|
return self.jsonp_handler()
|
||||||
try:
|
try:
|
||||||
|
@ -541,7 +556,23 @@ def jsonrequest(f):
|
||||||
return route([base, base + "/<path:_ignored_path>"], type="json", auth="user", combine=True)(f)
|
return route([base, base + "/<path:_ignored_path>"], type="json", auth="user", combine=True)(f)
|
||||||
|
|
||||||
class HttpRequest(WebRequest):
|
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"
|
_request_type = "http"
|
||||||
|
|
||||||
|
@ -596,7 +627,7 @@ class HttpRequest(WebRequest):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def render(self, template, qcontext=None, lazy=True, **kw):
|
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 actual rendering of the given template will occur at then end of
|
||||||
the dispatching. Meanwhile, the template and/or qcontext can be
|
the dispatching. Meanwhile, the template and/or qcontext can be
|
||||||
|
@ -604,7 +635,9 @@ class HttpRequest(WebRequest):
|
||||||
|
|
||||||
:param basestring template: template to render
|
:param basestring template: template to render
|
||||||
:param dict qcontext: Rendering context to use
|
: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)
|
response = Response(template=template, qcontext=qcontext, **kw)
|
||||||
if not lazy:
|
if not lazy:
|
||||||
|
@ -612,7 +645,9 @@ class HttpRequest(WebRequest):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def not_found(self, description=None):
|
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)
|
return werkzeug.exceptions.NotFound(description)
|
||||||
|
|
||||||
|
@ -1073,13 +1108,20 @@ class Retry(RuntimeError):
|
||||||
class Response(werkzeug.wrappers.Response):
|
class Response(werkzeug.wrappers.Response):
|
||||||
""" Response object passed through controller route chain.
|
""" Response object passed through controller route chain.
|
||||||
|
|
||||||
In addition to the werkzeug.wrappers.Response parameters, this
|
In addition to the :class:`werkzeug.wrappers.Response` parameters, this
|
||||||
classe's constructor can take the following additional parameters
|
class's constructor can take the following additional parameters
|
||||||
for QWeb Lazy Rendering.
|
for QWeb Lazy Rendering.
|
||||||
|
|
||||||
:param basestring template: template to render
|
:param basestring template: template to render
|
||||||
:param dict qcontext: Rendering context to use
|
: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'
|
default_mimetype = 'text/html'
|
||||||
def __init__(self, *args, **kw):
|
def __init__(self, *args, **kw):
|
||||||
|
@ -1108,6 +1150,8 @@ class Response(werkzeug.wrappers.Response):
|
||||||
return self.template is not None
|
return self.template is not None
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
|
""" Renders the Response's template, returns the result
|
||||||
|
"""
|
||||||
view_obj = request.registry["ir.ui.view"]
|
view_obj = request.registry["ir.ui.view"]
|
||||||
uid = self.uid or request.uid or openerp.SUPERUSER_ID
|
uid = self.uid or request.uid or openerp.SUPERUSER_ID
|
||||||
while True:
|
while True:
|
||||||
|
@ -1119,6 +1163,9 @@ class Response(werkzeug.wrappers.Response):
|
||||||
self.qcontext.update(e.updates)
|
self.qcontext.update(e.updates)
|
||||||
|
|
||||||
def flatten(self):
|
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.response.append(self.render())
|
||||||
self.template = None
|
self.template = None
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,10 @@ class lazy_property(object):
|
||||||
setattr(obj, self.fget.__name__, value)
|
setattr(obj, self.fget.__name__, value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def __doc__(self):
|
||||||
|
return self.fget.__doc__
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def reset_all(obj):
|
def reset_all(obj):
|
||||||
""" Reset all lazy properties on the instance `obj`. """
|
""" Reset all lazy properties on the instance `obj`. """
|
||||||
|
|
Loading…
Reference in New Issue