Merge lp:~openerp-connector-core-editors/openerp-connector/7.0-connector-mapper-refactor into lp:~openerp-connector-core-editors/openerp-connector/7.0
Status: | Merged |
---|---|
Approved by: | Guewen Baconnier @ Camptocamp |
Approved revision: | 595 |
Merged at revision: | 605 |
Proposed branch: | lp:~openerp-connector-core-editors/openerp-connector/7.0-connector-mapper-refactor |
Merge into: | lp:~openerp-connector-core-editors/openerp-connector/7.0 |
Prerequisite: | lp:~camptocamp/openerp-connector/7.0-connector-closure-functions |
Diff against target: |
1271 lines (+790/-197) 6 files modified
connector/CHANGES.rst (+1/-5) connector/backend.py (+7/-4) connector/exception.py (+4/-0) connector/tests/test_backend.py (+2/-1) connector/tests/test_mapper.py (+273/-67) connector/unit/mapper.py (+503/-120) |
To merge this branch: | bzr merge lp:~openerp-connector-core-editors/openerp-connector/7.0-connector-mapper-refactor |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Joël Grand-Guillaume @ camptocamp | code review, no tests | Approve | |
Review via email:
|
This proposal supersedes a proposal from 2013-11-08.
Description of the change
Refactoring of the Mappers.
=======
Work in progress.
Now that I have gained more experience with my framework and that I have been confronted with different implementations, I discover some potentialities as well as some of its limitations.
This proposal aims to address some issues and limitations of the actual mappers.
It is _as possible_ backward compatible, but a few things will need to be changed in the implementations using the connector (not in the Mappers themselves though, but in the way we use them, so the change has a limited effect).
Rationale
---------
State
~~~~~
The synchronizers have a a direct access to the most used ConnectorUnit classes, self.binder, self.backend_
self.
values = self.mapper.data
One of my point was to re-establish a stateless Mapper. Now, the mapper returns a MapRecord prepared for the mapping. The MapRecord can ask to be mapped at any moment:
map_record = self.mapper.
map_record2 = self.mapper.
values = map_record.values()
(in fact, there is some transient state in the Mapper, only when we call map_record.
This forms allows brings a nice feature which are the options.
Options
~~~~~~~
The new form with a MapRecord allows to specify custom options for the mappings, example:
map_record = self.mapper.
values = map_record.
# in the mapper
@mapping
def fees(self, record):
if self.options.
else:
The 'for_create' and 'fields' are now handled like options.
Coupling of "children" mappings
~~~~~~~
Children mappings are, for instance, the lines of a sales order that we want to create in the same call to "create()" than the sales order's one.
In the current code, they are defined in the mappers with:
children = [('items', 'magento_
Where 'items' is the key containing the lines in the source record, 'magento_
The connector would search the Mapper for the model of the lines, process each lines and add them in the target record (for instance with the OpenERP commands (0, 0, {values}).
This was done directly in the main Mapper and thus was completely coupled with it. It brought difficulties when we wanted a more elaborate behavior than the default one: skip some lines, update existing lines instead of just creating them...
So I introduce a "MapChild" ConnectorUnit, which defines *how* items are converted and added to the main target record. The result of the default implementation is similar to the previous one. It can be easily extended to customize it though. It is not mandatory to create a MapChild each time we use 'children', the default one will be used if it not defined (although this ensures the backward compatibility).
Changes after the mapping
~~~~~~~
The Mappers apply all the 'direct' mappings, then all the @mapping methods, then all the 'children' mappings. Finally, we have the opportunity to override _after_mapping() so we can alter the values just before they are returned by the Mapper. Unfortunately, this method wasn't aware of the source values, which was a pity since we may need them if we want to change the target values. This is fixed in the new finalize() method.
Modifiers
~~~~~~~~~
Non-Backward compatible changes
-------
Usage of a Mapper
~~~~~~~~~~~~~~~~~
Previously:
self.
values = self.mapper.data
values = self.mapper.
Now:
map_record = self.mapper.
values = map_record.values()
values = map_record.
values = map_record.
values = map_record.
This change impacts the Synchronizers.
Filtering of items
~~~~~~~~~~~~~~~~~~
For 'children' mappings (submappings), (e.g. lines of a sales order),
the way in place to filter out some items was:
# method in 'Mapper'
def skip_convert_
return record['type'] == 'configurable'
Now, we need to create a 'MapChild' ConnectorUnit that will decide *how* the items are converted (note that creating a 'MapChild' is not necessary as soon as we don't need to modify its default behavior):
@magento
class SalesLineMapChi
_model_name = 'magento.
def skip_item(self, map_record):
# map_record.parent is accessible
return map_record.
Changes at the end of a mapping
~~~~~~~
At the end of a mapping, Mapper.
which was unfortunate if we needed them.
Now, instead of overriding Mapper.
def finalize(self, map_record, values):
# call onchanges...
# add extra order lines...
return values
Concretely
~~~~~~~~~~
Here are the changes I needed to do for the Magento Connector:
https:/
-------
Ran 74 tests in 4.278s
OK
Branch is now sufficiently stable to be tested and used for development.