Backend¶
A backend for a version (for instance Magento 1.7), is represented
by an instance of the Backend
class.
Each connector will also create a connector.backend
which allows the users
to register their backends. For instance, the Magento connector has
magento.backend
(_inherit
connector.backend_model.connector_backend
). This model contains a
version
field which should have the same list of versions (with the exact
same name) than the instances of Backend
.
Example with the Magento Connector:
# in magentoerpconnect/backend.py
magento = backend.Backend('magento')
""" Generic Magento Backend """
magento1700 = backend.Backend(parent=magento, version='1.7')
""" Magento Backend for version 1.7 """
# in magentoerpconnect/magento_model.py
class MagentoBackend(models.Model):
_name = 'magento.backend'
_description = 'Magento Backend'
_inherit = 'connector.backend'
_backend_type = 'magento'
@api.model
def _select_versions(self):
""" Available versions
Can be inherited to add custom versions.
"""
return [('1.7', 'Magento 1.7')]
# <snip>
version = fields.Selection(
selection='_select_versions',
string='Version',
required=True,
)
location = fields.Char(string='Location', required=True)
username = fields.Char(string='Username')
password = fields.Char(string='Password')
# <snip>
In the code above, ‘1.7’ is the matching key between the
Backend
instance (magento1700
) and the
magento_backend
record.
-
class
connector.backend.
Backend
(service=None, version=None, parent=None, registry=None)[source]¶ Bases:
object
A backend represents a system to interact with, like Magento, Prestashop, Redmine, …
It owns 3 properties:
-
service
¶ Name of the service, for instance ‘magento’
-
version
¶ The version of the service. For instance: ‘1.7’
-
parent
¶ A parent backend. When no
ConnectorUnit
is found for a backend, it will search it in the parent.
The Backends structure is a key part of the framework, but is rather simple.
- A
Backend
instance holds a registry ofConnectorUnit
classes - It can return the appropriate
ConnectorUnit
to use for a task - If no
ConnectorUnit
is registered for a task, it will ask it to its direct parent (and so on)
The Backends support 2 different extension mechanisms. One is more vertical - across the versions - and the other would be more horizontal as it allows to modify the behavior for 1 version of backend.
For the sake of the example, let’s say we have theses backend versions:
<Magento> | ----------------- | | <Magento 1.7> <Magento 2.0> | <Magento with specific>
And here is the way they are declared in Python:
magento = Backend('magento') magento1700 = Backend(parent=magento, version='1.7') magento2000 = Backend(parent=magento, version='2.0') magento_specific = Backend(parent=magento1700, version='1.7-specific')
In the graph above,
<Magento>
will hold all the classes shared between all the versions. Each Magento version (<Magento 1.7>
,<Magento 2.0>
) will use the classes defined on<Magento>
, excepted if they registered their own ones instead. That’s the same for<Magento with specific>
but this one contains customizations which are specific to an instance (typically you want specific mappings for one instance).Here is how you would register classes on
<Magento>
and another on<Magento 1.7>
:@magento class Synchronizer(ConnectorUnit): _model_name = 'res.partner' @magento class Mapper(ConnectorUnit): _model_name = 'res.partner' @magento1700 class Synchronizer1700(Synchronizer): _model_name = 'res.partner'
Here, the
get_class()
called onmagento1700
would return:magento1700.get_class(Synchronizer, session, 'res.partner') # => Synchronizer1700 magento1700.get_class(Mapper, session, 'res.partner') # => Mapper
This is the vertical extension mechanism, it says that each child version is able to extend or replace the behavior of its parent.
Note
when using the framework, you won’t need to call
get_class()
, usually, you will callconnector.connector.ConnectorEnvironment.get_connector_unit()
.The vertical extension is the one you will probably use the most, because most of the things you will change concern your custom adaptations or different behaviors between the versions of the backend.
However, some time, we need to change the behavior of a connector, by installing an addon. For example, say that we already have an
ImportMapper
for the products in the Magento Connector. We create a - generic - addon to handle the catalog in a more advanced manner. We redefine anAdvancedImportMapper
, which should be used when the addon is installed. This is the horizontal extension mechanism.Replace a
ConnectorUnit
by another one in a backend:@backend(replacing=ImportMapper) class AdvancedImportMapper(ImportMapper): _model_name = 'product.product'
Warning
The horizontal extension should be used sparingly and cautiously since as soon as 2 addons want to replace the same class, you’ll have a conflict (which would need to create a third addon to glue them,
replacing
can take a tuple of classes to replace and this is exponential). This mechanism should be used only in some well placed circumstances for generic addons.-
get_class
(base_class, session, model_name)[source]¶ Find a matching subclass of
base_class
in the registered classes.Parameters: - base_class (
connector.connector.MetaConnectorUnit
) – class (and its subclass) to search in the registry - session (
connector.session.ConnectorSession
) – current session
- base_class (
-
register_class
(cls, replacing=None)[source]¶ Register a class in the backend.
Parameters: - cls (
connector.connector.MetaConnectorUnit
) – the ConnectorUnit class class to register - replacing (
connector.connector.MetaConnectorUnit
) – optional, the ConnectorUnit class to replace
- cls (
-
service
-