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

Issue:
attribute and column_name mixed up for export and Meta.fields
**Describe the bug**
Unable to export columns when the column_name is not the same as attribute when fields using attribute.

**To Reproduce**
Steps to reproduce the behavior:
1- Create a field and put his attribute name in the Meta.fields list and use another name for column_name
2- execute export

**Versions (please complete the following information):**
Last, last last, surfing on the last.

**Expected behavior**
Columns appears on export with the good translation of the column_name header

**Screenshots**
Useless

**Additional context**
Use less

** Fix **
Replace column_name by attribute when applicable. resource.py line 1034
```python
    def export_resource(self, instance, fields=None):
        export_fields = self.get_export_fields()

        if isinstance(fields, list) and fields:
            return [
                self.export_field(field, instance)
                for field in export_fields
                if field.attribute in fields
            ]

        return [self.export_field(field, instance) for field in export_fields]

    def get_export_headers(self, fields=None):
        if isinstance(fields, list) and fields:
            return [force_str(field.column_name) for field in self.get_export_fields() if field.attribute in fields]
        return [force_str(field.column_name) for field in self.get_export_fields()]
```

Thanks



Discussion:
also seeing this. thanks for reporting it. 
the feature used to work in the v3 series. desired behavior is also documented here: https://django-import-export.readthedocs.io/en/stable/advanced_usage.html#explicit-field-declaration
Thanks for reporting.  I confirm this is a bug in v4.  I tested in v3 with the following in the example app:

```python
class BookResource(ModelResource):
    published = Field(attribute='published', column_name='published_date')

    class Meta:
        model = Book
        fields = ('id', 'name', 'price', 'published')
```

In v3, this will export to a column with header 'published_date'.  In v4, it doesn't export the column.  If you remove the custom declaration, then it exports ok as 'published'.

Resolving patch:
diff --git a/import_export/declarative.py b/import_export/declarative.py
--- a/import_export/declarative.py
+++ b/import_export/declarative.py
@@ -91,7 +91,7 @@ def __new__(cls, name, bases, attrs):
                 if opts.exclude and f.name in opts.exclude:
                     continue
 
-                if f.name in declared_fields:
+                if f.name in set(declared_fields.keys()):
                     # If model field is declared in `ModelResource`,
                     # remove it from `declared_fields`
                     # to keep exact order of model fields
diff --git a/import_export/resources.py b/import_export/resources.py
--- a/import_export/resources.py
+++ b/import_export/resources.py
@@ -431,6 +431,9 @@ def import_field(self, field, instance, row, is_m2m=False, **kwargs):
         field.save(instance, row, is_m2m, **kwargs)
 
     def get_import_fields(self):
+        import_fields = []
+        for f in self.fields:
+            import_fields.append(f)
         return [self.fields[f] for f in self.get_import_order()]
 
     def import_obj(self, obj, data, dry_run, **kwargs):
@@ -1037,7 +1040,8 @@ def export_field(self, field, instance):
         return field.export(instance)
 
     def get_export_fields(self):
-        return [self.fields[f] for f in self.get_export_order()]
+        export_order = self.get_export_order()
+        return [self.fields[f] for f in export_order]
 
     def export_resource(self, instance, fields=None):
         export_fields = self.get_export_fields()
@@ -1046,18 +1050,17 @@ def export_resource(self, instance, fields=None):
             return [
                 self.export_field(field, instance)
                 for field in export_fields
-                if field.column_name in fields
+                if field.attribute in fields
             ]
 
         return [self.export_field(field, instance) for field in export_fields]
 
     def get_export_headers(self, fields=None):
-        headers = [force_str(field.column_name) for field in self.get_export_fields()]
-
+        export_fields = self.get_export_fields()
         if isinstance(fields, list) and fields:
-            return [f for f in headers if f in fields]
+            return [f.column_name for f in export_fields if f.attribute in fields]
 
-        return headers
+        return [force_str(field.column_name) for field in export_fields]
 
     def get_user_visible_fields(self):
         return self.get_fields()
@@ -1097,7 +1100,8 @@ def export(self, queryset=None, **kwargs):
         dataset = tablib.Dataset(headers=headers)
 
         for obj in self.iter_queryset(queryset):
-            dataset.append(self.export_resource(obj, fields=export_fields))
+            r = self.export_resource(obj, fields=export_fields)
+            dataset.append(r)
 
         self.after_export(queryset, dataset, **kwargs)
 


Test patch:
diff --git a/tests/core/admin.py b/tests/core/admin.py
--- a/tests/core/admin.py
+++ b/tests/core/admin.py
@@ -5,6 +5,7 @@
     ImportExportModelAdmin,
     ImportMixin,
 )
+from import_export.fields import Field
 from import_export.resources import ModelResource
 
 from .forms import CustomConfirmImportForm, CustomExportForm, CustomImportForm
@@ -50,6 +51,8 @@ class AuthorAdmin(ImportMixin, admin.ModelAdmin):
 
 
 class EBookResource(ModelResource):
+    published = Field(attribute="published", column_name="published_date")
+
     def __init__(self, **kwargs):
         super().__init__()
         self.author_id = kwargs.get("author_id")
@@ -59,11 +62,7 @@ def filter_export(self, queryset, **kwargs):
 
     class Meta:
         model = EBook
-        fields = (
-            "id",
-            "author_email",
-            "name",
-        )
+        fields = ("id", "author_email", "name", "published")
 
 
 class CustomBookAdmin(ImportExportModelAdmin):
@@ -97,10 +96,10 @@ def get_export_resource_kwargs(self, request, **kwargs):
         # this is overridden to demonstrate that custom form fields can be used
         # to override the export query.
         # The dict returned here will be passed as kwargs to EBookResource
-        export_form = kwargs["export_form"]
+        export_form = kwargs.get("export_form")
         if export_form:
-            return dict(author_id=export_form.cleaned_data["author"].id)
-        return {}
+            kwargs.update(author_id=export_form.cleaned_data["author"].id)
+        return kwargs
 
 
 admin.site.register(Book, BookAdmin)
diff --git a/tests/core/forms.py b/tests/core/forms.py
--- a/tests/core/forms.py
+++ b/tests/core/forms.py
@@ -1,6 +1,10 @@
 from django import forms
 
-from import_export.forms import ConfirmImportForm, ExportForm, ImportForm
+from import_export.forms import (
+    ConfirmImportForm,
+    ImportForm,
+    SelectableFieldsExportForm,
+)
 
 from .models import Author
 
@@ -21,7 +25,7 @@ class CustomConfirmImportForm(AuthorFormMixin, ConfirmImportForm):
     pass
 
 
-class CustomExportForm(AuthorFormMixin, ExportForm):
+class CustomExportForm(AuthorFormMixin, SelectableFieldsExportForm):
     """Customized ExportForm, with author field required"""
 
     pass
diff --git a/tests/core/tests/admin_integration/test_export.py b/tests/core/tests/admin_integration/test_export.py
--- a/tests/core/tests/admin_integration/test_export.py
+++ b/tests/core/tests/admin_integration/test_export.py
@@ -308,6 +308,29 @@ def test_export_model_with_custom_PK(self):
         self.assertEqual(response.status_code, 200)
         self.assertContains(response, "UUIDCategoryResource")
 
+    def test_export_with_custom_field(self):
+        # issue 1808
+        a = Author.objects.create(id=11, name="Ian Fleming")
+        data = {
+            "format": "0",
+            "author": a.id,
+            "resource": "",
+            "ebookresource_id": True,
+            "ebookresource_author_email": True,
+            "ebookresource_name": True,
+            "ebookresource_published": True,
+        }
+        date_str = datetime.now().strftime("%Y-%m-%d")
+        response = self.client.post(self.ebook_export_url, data)
+        self.assertEqual(response.status_code, 200)
+        self.assertTrue(response.has_header("Content-Disposition"))
+        self.assertEqual(response["Content-Type"], "text/csv")
+        self.assertEqual(
+            response["Content-Disposition"],
+            'attachment; filename="EBook-{}.csv"'.format(date_str),
+        )
+        self.assertEqual(b"id,author_email,name,published_date\r\n", response.content)
+
 
 class FilteredExportAdminIntegrationTest(AdminTestMixin, TestCase):
     fixtures = ["category", "book", "author"]
@@ -316,7 +339,14 @@ def test_export_filters_by_form_param(self):
         # issue 1578
         author = Author.objects.get(name="Ian Fleming")
 
-        data = {"format": "0", "author": str(author.id)}
+        data = {
+            "format": "0",
+            "author": str(author.id),
+            "ebookresource_id": True,
+            "ebookresource_author_email": True,
+            "ebookresource_name": True,
+            "ebookresource_published": True,
+        }
         date_str = datetime.now().strftime("%Y-%m-%d")
         response = self.client.post(self.ebook_export_url, data)
         self.assertEqual(response.status_code, 200)
@@ -327,8 +357,8 @@ def test_export_filters_by_form_param(self):
             'attachment; filename="EBook-{}.csv"'.format(date_str),
         )
         self.assertEqual(
-            b"id,author_email,name\r\n"
-            b"5,ian@example.com,The Man with the Golden Gun\r\n",
+            b"id,author_email,name,published_date\r\n"
+            b"5,ian@example.com,The Man with the Golden Gun,1965-04-01\r\n",
             response.content,
         )
 
diff --git a/tests/core/tests/admin_integration/test_import.py b/tests/core/tests/admin_integration/test_import.py
--- a/tests/core/tests/admin_integration/test_import.py
+++ b/tests/core/tests/admin_integration/test_import.py
@@ -932,8 +932,14 @@ def test_import_preview_order(self):
         )
         # test header rendered in correct order
         target_header_re = (
-            r"<thead>[\\n\s]+<tr>[\\n\s]+<th></th>[\\n\s]+<th>id</th>"
-            r"[\\n\s]+<th>author_email</th>[\\n\s]+<th>name</th>[\\n\s]+</tr>[\\n\s]+"
+            r"<thead>[\\n\s]+"
+            r"<tr>[\\n\s]+"
+            r"<th></th>[\\n\s]+"
+            r"<th>id</th>[\\n\s]+"
+            r"<th>author_email</th>[\\n\s]+"
+            r"<th>name</th>[\\n\s]+"
+            r"<th>published_date</th>[\\n\s]+"
+            r"</tr>[\\n\s]+"
             "</thead>"
         )
         self.assertRegex(str(response.content), target_header_re)
@@ -944,6 +950,7 @@ def test_import_preview_order(self):
             r'<td><ins style="background:#e6ffe6;">1</ins></td>[\\n\s]+'
             r'<td><ins style="background:#e6ffe6;">test@example.com</ins></td>[\\n\s]+'
             r'<td><ins style="background:#e6ffe6;">Some book</ins></td>[\\n\s]+'
+            r"<td><span>None</span></td>[\\n\s]+"
             "</tr>"
         )
         self.assertRegex(str(response.content), target_row_re)
diff --git a/tests/core/tests/test_resources/test_modelresource/test_export.py b/tests/core/tests/test_resources/test_modelresource/test_export.py
--- a/tests/core/tests/test_resources/test_modelresource/test_export.py
+++ b/tests/core/tests/test_resources/test_modelresource/test_export.py
@@ -1,9 +1,8 @@
 import tablib
+from core.admin import BookResource
 from core.models import Author, Book
 from django.test import TestCase
 
-from tests.core.tests.resources import BookResource
-
 
 class ExportFunctionalityTest(TestCase):
     def setUp(self):
@@ -22,7 +21,8 @@ def test_get_export_headers(self):
                 "name",
                 "author",
                 "author_email",
-                "published_date",
+                "imported",
+                "published",
                 "published_time",
                 "price",
                 "added",