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
-
Backend Models¶
-
class
connector.backend_model.
ConnectorBackend
(pool, cr)[source]¶ Bases:
openerp.models.AbstractModel
An instance of an external backend to synchronize with.
The backends have to
_inherit
this model in the connectors modules.-
get_backend
(*args, **kwargs)[source]¶ For a record of backend, returns the appropriate instance of
Backend
.
-
name
¶ Basic string field, can be length-limited, usually displayed as a single-line string in clients
Parameters:
-
version
¶ Parameters: - selection – specifies the possible values for this field.
It is given as either a list of pairs (
value
,string
), or a model method, or a method name. - selection_add – provides an extension of the selection in the case
of an overridden field. It is a list of pairs (
value
,string
).
The attribute
selection
is mandatory except in the case of related fields or field extensions.- selection – specifies the possible values for this field.
It is given as either a list of pairs (
-
-
class
connector.backend_model.
ExternalBinding
(pool, cr)[source]¶ Bases:
openerp.models.AbstractModel
An abstract model for bindings to external records.
An external binding is a binding between a backend and OpenERP. For example, for a partner, it could be
magento.res.partner
or for a product,magento.product
.The final model, will be an
_inherits
of the OpenERP model and will_inherit
this model.It will have a relation to the record (via
_inherits
) and to the concrete backend model (magento.backend
for instance).It will also contains all the data relative to the backend for the record.
It needs to implements at least these fields:
openerp_id
The many2one to the record it links (used by_inherits
).backend_id
The many2one to the backend (for instancemagento.backend
).magento_id or prestashop_id or …
The ID on the backend.sync_date
Last date of synchronizationThe definition of the relations in
_columns
is to be done in the concrete classes because the relations themselves do not exist in this addon.For example, for a
res.partner.category
from Magento, I would have (this is a consolidation of all the columns from the abstract models, inmagentoerpconnect
you would not find that):class MagentoResPartnerCategory(models.Model): _name = 'magento.res.partner.category' _inherits = {'res.partner.category': 'openerp_id'} openerp_id = fields.Many2one(comodel_name='res.partner.category', string='Partner Category', required=True, ondelete='cascade') backend_id = fields.Many2one( comodel_name='magento.backend', string='Magento Backend', required=True, ondelete='restrict') magento_id = fields.Char(string='ID on Magento') tax_class_id = fields.Integer(string='Tax Class ID') _sql_constraints = [ ('magento_uniq', 'unique(backend_id, magento_id)', 'Partner Tag with same ID on Magento already exists.'), ]
-
sync_date
¶
-