Events

Events is an implementation of an Observer pattern for Odoo.

Components

Events

Events are a notification system.

On one side, one or many listeners await for an event to happen. On the other side, when such event happen, a notification is sent to the listeners.

An example of event is: ‘when a record has been created’.

The event system allows to write the notification code in only one place, in one Odoo addon, and to write as many listeners as we want, in different places, different addons.

We’ll see below how the on_record_create is implemented.

Notifier

The first thing is to find where/when the notification should be sent. For the creation of a record, it is in odoo.models.BaseModel.create(). We can inherit from the ‘base’ model to add this line:

class Base(models.AbstractModel):
    _inherit = 'base'

    @api.model
    def create(self, vals):
        record = super(Base, self).create(vals)
        self._event('on_record_create').notify(record, fields=vals.keys())
        return record

The models.base.Base._event() method has been added to the ‘base’ model, so an event can be notified from any model. The CollectedEvents.notify() method triggers the event and forward the arguments to the listeners.

This should be done only once. See models.base.Base for a list of events that are implemented in the ‘base’ model.

Listeners

Listeners are Components that respond to the event names. The components must have a _usage equals to 'event.listener', but it doesn’t to be set manually if the component inherits from 'base.event.listener'

Here is how we would log something each time a record is created:

class MyEventListener(Component):
    _name = 'my.event.listener'
    _inherit = 'base.event.listener'

    def on_record_create(self, record, fields=None):
        _logger.info("%r has been created", record)

Many listeners such as this one could be added for the same event.

Collection and models

In the example above, the listeners is global. It will be executed for any model and collection. You can also restrict a listener to only a collection or model, using the _collection or _apply_on attributes.

class MyEventListener(Component):
    _name = 'my.event.listener'
    _inherit = 'base.event.listener'
    _collection = 'magento.backend'

    def on_record_create(self, record, fields=None):
        _logger.info("%r has been created", record)


class MyModelEventListener(Component):
    _name = 'my.event.listener'
    _inherit = 'base.event.listener'
    _apply_on = ['res.users']

    def on_record_create(self, record, fields=None):
        _logger.info("%r has been created", record)

If you want an event to be restricted to a collection, the notification must also precise the collection, otherwise all listeners will be executed:

collection = self.env['magento.backend']
self._event('on_foo_created', collection=collection).notify(record, vals)

An event can be skipped based on a condition evaluated from the notified arguments. See skip_if()

odoo.addons.component_event.components.event.skip_if(cond)[source]

Decorator allowing to skip an event based on a condition

The condition is a python lambda expression, which takes the same arguments than the event.

Example:

@skip_if(lambda self, *args, **kwargs:
         self.env.context.get('connector_no_export'))
def on_record_write(self, record, fields=None):
    _logger('I'll delay a job, but only if we didn't disabled '
            ' the export with a context key')
    record.with_delay().export_record()

@skip_if(lambda self, record, kind: kind == 'complete')
def on_record_write(self, record, kind):
    _logger("I'll delay a job, but only if the kind is 'complete'")
    record.with_delay().export_record()
class odoo.addons.component_event.components.event.CollectedEvents(events)[source]

Bases: object

Event methods ready to be notified

This is a rather internal class. An instance of this class is prepared by the EventCollecter when we need to notify the listener that the event has been triggered.

EventCollecter.collect_events() collects the events, feed them to the instance, so we can use the notify() method that will forward the arguments and keyword arguments to the listeners of the event.

>>> # collecter is an instance of CollectedEvents
>>> collecter.collect_events('on_record_create').notify(something)
notify(*args, **kwargs)[source]

Forward the arguments to every listeners of an event

class odoo.addons.component_event.components.event.EventCollecter(work_context)[source]

Bases: odoo.addons.component.core.Component

Component that collects the event from an event name

For doing so, it searches all the components that respond to the event.listener _usage and having an event of the same name.

Then it feeds the events to an instance of EventCollecter and return it to the caller.

It keeps the results in a cache, the Component is rebuilt when the Odoo’s registry is rebuilt, hence the cache is cleared as well.

An event always starts with on_.

Note that the special odoo.addons.component_event.core.EventWorkContext class should be used for this Component, because it can work without a collection.

It is used by odoo.addons.component_event.models.base.Base._event().

collect_events(name)[source]

Collect the events of a given name

class odoo.addons.component_event.components.event.EventListener(work_context)[source]

Bases: odoo.addons.component.core.AbstractComponent

Base Component for the Event listeners

Events must be methods starting with on_.

Example: RecordsEventListener

classmethod has_event(name)[source]

Indicate if the class has an event of this name

Odoo Models Extensions

Base Model

Extend the ‘base’ Odoo Model to add Events related features.

class odoo.addons.component_event.models.base.Base(pool, cr)[source]

Bases: odoo.models.BaseModel

The base model, which is implicitly inherited by all models.

Add an _event() method to all Models. This method allows to trigger events.

It also notifies the following events:

  • on_record_create(self, record, fields=None)
  • on_record_write(self, record, fields=none)
  • on_record_unlink(self, record)

on_record_unlink is notified just before the unlink is done.

create(vals)[source]
write(vals)[source]

Internals

Events Internals

Core classes for the events system.

class odoo.addons.component_event.core.EventWorkContext(model_name=None, collection=None, env=None, components_registry=None, **kwargs)[source]

Bases: odoo.addons.component.core.WorkContext

Work context used by the Events internals

Should not be used outside of the events internals. The work context to use generally is odoo.addons.component.core.WorkContext or your own subclass.

The events are a special kind of components because they are not attached to any collection (they can but not the main use case).

So the work context must not need to have a collection, but when it has no collection, it must at least have an ‘env’.

When no collection is provided, the methods to get the Components cannot be used, but work_on() can be used to switch back to a odoo.addons.component.core.WorkContext with collection. This is needed when one want to get a component for a collection from inside an event listener.

env

Return the current Odoo env

collection

Return the current Odoo env

work_on(model_name=None, collection=None)[source]

Create a new work context for another model keeping attributes

Used when one need to lookup components for another model.

Used on an EventWorkContext, it switch back to a normal WorkContext. It means we are inside an event listener, and we want to get a component. We need to set a collection to be able to get components.

component_by_name(name, model_name=None)[source]
component(usage=None, model_name=None)[source]
many_components(usage=None, model_name=None)[source]