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

Issue:
Failure on Django 1.7+ with `django.db.models.fields.related.RelatedManager`
We're trying to upgrade from Django 1.6 to 1.7 (and then to 1.8).

My tests fail with:

```
  File "/home/roger/.virtualenvs/fdw/local/lib/python2.7/site-packages/import_export/resources.py", line 383, in import_data
    real_dry_run)
  File "/home/roger/.virtualenvs/fdw/local/lib/python2.7/site-packages/import_export/resources.py", line 294, in get_diff
    v2 = self.export_field(field, current) if current else ""
  File "/home/roger/.virtualenvs/fdw/local/lib/python2.7/site-packages/import_export/resources.py", line 416, in export_field
    return field.export(obj)
  File "/home/roger/.virtualenvs/fdw/local/lib/python2.7/site-packages/import_export/fields.py", line 101, in export
    value = self.get_value(obj)
  File "/home/roger/.virtualenvs/fdw/local/lib/python2.7/site-packages/import_export/fields.py", line 86, in get_value
    value = value()
  File "/home/roger/.virtualenvs/fdw/local/lib/python2.7/site-packages/django/db/models/fields/related.py", line 693, in __call__
    manager = getattr(self.model, kwargs.pop('manager'))
KeyError: u'manager'
```

I can see that 0dfb4b9d8288bf47e56465f9269f96f56aea5c55 first tried to solve this using:

```
       # Manyrelatedmanagers are callable in Django >= 1.7 but we don't want
       # to call them
       if callable(value) and not value.through:
          value = value()
```

And that this was quickly upgraded by 2e2dc84aea199393549f21d312185cb88487eb4b to:

```
        if callable(value) and not hasattr(value, 'through'):
            value = value()
```

This still doesn't work for me. I have a model with a straightforward foreign key. It is still represented as a `django.db.models.fields.related.RelatedManager` but it isn't a ManyToMany and it doesn't have a `through` attribute and consequently it gets called here. That results in the following Django code being called:

```
def create_foreign_related_manager(superclass, rel_field, rel_model):
    class RelatedManager(superclass):
        def __init__(self, instance):
            super(RelatedManager, self).__init__()
            self.instance = instance
            self.core_filters = {rel_field.name: instance}
            self.model = rel_model

        def __call__(self, **kwargs):
            # We use **kwargs rather than a kwarg argument to enforce the
            # `manager='manager_name'` syntax.
            manager = getattr(self.model, kwargs.pop('manager'))
```

We aren't passing any arguments and the `kwargs` is an empty `dict` and so the `pop` fails.

If we don't want to call Managers, why don't we just test for them:

```
from django.db.models.manager import Manager


        # RelatedManager and ManyRelatedManager classes are callable in
        # Django >= 1.7 but we don't want to call them
        if callable(value) and not isinstance(value, Manager):
            value = value()
        return value
```



Discussion:
@rhunwicks does your proposed solution works? if so, can you send a pull request?

thanks!

It does fix the error I was getting. I'm trying to reproduce it in the django-import-export test suite so I can provide a test as well as a fix, but I haven't managed it yet.


Resolving patch:
diff --git a/import_export/fields.py b/import_export/fields.py
--- a/import_export/fields.py
+++ b/import_export/fields.py
@@ -3,6 +3,7 @@
 from . import widgets
 
 from django.core.exceptions import ObjectDoesNotExist
+from django.db.models.manager import Manager
 
 
 class Field(object):
@@ -80,9 +81,9 @@ def get_value(self, obj):
             if value is None:
                 return None
 
-        # Manyrelatedmanagers are callable in Django >= 1.7 but we don't want
-        # to call them
-        if callable(value) and not hasattr(value, 'through'):
+        # RelatedManager and ManyRelatedManager classes are callable in
+        # Django >= 1.7 but we don't want to call them
+        if callable(value) and not isinstance(value, Manager):
             value = value()
         return value
 


Test patch:
diff --git a/tests/core/tests/resources_tests.py b/tests/core/tests/resources_tests.py
--- a/tests/core/tests/resources_tests.py
+++ b/tests/core/tests/resources_tests.py
@@ -96,6 +96,16 @@ class B(A):
         resource = B()
         self.assertEqual(resource._meta.custom_attribute, True)
 
+
+class AuthorResource(resources.ModelResource):
+
+    books = fields.Field(column_name='books', attribute='book_set', readonly=True)
+
+    class Meta:
+        model = Author
+        export_order = ('name', 'books')
+
+
 class BookResource(resources.ModelResource):
     published = fields.Field(column_name='published_date')
 
@@ -185,6 +195,18 @@ def test_get_diff(self):
                 u'other </ins><span>book</span>')
         self.assertFalse(diff[headers.index('author_email')])
 
+    def test_get_diff_with_callable_related_manager(self):
+        resource = AuthorResource()
+        author = Author(name="Some author")
+        author.save()
+        author2 = Author(name="Some author")
+        self.book.author = author
+        self.book.save()
+        diff = resource.get_diff(author2, author)
+        headers = resource.get_export_headers()
+        self.assertEqual(diff[headers.index('books')],
+                '<span>core.Book.None</span>')
+
     def test_import_data(self):
         result = self.resource.import_data(self.dataset, raise_errors=True)