901 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
			
		
		
	
	
			901 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
.. _migration-guide:
 | 
						|
 | 
						|
########################################
 | 
						|
Migration Guide to the new Connector API
 | 
						|
########################################
 | 
						|
 | 
						|
During the year 2017, the connector evolved greatly.
 | 
						|
We can recognize three different aspect of the framework, they all have been
 | 
						|
rewritten:
 | 
						|
 | 
						|
* The Job Queue API (:ref:`api-queue`)
 | 
						|
* The Event API (:ref:`api-event`)
 | 
						|
* The ``ConnectorUnit`` API, which is the core of the composability
 | 
						|
  of the Connector. It has been replaced by a standalone addon
 | 
						|
  called ``component``. (:ref:`api-component`)
 | 
						|
 | 
						|
The Connector has been splitted in different addons:
 | 
						|
 | 
						|
* ``queue_job`` in https://github.com/OCA/queue
 | 
						|
* ``component`` in the same repository
 | 
						|
* ``component_event`` in the same repository
 | 
						|
* ``connector`` uses the 3 addons and the parts specifics to the connectors
 | 
						|
 | 
						|
This guide will show how to migrate from the old API to the new one.
 | 
						|
 | 
						|
The previous API will stay until the migration to Odoo 11.0.
 | 
						|
 | 
						|
.. contents:: Sections:
 | 
						|
   :local:
 | 
						|
   :backlinks: top
 | 
						|
   :depth: 2
 | 
						|
 | 
						|
**************
 | 
						|
Migrating Jobs
 | 
						|
**************
 | 
						|
 | 
						|
Jobs are now more integrated within the Odoo API. They are no longer
 | 
						|
standalone functions but are applied on methods of Models.  Another change is
 | 
						|
that they have been extracted into their own addon, so obviously the Python
 | 
						|
paths change.
 | 
						|
 | 
						|
Declaration of a job
 | 
						|
====================
 | 
						|
 | 
						|
Before
 | 
						|
------
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.connector.queue.job import job, related_action
 | 
						|
    from ..related_action import unwrap_binding, link
 | 
						|
 | 
						|
    # function at module-level
 | 
						|
    @job(default_channel='root.magento')
 | 
						|
    @related_action(action=link)
 | 
						|
    def import_record(session, model_name, backend_id, magento_id, force=False):
 | 
						|
        """ Import a record from Magento """
 | 
						|
        # ...
 | 
						|
 | 
						|
    @job(default_channel='root.magento')
 | 
						|
    @related_action(action=unwrap_binding)
 | 
						|
    def export_record(session, model_name, binding_id, fields=None):
 | 
						|
        """ Import a record from Magento """
 | 
						|
        # ...
 | 
						|
 | 
						|
 | 
						|
After
 | 
						|
-----
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.queue_job.job import job, related_action
 | 
						|
    from odoo import api, models
 | 
						|
 | 
						|
 | 
						|
    class MagentoBinding(models.AbstractModel):
 | 
						|
        _name = 'magento.binding'
 | 
						|
        _inherit = 'external.binding'
 | 
						|
        _description = 'Magento Binding (abstract)'
 | 
						|
 | 
						|
        @job(default_channel='root.magento')
 | 
						|
        @related_action(action='related_action_magento_link')
 | 
						|
        @api.model
 | 
						|
        def import_record(self, backend, external_id, force=False):
 | 
						|
            """ Import a Magento record """
 | 
						|
            backend.ensure_one()
 | 
						|
            # ...
 | 
						|
 | 
						|
        @job(default_channel='root.magento')
 | 
						|
        @related_action(action='related_action_unwrap_binding')
 | 
						|
        @api.multi
 | 
						|
        def export_record(self, fields=None):
 | 
						|
            """ Export a record on Magento """
 | 
						|
            self.ensure_one()
 | 
						|
            # ...
 | 
						|
 | 
						|
 | 
						|
Observations
 | 
						|
------------
 | 
						|
 | 
						|
* The job is declared on the generic abstract binding model from which all
 | 
						|
  bindings inherit. This is not a requirement, but for this kind of job it is
 | 
						|
  the perfect fit.
 | 
						|
* ``session``, ``model_name`` and ``binding_id`` are no longer required as they
 | 
						|
  are already known in ``self``.  Jobs can be used as well on ``@api.multi`` and
 | 
						|
  ``@api.model``.
 | 
						|
* Passing arguments as records is supported, in the new version of
 | 
						|
  ``import_record``, no need to browse on the backend if a record was passed
 | 
						|
* The action of a related action is now the name of a method on the
 | 
						|
  ``queue.job`` model.
 | 
						|
* If you need to share a job between several models, put them in an
 | 
						|
  AbstractModel and add an ``_inherit`` on the models.
 | 
						|
 | 
						|
Links
 | 
						|
-----
 | 
						|
 | 
						|
* :meth:`odoo.addons.queue_job.job.job`
 | 
						|
* :meth:`odoo.addons.queue_job.job.related_action`
 | 
						|
 | 
						|
 | 
						|
Invocation of a job
 | 
						|
===================
 | 
						|
 | 
						|
Before
 | 
						|
------
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.connector.session import ConnectorSession
 | 
						|
    from .unit.export_synchronizer import export_record
 | 
						|
 | 
						|
 | 
						|
    class MyBinding(models.Model):
 | 
						|
        _name = 'my.binding'
 | 
						|
        _inherit = 'magento.binding'
 | 
						|
 | 
						|
        @api.multi
 | 
						|
        def button_trigger_export_sync(self):
 | 
						|
            session = ConnectorSession.from_env(self.env)
 | 
						|
            export_record(session, binding._name, self.id, fields=['name'])
 | 
						|
 | 
						|
        @api.multi
 | 
						|
        def button_trigger_export_async(self):
 | 
						|
            session = ConnectorSession.from_env(self.env)
 | 
						|
            export_record.delay(session, self._name, self.id,
 | 
						|
                                fields=['name'], priority=12)
 | 
						|
 | 
						|
 | 
						|
After
 | 
						|
-----
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    class MyBinding(models.Model):
 | 
						|
        _name = 'my.binding'
 | 
						|
 | 
						|
        @api.multi
 | 
						|
        def button_trigger_export_sync(self):
 | 
						|
            self.export_record(fields=['name'])
 | 
						|
 | 
						|
        @api.multi
 | 
						|
        def button_trigger_export_async(self):
 | 
						|
            self.with_delay(priority=12).export_record(fields=['name'])
 | 
						|
 | 
						|
Observations
 | 
						|
------------
 | 
						|
 | 
						|
* No more imports are needed for the invocation
 | 
						|
* ``ConnectorSession`` is now dead
 | 
						|
* Arguments for the job (such as ``priority``) are no longer mixed with the
 | 
						|
  arguments passed to the method
 | 
						|
* When the job is called on a "browse" record, the job will be executed
 | 
						|
  on an instance of this record:
 | 
						|
 | 
						|
  .. code-block:: python
 | 
						|
 | 
						|
      >>> binding = self.env['my.binding'].browse(1)
 | 
						|
      >>> binding.button_trigger_export_async()
 | 
						|
 | 
						|
  In the execution of the job:
 | 
						|
 | 
						|
  .. code-block:: python
 | 
						|
 | 
						|
      @job
 | 
						|
      def export_record(self, fields=None):
 | 
						|
          print self
 | 
						|
          print fields
 | 
						|
      # =>
 | 
						|
      # my.binding,1
 | 
						|
      # ['name']
 | 
						|
 | 
						|
Links
 | 
						|
-----
 | 
						|
 | 
						|
* :meth:`odoo.addons.queue_job.job.job`
 | 
						|
* :meth:`odoo.addons.queue_job.models.base.Base.with_delay`
 | 
						|
 | 
						|
****************
 | 
						|
Migrating Events
 | 
						|
****************
 | 
						|
 | 
						|
Events are now handled by the ``component_event`` addon.
 | 
						|
 | 
						|
Triggering an event
 | 
						|
===================
 | 
						|
 | 
						|
Before
 | 
						|
------
 | 
						|
 | 
						|
First you had to create an :class:`~odoo.addons.connector.event.Event` instance:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    on_record_create = Event()
 | 
						|
 | 
						|
And then import and trigger it, passing a lot of arguments to it:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.connector.event import on_record_create
 | 
						|
 | 
						|
    class Base(models.AbstractModel):
 | 
						|
        """ The base model, which is implicitly inherited by all models. """
 | 
						|
        _inherit = 'base'
 | 
						|
 | 
						|
        @api.model
 | 
						|
        def create(self, vals):
 | 
						|
            record = super(Base, self).create(vals)
 | 
						|
            on_record_create.fire(self.env, self._name, record.id, vals)
 | 
						|
            return record
 | 
						|
 | 
						|
 | 
						|
After
 | 
						|
-----
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    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
 | 
						|
 | 
						|
Observations
 | 
						|
------------
 | 
						|
 | 
						|
* No more imports are needed for the invocation
 | 
						|
  Only the arguments you want to pass should be passed to
 | 
						|
  :meth:`odoo.addons.component_event.components.event.CollectedEvents.notify`.
 | 
						|
* The name of the event must start with ``'on_'``
 | 
						|
 | 
						|
Links
 | 
						|
-----
 | 
						|
 | 
						|
* :mod:`odoo.addons.component_event.components.event`
 | 
						|
 | 
						|
 | 
						|
Listening to an event
 | 
						|
=====================
 | 
						|
 | 
						|
Before
 | 
						|
------
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.connector.event import on_record_create
 | 
						|
 | 
						|
    @on_record_create
 | 
						|
    def delay_export(env, model_name, record_id, vals):
 | 
						|
        if session.context.get('connector_no_export'):
 | 
						|
            return
 | 
						|
        fields = vals.keys()
 | 
						|
        export_record.delay(session, model_name, record_id, fields=fields)
 | 
						|
 | 
						|
    @on_something
 | 
						|
    def do_anything(env, model_name, record_id):
 | 
						|
      # ...
 | 
						|
 | 
						|
After
 | 
						|
-----
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.component.core import Component
 | 
						|
    from odoo.addons.component_event import skip_if
 | 
						|
 | 
						|
    class MagentoListener(Component):
 | 
						|
        _name = 'magento.event.listener'
 | 
						|
        _inherit = 'base.connector.listener'
 | 
						|
 | 
						|
        @skip_if(lambda self, record, **kwargs: self.no_connector_export(record))
 | 
						|
        def on_record_create(self, record, fields=None):
 | 
						|
            """ Called when a record is created """
 | 
						|
            record.with_delay().export_record(fields=fields)
 | 
						|
 | 
						|
        def on_something(self, record):
 | 
						|
            # ...
 | 
						|
 | 
						|
Observations
 | 
						|
------------
 | 
						|
 | 
						|
* The listeners are now components
 | 
						|
* The name of the method is the same than the one notified in the previous
 | 
						|
  section
 | 
						|
* A listener Component might container several listener methods
 | 
						|
* It must inherit from ``'base.event.listener'``, or one of its descendants.
 | 
						|
* The check of the key ``connector_no_export`` in the context can
 | 
						|
  be replaced by the decorator :func:`odoo.addons.component_event.skip_if`
 | 
						|
 | 
						|
Links
 | 
						|
-----
 | 
						|
 | 
						|
* :mod:`odoo.addons.component_event.components.event`
 | 
						|
 | 
						|
 | 
						|
Listening to an event only for some Models
 | 
						|
==========================================
 | 
						|
 | 
						|
Before
 | 
						|
------
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.connector.event import on_record_create
 | 
						|
 | 
						|
    @on_record_create(model_names=['magento.address', 'magento.res.partner'])
 | 
						|
    def delay_export(env, model_name, record_id, vals):
 | 
						|
        if session.context.get('connector_no_export'):
 | 
						|
            return
 | 
						|
        fields = vals.keys()
 | 
						|
        export_record.delay(session, model_name, record_id, fields=fields)
 | 
						|
 | 
						|
After
 | 
						|
-----
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.component.core import Component
 | 
						|
    from odoo.addons.component_event import skip_if
 | 
						|
 | 
						|
    class MagentoListener(Component):
 | 
						|
        _name = 'magento.event.listener'
 | 
						|
        _inherit = 'base.event.listener'
 | 
						|
        _apply_on = ['magento.address', 'magento.res.partner']
 | 
						|
 | 
						|
        @skip_if(lambda self, record, **kwargs: self.no_connector_export(record))
 | 
						|
        def on_record_create(self, record, fields=None):
 | 
						|
            """ Called when a record is created """
 | 
						|
            record.with_delay().export_record(fields=fields)
 | 
						|
 | 
						|
Observations
 | 
						|
------------
 | 
						|
 | 
						|
* Same than previous example but we added ``_apply_on`` on the Component.
 | 
						|
 | 
						|
Links
 | 
						|
-----
 | 
						|
 | 
						|
* :mod:`odoo.addons.component_event.components.event`
 | 
						|
 | 
						|
 | 
						|
********************
 | 
						|
Migrating Components
 | 
						|
********************
 | 
						|
 | 
						|
Backends
 | 
						|
========
 | 
						|
 | 
						|
Before
 | 
						|
------
 | 
						|
 | 
						|
You could have several versions for a backend:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    magento = backend.Backend('magento')
 | 
						|
    """ Generic Magento Backend """
 | 
						|
 | 
						|
    magento1700 = backend.Backend(parent=magento, version='1.7')
 | 
						|
    """ Magento Backend for version 1.7 """
 | 
						|
 | 
						|
    magento1900 = backend.Backend(parent=magento, version='1.9')
 | 
						|
    """ Magento Backend for version 1.9 """
 | 
						|
 | 
						|
 | 
						|
 | 
						|
It was linked with a Backend model such as:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    class MagentoBackend(models.Model):
 | 
						|
        _name = 'magento.backend'
 | 
						|
        _description = 'Magento Backend'
 | 
						|
        _inherit = 'connector.backend'
 | 
						|
 | 
						|
        _backend_type = 'magento'
 | 
						|
 | 
						|
        @api.model
 | 
						|
        def select_versions(self):
 | 
						|
            """ Available versions in the backend.
 | 
						|
            Can be inherited to add custom versions.  Using this method
 | 
						|
            to add a version from an ``_inherit`` does not constrain
 | 
						|
            to redefine the ``version`` field in the ``_inherit`` model.
 | 
						|
            """
 | 
						|
            return [('1.7', '1.7+')]
 | 
						|
 | 
						|
        version = fields.Selection(selection='select_versions', required=True)
 | 
						|
 | 
						|
 | 
						|
 | 
						|
After
 | 
						|
-----
 | 
						|
 | 
						|
All the :class:`backend.Backend` instances must be deleted.
 | 
						|
 | 
						|
And the ``_backend_type`` must be removed from the Backend model.
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    class MagentoBackend(models.Model):
 | 
						|
        _name = 'magento.backend'
 | 
						|
        _description = 'Magento Backend'
 | 
						|
        _inherit = 'connector.backend'
 | 
						|
 | 
						|
        @api.model
 | 
						|
        def select_versions(self):
 | 
						|
            """ Available versions in the backend.
 | 
						|
            Can be inherited to add custom versions.  Using this method
 | 
						|
            to add a version from an ``_inherit`` does not constrain
 | 
						|
            to redefine the ``version`` field in the ``_inherit`` model.
 | 
						|
            """
 | 
						|
            return [('1.7', '1.7+')]
 | 
						|
 | 
						|
        version = fields.Selection(selection='select_versions', required=True)
 | 
						|
 | 
						|
 | 
						|
Observations
 | 
						|
------------
 | 
						|
 | 
						|
* The version is now optional in the Backend Models.
 | 
						|
* Backend Models are based on Component's Collections:
 | 
						|
  :class:`odoo.addons.component.models.collection.Collection`
 | 
						|
 | 
						|
Links
 | 
						|
-----
 | 
						|
 | 
						|
* :ref:`api-component`
 | 
						|
* :class:`odoo.addons.component.models.collection.Collection`
 | 
						|
 | 
						|
 | 
						|
Inheritance
 | 
						|
===========
 | 
						|
 | 
						|
Before
 | 
						|
------
 | 
						|
 | 
						|
You could inherit a ``ConnectorUnit`` by creating a custom Backend
 | 
						|
version and decorating your class with it
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    magento_custom = backend.Backend(parent=magento1700, version='custom')
 | 
						|
    """ Custom Magento Backend """
 | 
						|
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    # base one
 | 
						|
    @magento
 | 
						|
    class MagentoPartnerAdapter(GenericAdapter):
 | 
						|
        # ...
 | 
						|
 | 
						|
    # other file...
 | 
						|
 | 
						|
    from .backend import magento_custom
 | 
						|
 | 
						|
    # custom one
 | 
						|
    @magento_custom
 | 
						|
    class MyPartnerAdapter(MagentoPartnerAdapter):
 | 
						|
        # ...
 | 
						|
 | 
						|
        def do_something(self):
 | 
						|
            # do it this way
 | 
						|
 | 
						|
You could also replace an existing class, this is mentionned in `Replace or
 | 
						|
unregister a component`_.
 | 
						|
 | 
						|
 | 
						|
After
 | 
						|
-----
 | 
						|
 | 
						|
For an existing component:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.component.core import Component
 | 
						|
 | 
						|
    class MagentoPartnerAdapter(Component):
 | 
						|
        _name = 'magento.partner.adapter'
 | 
						|
        _inherit = 'magento.adapter'
 | 
						|
 | 
						|
        def do_something(self):
 | 
						|
            # do it this way
 | 
						|
 | 
						|
You can extend it:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.component.core import Component
 | 
						|
 | 
						|
    class MyPartnerAdapter(Component):
 | 
						|
        _inherit = 'magento.partner.adapter'
 | 
						|
 | 
						|
        def do_something(self):
 | 
						|
            # do it this way
 | 
						|
 | 
						|
Or create a new different component with the existing one as base:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.component.core import Component
 | 
						|
 | 
						|
    class MyPartnerAdapter(Component):
 | 
						|
        _name = 'my.magento.partner.adapter'
 | 
						|
        _inherit = 'magento.partner.adapter'
 | 
						|
 | 
						|
        def do_something(self):
 | 
						|
            # do it this way
 | 
						|
 | 
						|
 | 
						|
Observations
 | 
						|
------------
 | 
						|
 | 
						|
* The inheritance is similar to the Odoo's one (without ``_inherits``.
 | 
						|
* All components have a Python inheritance on
 | 
						|
  :class:`~odoo.addons.component.core.AbstractComponent` or
 | 
						|
  :class:`~odoo.addons.component.core.Component`
 | 
						|
* The names are global (as in Odoo), so you should prefix them with a namespace
 | 
						|
* The name of the classes has no effect
 | 
						|
* As in Odoo Models, a Component can ``_inherit`` from a list of Components
 | 
						|
* All components implicitly inherits from a ``'base'`` component
 | 
						|
 | 
						|
Links
 | 
						|
-----
 | 
						|
 | 
						|
* :ref:`api-component`
 | 
						|
* :class:`odoo.addons.component.core.AbstractComponent`
 | 
						|
 | 
						|
 | 
						|
 | 
						|
Entrypoint for working with components
 | 
						|
======================================
 | 
						|
 | 
						|
Before
 | 
						|
------
 | 
						|
 | 
						|
Previously, when you had to work with ``ConnectorUnit`` from a Model or from a job,
 | 
						|
depending of the Odoo version you to:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.connector.connector import ConnectorEnvironment
 | 
						|
 | 
						|
    # ...
 | 
						|
 | 
						|
        backend_record = session.env['magento.backend'].browse(backend_id)
 | 
						|
        env = ConnectorEnvironment(backend_record, 'magento.res.partner')
 | 
						|
        importer = env.get_connector_unit(MagentoImporter)
 | 
						|
        importer.run(magento_id, force=force)
 | 
						|
 | 
						|
Or:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.connector.connector import ConnectorEnvironment
 | 
						|
    from odoo.addons.connector.session import ConnectorSession
 | 
						|
 | 
						|
    #...
 | 
						|
 | 
						|
        backend_record = session.env['magento.backend'].browse(backend_id)
 | 
						|
        session = ConnectorSession.from_env(self.env)
 | 
						|
        env = ConnectorEnvironment(backend_record, session, 'magento.res.partner')
 | 
						|
        importer = env.get_connector_unit(MagentoImporter)
 | 
						|
        importer.run(external_id, force=force)
 | 
						|
 | 
						|
Which was commonly abstracted in a helper function such as:
 | 
						|
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    def get_environment(session, model_name, backend_id):
 | 
						|
        """ Create an environment to work with.  """
 | 
						|
        backend_record = session.env['magento.backend'].browse(backend_id)
 | 
						|
        env = ConnectorEnvironment(backend_record, session, 'magento.res.partner')
 | 
						|
        lang = backend_record.default_lang_id
 | 
						|
        lang_code = lang.code if lang else 'en_US'
 | 
						|
        if lang_code == session.context.get('lang'):
 | 
						|
            return env
 | 
						|
        else:
 | 
						|
            with env.session.change_context(lang=lang_code):
 | 
						|
                return env
 | 
						|
 | 
						|
After
 | 
						|
-----
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    # ...
 | 
						|
        backend_record = self.env['magento.backend'].browse(backend_id)
 | 
						|
        with backend_record.work_on('magento.res.partner') as work:
 | 
						|
            importer = work.component(usage='record.importer')
 | 
						|
            importer.run(external_id, force=force)
 | 
						|
 | 
						|
Observations
 | 
						|
------------
 | 
						|
 | 
						|
* And when you are already in a Component, refer to `Find a component`_
 | 
						|
 | 
						|
Links
 | 
						|
-----
 | 
						|
 | 
						|
* :class:`~odoo.addons.component.core.WorkContext`
 | 
						|
 | 
						|
 | 
						|
Find a component
 | 
						|
================
 | 
						|
 | 
						|
Before
 | 
						|
------
 | 
						|
 | 
						|
To find a ``ConnectorUnit``, you had to ask for given class or subclass:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    # our ConnectorUnit to find
 | 
						|
    @magento
 | 
						|
    class MagentoPartnerAdapter(GenericAdapter):
 | 
						|
        _model_name = ['magent.res.partner']
 | 
						|
 | 
						|
    # other file...
 | 
						|
 | 
						|
    def run(self, record):
 | 
						|
        backend_adapter = self.unit_for(GenericAdapter)
 | 
						|
 | 
						|
It was searched for the current model and the current backend.
 | 
						|
 | 
						|
After
 | 
						|
-----
 | 
						|
 | 
						|
For an existing component:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.component.core import Component
 | 
						|
 | 
						|
    class MagentoPartnerAdapter(Component):
 | 
						|
        _name = 'magento.partner.adapter'
 | 
						|
        _inherit = 'magento.adapter'
 | 
						|
 | 
						|
        _usage = 'backend.adapter'
 | 
						|
        _collection = 'magento.backend'
 | 
						|
        _apply_on = ['res.partner']
 | 
						|
 | 
						|
    # other file...
 | 
						|
 | 
						|
    def run(self, record):
 | 
						|
        backend_adapter = self.component(usage='backend.adapter')
 | 
						|
 | 
						|
 | 
						|
 | 
						|
Observations
 | 
						|
------------
 | 
						|
 | 
						|
* The model is compared with the ``_apply_on`` attribute
 | 
						|
* The Backend is compared with the ``_collection`` attribute, it must
 | 
						|
  have the same name than the Backend Model.
 | 
						|
* The ``_usage`` indicates what the purpose of the component is, and
 | 
						|
  allow to find the correct one for our task. It allow more dynamic
 | 
						|
  usages than the previous usage of a class.
 | 
						|
* Usually, the ``_usage`` and the ``_collection`` will be ``_inherit`` 'ed from
 | 
						|
  a component (here from ``'magento.adapter``), so they won't need to be
 | 
						|
  repeated in all Components.
 | 
						|
* A good idea is to have a base abstract Component for the Collection, then
 | 
						|
  an abstract Component for every usage::
 | 
						|
 | 
						|
    class BaseMagentoConnectorComponent(AbstractComponent):
 | 
						|
 | 
						|
        _name = 'base.magento.connector'
 | 
						|
        _inherit = 'base.connector'
 | 
						|
        _collection = 'magento.backend'
 | 
						|
 | 
						|
    class MagentoBaseExporter(AbstractComponent):
 | 
						|
        """ Base exporter for Magento """
 | 
						|
 | 
						|
        _name = 'magento.base.exporter'
 | 
						|
        _inherit = ['base.exporter', 'base.magento.connector']
 | 
						|
        _usage = 'record.exporter'
 | 
						|
 | 
						|
    class MagentoImportMapper(AbstractComponent):
 | 
						|
        _name = 'magento.import.mapper'
 | 
						|
        _inherit = ['base.magento.connector', 'base.import.mapper']
 | 
						|
        _usage = 'import.mapper'
 | 
						|
 | 
						|
    # ...
 | 
						|
 | 
						|
* The main usages are:
 | 
						|
  * import.mapper
 | 
						|
  * export.mapper
 | 
						|
  * backend.adapter
 | 
						|
  * importer
 | 
						|
  * exporter
 | 
						|
  * binder
 | 
						|
  * event.listener
 | 
						|
* But for the importer and exporter, I recommend to use more precise ones in
 | 
						|
  the connectors: record.importer, record.exporter, batch.importer,
 | 
						|
  batch.exporter
 | 
						|
* You are allowed to be creative with the ``_usage``, it's the key that will
 | 
						|
  allow you to find the right one component you need. (e.g. on
 | 
						|
  ``stock.picking`` you need to 1. export the record, 2. export the tracking.
 | 
						|
  Then use ``record.exporter`` and ``tracking.exporter``).
 | 
						|
* AbstractComponent will never be returned by a lookup
 | 
						|
 | 
						|
 | 
						|
Links
 | 
						|
-----
 | 
						|
 | 
						|
* :ref:`api-component`
 | 
						|
* :class:`odoo.addons.component.core.AbstractComponent`
 | 
						|
 | 
						|
 | 
						|
Backend Versions
 | 
						|
================
 | 
						|
 | 
						|
Before
 | 
						|
------
 | 
						|
 | 
						|
You could have several versions for a backend:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    magento = backend.Backend('magento')
 | 
						|
    """ Generic Magento Backend """
 | 
						|
 | 
						|
    magento1700 = backend.Backend(parent=magento, version='1.7')
 | 
						|
    """ Magento Backend for version 1.7 """
 | 
						|
 | 
						|
    magento1900 = backend.Backend(parent=magento, version='1.9')
 | 
						|
    """ Magento Backend for version 1.9 """
 | 
						|
 | 
						|
 | 
						|
And use them for a class-level dynamic dispatch
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.magentoerpconnect.backend import magento1700, magento1900
 | 
						|
 | 
						|
    @magento1700
 | 
						|
    class PartnerAdapter1700(GenericAdapter):
 | 
						|
        # ...
 | 
						|
 | 
						|
        def do_something(self):
 | 
						|
            # do it this way
 | 
						|
 | 
						|
    @magento1900
 | 
						|
    class PartnerAdapter1900(GenericAdapter):
 | 
						|
        # ...
 | 
						|
 | 
						|
        def do_something(self):
 | 
						|
            # do it that way
 | 
						|
 | 
						|
 | 
						|
After
 | 
						|
-----
 | 
						|
 | 
						|
This feature has been removed, it introduced a lot of complexity (notably
 | 
						|
regarding inheritance) for few gain.  The version is now optional on the
 | 
						|
backends and the version dispatch, if needed, should be handled manually.
 | 
						|
 | 
						|
In methods:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.component.core import Component
 | 
						|
 | 
						|
    class PartnerAdapter(Component):
 | 
						|
        # ...
 | 
						|
 | 
						|
        def do_something(self):
 | 
						|
            if self.backend_record.version == '1.7':
 | 
						|
                # do it this way
 | 
						|
            else:
 | 
						|
                # do it that way
 | 
						|
 | 
						|
Or with a factory:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.component.core import Component
 | 
						|
 | 
						|
    class PartnerAdapterFactory(Component):
 | 
						|
        # ...
 | 
						|
 | 
						|
        def get_component(self, version):
 | 
						|
            if self.backend_record.version == '1.7':
 | 
						|
                return self.component(usage='backend.adapter.1.7')
 | 
						|
            else:
 | 
						|
                return self.component(usage='backend.adapter.1.9')
 | 
						|
 | 
						|
Observations
 | 
						|
------------
 | 
						|
 | 
						|
* None
 | 
						|
 | 
						|
Links
 | 
						|
-----
 | 
						|
 | 
						|
* :ref:`api-component`
 | 
						|
 | 
						|
 | 
						|
Replace or unregister a component
 | 
						|
=================================
 | 
						|
 | 
						|
Before
 | 
						|
------
 | 
						|
 | 
						|
You could replace a ``ConnectorUnit`` with the ``replace`` argument passed to
 | 
						|
the backend decorator:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    @magento(replacing=product.ProductImportMapper)
 | 
						|
    class ProductImportMapper(product.ProductImportMapper):
 | 
						|
 | 
						|
 | 
						|
After
 | 
						|
-----
 | 
						|
 | 
						|
First point: this should hardly be needed now, as you can inherit a component
 | 
						|
like Odoo Models.  Still, if you need to totally replace a component by
 | 
						|
another, let's say there is this component:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.component.core import Component
 | 
						|
 | 
						|
    class ProductImportMapper(Component):
 | 
						|
        _name = 'magento.product.import.mapper'
 | 
						|
        _inherit = 'magento.import.mapper'
 | 
						|
 | 
						|
        _apply_on = ['magento.product.product']
 | 
						|
        # normally the following attrs are inherited from the _inherit
 | 
						|
        _usage = 'import.mapper'
 | 
						|
        _collection = 'magento.backend'
 | 
						|
 | 
						|
 | 
						|
Then you can remove the usage of the component: it will never be used:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.component.core import Component
 | 
						|
 | 
						|
    class ProductImportMapper(Component):
 | 
						|
        _inherit = 'magento.product.import.mapper'
 | 
						|
        _usage = None
 | 
						|
 | 
						|
And create your own, that will be picked up instead of the base one:
 | 
						|
 | 
						|
.. code-block:: python
 | 
						|
 | 
						|
    from odoo.addons.component.core import Component
 | 
						|
 | 
						|
    class MyProductImportMapper(Component):
 | 
						|
        _name = 'my.magento.product.import.mapper'
 | 
						|
        _inherit = 'magento.import.mapper'
 | 
						|
 | 
						|
        _apply_on = ['magento.product.product']
 | 
						|
        # normally the following attrs are inherited from the _inherit
 | 
						|
        _usage = 'import.mapper'
 | 
						|
        _collection = 'magento.backend'
 | 
						|
 | 
						|
 | 
						|
Observations
 | 
						|
------------
 | 
						|
 | 
						|
* None
 | 
						|
 | 
						|
Links
 | 
						|
-----
 | 
						|
 | 
						|
* :ref:`api-component`
 | 
						|
 | 
						|
 | 
						|
Various hints
 | 
						|
=============
 | 
						|
 | 
						|
* The components and the jobs know how to work with Model instances,
 | 
						|
  so prefer them over ids in parameters.
 |