Repository: django-import-export/django-import-export

Issue:
Additional customization hooks
There are some hooks in `Resource` to allow easy customization: `before_save_instance`, etc.

I want to take an extract file and use it to create some parent models as part of the same import process. The best way for me to do this, is to call the `import_data` method for the other ModelResources immediately before I start processing the rows (so that I am already inside the transaction management, if required). At the moment, I have to copy and paste the entire `import_data` method into my `Resource`: it would be better if we had something like:

``` python
        if use_transactions is True:
            # when transactions are used we want to create/update/delete object
            # as transaction will be rolled back if dry_run is set
            real_dry_run = False
            transaction.enter_transaction_management()
            transaction.managed(True)
        else:
            real_dry_run = dry_run

        instance_loader = self._meta.instance_loader_class(self, dataset)

        self.before_import(dataset, real_dry_run)

        for row in dataset.dict:
```

Similarly, some of my customizations work by overriding `import_obj`, but this can more difficult than necessary because we don't know whether we are in a dry run or not.

This could be solved by adding `dry_run` to the method signature of `import_obj` and passing it when you call it from `import_data`.

Would those changes be acceptable? If they would, I'll prepare a pull request.



Discussion:
It looks like nice addition so please go ahead  and make PR. Do not forget tests and docs :)

Maybe transferring `dry_run` around looks cumbersome, but do not have better idea at the moment.

I think that transferring dry_run is the best way for now, because if we are using transaction management we need to pass it as False to the lower-level code even if it is `True` at the top-level.

agreed


Resolving patch:
diff --git a/import_export/resources.py b/import_export/resources.py
--- a/import_export/resources.py
+++ b/import_export/resources.py
@@ -184,7 +184,7 @@ def import_field(self, field, obj, data):
         if field.attribute and field.column_name in data:
             field.save(obj, data)
 
-    def import_obj(self, obj, data):
+    def import_obj(self, obj, data, dry_run):
         """
         """
         for field in self.get_fields():
@@ -260,6 +260,12 @@ def get_diff_headers(self):
         """
         return self.get_export_headers()
 
+    def before_import(self, dataset, dry_run):
+        """
+        Override to add additional logic.
+        """
+        pass
+
     def import_data(self, dataset, dry_run=False, raise_errors=False,
             use_transactions=None):
         """
@@ -286,6 +292,17 @@ def import_data(self, dataset, dry_run=False, raise_errors=False,
 
         instance_loader = self._meta.instance_loader_class(self, dataset)
 
+        try:
+            self.before_import(dataset, real_dry_run)
+        except Exception, e:
+            tb_info = traceback.format_exc(sys.exc_info()[2])
+            result.base_errors.append(Error(repr(e), tb_info))
+            if raise_errors:
+                if use_transactions:
+                    transaction.rollback()
+                    transaction.leave_transaction_management()
+                raise
+
         for row in dataset.dict:
             try:
                 row_result = RowResult()
@@ -307,7 +324,7 @@ def import_data(self, dataset, dry_run=False, raise_errors=False,
                         row_result.diff = self.get_diff(original, None,
                                 real_dry_run)
                 else:
-                    self.import_obj(instance, row)
+                    self.import_obj(instance, row, real_dry_run)
                     if self.skip_row(instance, original):
                         row_result.import_type = RowResult.IMPORT_TYPE_SKIP
                     else:


Test patch: