Backend

Le backend pour une version (par exemple Magento 1.7) est représenté par une instance de la classe Backend.

Chaque connecteur va aussi créer un connector.backend qui permet aux utilisateurs d’inscrire leurs backends. Par exemple le connecteur Magento a un magento.backend (_inherit connector.backend_model.connector_backend). Ce modèle contient un champ version qui doit avoir la même lists de versions (avec exactement le même nom) que les instances de Backend.

Exemple avec le connecteur Magento

# 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>

Dans le code ci-dessus, “1.7” est la clé de correspondance entre l’instance de Backend (magento1700) et l’enregistrement magento_backend.

class connector.backend.Backend(service=None, version=None, parent=None, registry=None)[source]

Bases : object

Un backend représente un système avec lequel interagir, comme Magento, Prestashop, Redmine, …

Il possède trois propriétés :

service

Le nom du service, par exemple “magento”

version

La version du service. Par exemple “1.7”

parent

Un backend parent. Lorsqu’aucune classe ConnectorUnit n’est trouvée pour un backend, elle est cherchée dans le parent.

La structure des Backends est la partie clé du framework, mais elle est plutôt simple.

  • Une instance de Backend contient un registre de classes ConnectorUnit
  • Il peut renvoyer la classe ConnectorUnit à utiliser pour une tâche
  • Si aucune ConnectorUnit n’est inscrite pour une tâche, le parent direct est interrogé (et ainsi de suite)

Les Backends ont deux mécanismes différents d’extension. L’un est plus vertical (entre plusieurs versions) et l’autre est plus horizontal car il permet de modifier le comportement pour une version donnée du backend.

Pour l’exemple, disons que nous avons les versions suivantes de backend

         <Magento>
             |
      -----------------
      |               |
<Magento 1.7>   <Magento 2.0>
      |
<Magento with specific>

Et voici la façon dont elles sont déclarées en 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')

Dans le graphe ci-dessus, <Magento> va contenir toutes les classes partagées entre toutes les versions. Chaque version de Magento (<Magento 1.7>, <Magento 2.0>) va utiliser les classes définies sur <Magento>, sauf si elles ont inscrit leur propres classes. C’est la même chose pour <Magento avec specifique> mais celui-ci contient des personnalisations qui sont spécifiques à une instance (typiquement pour mettre en place des mappeurs spécifiques à une instance).

Voici comment inscrire des classes sur <Magento> et une autre sur <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'

Ici, la méthode get_class() appelé sur magento1700 renverrait

magento1700.get_class(Synchronizer, session, 'res.partner')
# => Synchronizer1700
magento1700.get_class(Mapper, session, 'res.partner')
# => Mapper

C’est le mécanisme d’extension verticale, il dit que chaque version fille est capable d’étendre ou remplacer le comportement de son parent.

Note

en utilisant le framework, vous n’aurez pas besoin d’appeler

get_class(), habituellement vous appellerez connector.connector.ConnectorEnvironment.get_connector_unit().

L’extension verticale est celle que vous utiliserez probablement le plus, parce que la plupart des choses que vous modifierez concerne vos applications personnalisées ou des comportements différents selon les versions du backend.

Cependant il peut arriver que nous ayons besoin de changer le comportement d’un connecteur, en installant un module. Par exemple, imaginons que nous ayons déjà un ImportMapper pour les articles dans le Connecteur Magento. Nous créons un module (générique) pour gérer le catalogue d’une manière plus avancée. Nous redéfinissons un AdvancedImportMapper qui devrait être utilisé quand le module est installé. Ceci est le mécanisme d’extension horizontal.

Remplace une clase ConnectorUnit par une autre dans le backend

@backend(replacing=ImportMapper)
class AdvancedImportMapper(ImportMapper):
    _model_name = 'product.product'

Avertissement

L’extension horizontale devrait être utilisée de manière très occasionnelle et précautionneuse, car dès que deux modules voudront remplacer la même classe il y aura un conflit (qui nécessiterait de créer un troisième module pour lier les deux, le remplacement pouvant prendre un tuple de classes à remplacer et ceci étant exponentiel). Ce mécanisme ne devrait être utilisé que dans certains circonstances bien précises pour des modules génériques.

get_class(base_class, session, model_name)[source]

Trouve une sous-classe correspondante à base_class dans les classes inscrites.

Paramètres:
match(service, version)[source]

Utilisé pour trouver le backend pour un service et une version

register_class(cls, replacing=None)[source]

Inscrit une classe dans le backend

Paramètres:
service

Modèles du backend

class connector.backend_model.ConnectorBackend(pool, cr)[source]

Bases : openerp.models.AbstractModel

Une instance d’un backend externe auquel se synchroniser

Les backends doivent _inherit ce modèle dans les modules des connecteurs.

get_backend(*args, **kwargs)[source]

Pour un enregistrement d’un backend, renvoie l’instance appropriée de Backend.

name

Champ chaîne de caractère basique, peut être limité en longeur, habituellement affiché comme ligne unique dans les clients

Paramètres:
  • size (int) – la taille maximum des valeurs stockées dans ce champ
  • translate (bool) – si les valeurs de ce champ peuvent être traduites
version
Paramètres:
  • 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.

class connector.backend_model.ExternalBinding(pool, cr)[source]

Bases : openerp.models.AbstractModel

Un modèle abstrait pour une liaison à des enregistrements externes

Un binding externe est une liaison entre un backend et OpenERP. Par exemple, pour un partner, cela peut être magento.res.partner ou pour un article, magento.product.

Le modèle final sera un _inherits du modèle OpenERP et va _inherit ce modèle.

Il aura une relation vers l’enregistrement (via _inherits) et vers le modèle backend concret (par exemple magento.backend).

Il contiendra également toutes les données relatives aux backend pour l’enregistrement.

Il nécessite d’implémenter au moins ces champs :

openerp_id

Le many2one vers l’enregistrement lié (utilisé par _inherits).

backend_id

Le many2one vers le backend (par exemple magento.backend).

magento_id ou prestashop_id ou …

L’ID sur le backend.

sync_date

Dernière date de synchronisation

La définition des relations dans _columns doit être faite dans les classes concrètes, parce que les relations elle-mêmes n’existent pas dans ce module.

Par exemple, pour un res.partner.category depuis Magento, J’aurais (ceci est une consolidation de toutes les colonnes des modèles abstraits. Dans magentoerpconnect vous ne trouveriez pas ça)

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