[MERGE] trunk

bzr revid: abo@openerp.com-20130214173522-2ama9xjn4c2hloap
This commit is contained in:
Antonin Bourguignon 2013-02-14 18:35:22 +01:00
commit 74ba45d1fc
7 changed files with 115 additions and 12 deletions

View File

@ -6,6 +6,8 @@ Changelog
`trunk`
-------
- Added :ref:`orm-workflows` to the ORM.
- Added :ref:`routing-decorators` to the RPC and WSGI stack.
- Removed support for `__terp__.py` descriptor files.
- Removed support for `<terp>` root element in XML files.
- Removed support for the non-openerp namespace (e.g. importing `tools` instead

View File

@ -35,6 +35,7 @@ OpenERP Server API
.. toctree::
:maxdepth: 1
orm-methods.rst
api_models.rst
routing.rst

61
doc/orm-methods.rst Normal file
View File

@ -0,0 +1,61 @@
.. _orm-methods:
ORM methods
===========
.. _orm-workflows:
Workflow-related methods
------------------------
.. versionadded:: 7.1
Creating, deleting, or otherwise manipulating workflow instances is possible
right from a Model instance. (Previously, workflows were handled throught a
call to ``LocalService('workflow')``. Using the ORM methods is now the preferred
way.)
.. currentmodule:: openerp.osv.orm
.. automethod:: BaseModel.create_workflow
:noindex:
This is used instead of ``LocalService('workflow').trg_create()``.
.. automethod:: BaseModel.delete_workflow
:noindex:
This is used instead of ``LocalService('workflow').trg_delete()``.
.. automethod:: BaseModel.step_workflow
:noindex:
This is used instead of ``LocalService('workflow').trg_write()``.
.. automethod:: BaseModel.redirect_workflow
:noindex:
.. automethod:: BaseModel.signal_workflow
:noindex:
This is used instead of ``LocalService('workflow').trg_validate()``.
.. method:: BaseModel.signal_xxx(cr, uid, ids)
:noindex:
Sends a signal ``xxx`` to the workflow instances bound to the given record
IDs. (This is implemented using ``__getattr__`` so no source link is
rendered on the right.)
This is used instead of ``LocalService('workflow').trg_validate()``.
.. note::
Low-level access to the workflows is still possible by using the
``openerp.workflow`` module, that is, in a similar way to what was possible
with the previous ``LocalService('workflow')`` access. This may be useful
when looking-up a model in the registry and/or its records is not necessary.
For instance when working with raw model names and record IDs is preferred (to
avoid hitting unnecessarily the database). But this is something that should be
carefully considered as it would bypass the ORM methods (and thus any inherited
behaviors).

View File

@ -19,6 +19,8 @@ Starting with OpenERP 7.1, exposing a new arbitrary WSGI handler is done with
the :py:func:`openerp.http.handler` decorator while adding an RPC endpoint is
done with the :py:func:`openerp.http.rpc` decorator.
.. _routing-decorators:
Routing decorators
------------------

View File

@ -3911,20 +3911,44 @@ class BaseModel(object):
returned_ids = [x['id'] for x in cr.dictfetchall()]
self._check_record_rules_result_count(cr, uid, sub_ids, returned_ids, operation, context=context)
def _workflow_trigger(self, cr, uid, ids, trigger, context=None):
"""Call given workflow trigger as a result of a CRUD operation"""
wf_service = netsvc.LocalService("workflow")
def create_workflow(self, cr, uid, ids, context=None):
"""Create a workflow instance for each given record IDs."""
from openerp import workflow
for res_id in ids:
getattr(wf_service, trigger)(uid, self._name, res_id, cr)
workflow.trg_create(uid, self._name, res_id, cr)
return True
def _workflow_signal(self, cr, uid, ids, signal, context=None):
def delete_workflow(self, cr, uid, ids, context=None):
"""Delete the workflow instances bound to the given record IDs."""
from openerp import workflow
for res_id in ids:
workflow.trg_delete(uid, self._name, res_id, cr)
return True
def step_workflow(self, cr, uid, ids, context=None):
"""Reevaluate the workflow instances of the given record IDs."""
from openerp import workflow
for res_id in ids:
workflow.trg_write(uid, self._name, res_id, cr)
return True
def signal_workflow(self, cr, uid, ids, signal, context=None):
"""Send given workflow signal and return a dict mapping ids to workflow results"""
wf_service = netsvc.LocalService("workflow")
from openerp import workflow
result = {}
for res_id in ids:
result[res_id] = wf_service.trg_validate(uid, self._name, res_id, signal, cr)
result[res_id] = workflow.trg_validate(uid, self._name, res_id, signal, cr)
return result
def redirect_workflow(self, cr, uid, old_new_ids, context=None):
""" Rebind the workflow instance bound to the given 'old' record IDs to
the given 'new' IDs. (``old_new_ids`` is a list of pairs ``(old, new)``.
"""
from openerp import workflow
for old_id, new_id in old_new_ids:
workflow.trg_redirect(uid, self._name, old_id, new_id, cr)
return True
def unlink(self, cr, uid, ids, context=None):
"""
Delete records with given ids
@ -3963,7 +3987,7 @@ class BaseModel(object):
property_ids = ir_property.search(cr, uid, [('res_id', 'in', ['%s,%s' % (self._name, i) for i in ids])], context=context)
ir_property.unlink(cr, uid, property_ids, context=context)
self._workflow_trigger(cr, uid, ids, 'trg_delete', context=context)
self.delete_workflow(cr, uid, ids, context=context)
self.check_access_rule(cr, uid, ids, 'unlink', context=context)
pool_model_data = self.pool.get('ir.model.data')
@ -4268,7 +4292,7 @@ class BaseModel(object):
todo.append(id)
self.pool.get(object)._store_set_values(cr, user, todo, fields_to_recompute, context)
self._workflow_trigger(cr, user, ids, 'trg_write', context=context)
self.step_workflow(cr, user, ids, context=context)
return True
#
@ -4484,7 +4508,7 @@ class BaseModel(object):
self.name_get(cr, user, [id_new], context=context)[0][1] + \
"' " + _("created.")
self.log(cr, user, id_new, message, True, context=context)
self._workflow_trigger(cr, user, [id_new], 'trg_create', context=context)
self.create_workflow(cr, user, [id_new], context=context)
return id_new
def browse(self, cr, uid, select, context=None, list_class=None, fields_process=None):
@ -5248,6 +5272,14 @@ class BaseModel(object):
""" stuff to do right after the registry is built """
pass
def __getattr__(self, name):
if name.startswith('signal_'):
signal_name = name[len('signal_'):]
assert signal_name
return (lambda *args, **kwargs:
self.signal_workflow(*args, signal=signal_name, **kwargs))
return super(BaseModel, self).__getattr__(name)
# keep this import here, at top it will cause dependency cycle errors
import expression

View File

@ -166,7 +166,7 @@ def exec_workflow_cr(cr, uid, obj, signal, *args):
if not object:
raise except_orm('Object Error', 'Object %s doesn\'t exist' % str(obj))
res_id = args[0]
return object._workflow_signal(cr, uid, [res_id], signal)[res_id]
return object.signal_workflow(cr, uid, [res_id], signal)[res_id]
@check
def exec_workflow(db, uid, obj, signal, *args):

View File

@ -1,10 +1,15 @@
# -*- coding: utf-8 -*-
import base64
import logging
import sys
import threading
import openerp.netsvc
import openerp.pooler
from openerp import tools
import security
_logger = logging.getLogger(__name__)
@ -105,7 +110,7 @@ def exp_report(db, uid, object, ids, datas=None, context=None):
cr.close()
return True
thread.start_new_thread(go, (id, uid, ids, datas, context))
threading.Thread(target=go, args=(id, uid, ids, datas, context)).start()
return id
def _check_report(report_id):