django-import-export 3.0.2__tar.gz → 3.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. {django-import-export-3.0.2 → django-import-export-3.2.0}/AUTHORS +8 -1
  2. {django-import-export-3.0.2/django_import_export.egg-info → django-import-export-3.2.0}/PKG-INFO +7 -47
  3. {django-import-export-3.0.2 → django-import-export-3.2.0}/README.rst +4 -44
  4. {django-import-export-3.0.2 → django-import-export-3.2.0/django_import_export.egg-info}/PKG-INFO +7 -47
  5. django-import-export-3.2.0/django_import_export.egg-info/requires.txt +3 -0
  6. django-import-export-3.2.0/import_export/__init__.py +1 -0
  7. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/admin.py +85 -56
  8. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/exceptions.py +0 -5
  9. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/fields.py +27 -3
  10. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/formats/base_formats.py +36 -3
  11. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/forms.py +17 -3
  12. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/ar/LC_MESSAGES/django.po +11 -11
  13. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/bg/LC_MESSAGES/django.po +11 -11
  14. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/ca/LC_MESSAGES/django.po +11 -11
  15. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/cs/LC_MESSAGES/django.po +11 -11
  16. django-import-export-3.2.0/import_export/locale/de/LC_MESSAGES/django.mo +0 -0
  17. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/de/LC_MESSAGES/django.po +16 -14
  18. django-import-export-3.2.0/import_export/locale/es/LC_MESSAGES/django.mo +0 -0
  19. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/es/LC_MESSAGES/django.po +26 -20
  20. django-import-export-3.2.0/import_export/locale/es_AR/LC_MESSAGES/django.mo +0 -0
  21. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/es_AR/LC_MESSAGES/django.po +24 -18
  22. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/fa/LC_MESSAGES/django.po +11 -11
  23. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/fr/LC_MESSAGES/django.po +11 -11
  24. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/it/LC_MESSAGES/django.po +11 -11
  25. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/ja/LC_MESSAGES/django.po +11 -11
  26. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/ko/LC_MESSAGES/django.po +11 -11
  27. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/kz/LC_MESSAGES/django.po +11 -11
  28. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/nl/LC_MESSAGES/django.po +11 -11
  29. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/pl/LC_MESSAGES/django.po +11 -11
  30. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/pt_BR/LC_MESSAGES/django.po +11 -11
  31. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/ru/LC_MESSAGES/django.po +11 -11
  32. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/sk/LC_MESSAGES/django.po +11 -11
  33. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/tr/LC_MESSAGES/django.po +11 -11
  34. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/zh_Hans/LC_MESSAGES/django.po +11 -11
  35. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/mixins.py +33 -2
  36. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/resources.py +80 -67
  37. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/widgets.py +13 -4
  38. {django-import-export-3.0.2 → django-import-export-3.2.0}/setup.py +3 -3
  39. django-import-export-3.0.2/django_import_export.egg-info/requires.txt +0 -3
  40. django-import-export-3.0.2/import_export/__init__.py +0 -1
  41. django-import-export-3.0.2/import_export/locale/de/LC_MESSAGES/django.mo +0 -0
  42. django-import-export-3.0.2/import_export/locale/es/LC_MESSAGES/django.mo +0 -0
  43. django-import-export-3.0.2/import_export/locale/es_AR/LC_MESSAGES/django.mo +0 -0
  44. {django-import-export-3.0.2 → django-import-export-3.2.0}/LICENSE +0 -0
  45. {django-import-export-3.0.2 → django-import-export-3.2.0}/MANIFEST.in +0 -0
  46. {django-import-export-3.0.2 → django-import-export-3.2.0}/django_import_export.egg-info/SOURCES.txt +0 -0
  47. {django-import-export-3.0.2 → django-import-export-3.2.0}/django_import_export.egg-info/dependency_links.txt +0 -0
  48. {django-import-export-3.0.2 → django-import-export-3.2.0}/django_import_export.egg-info/not-zip-safe +0 -0
  49. {django-import-export-3.0.2 → django-import-export-3.2.0}/django_import_export.egg-info/top_level.txt +0 -0
  50. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/formats/__init__.py +0 -0
  51. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/instance_loaders.py +0 -0
  52. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/ar/LC_MESSAGES/django.mo +0 -0
  53. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/bg/LC_MESSAGES/django.mo +0 -0
  54. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/ca/LC_MESSAGES/django.mo +0 -0
  55. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/cs/LC_MESSAGES/django.mo +0 -0
  56. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/fa/LC_MESSAGES/django.mo +0 -0
  57. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/fr/LC_MESSAGES/django.mo +0 -0
  58. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/it/LC_MESSAGES/django.mo +0 -0
  59. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/ja/LC_MESSAGES/django.mo +0 -0
  60. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/ko/LC_MESSAGES/django.mo +0 -0
  61. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/kz/LC_MESSAGES/django.mo +0 -0
  62. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/nl/LC_MESSAGES/django.mo +0 -0
  63. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/pl/LC_MESSAGES/django.mo +0 -0
  64. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
  65. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/ru/LC_MESSAGES/django.mo +0 -0
  66. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/sk/LC_MESSAGES/django.mo +0 -0
  67. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/tr/LC_MESSAGES/django.mo +0 -0
  68. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  69. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/results.py +0 -0
  70. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/signals.py +0 -0
  71. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/static/import_export/action_formats.js +0 -0
  72. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/static/import_export/guess_format.js +0 -0
  73. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/static/import_export/import.css +0 -0
  74. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/templates/admin/import_export/base.html +0 -0
  75. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/templates/admin/import_export/change_list.html +0 -0
  76. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/templates/admin/import_export/change_list_export.html +0 -0
  77. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/templates/admin/import_export/change_list_export_item.html +0 -0
  78. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/templates/admin/import_export/change_list_import.html +0 -0
  79. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/templates/admin/import_export/change_list_import_export.html +0 -0
  80. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/templates/admin/import_export/change_list_import_item.html +0 -0
  81. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/templates/admin/import_export/export.html +0 -0
  82. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/templates/admin/import_export/import.html +0 -0
  83. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/templatetags/__init__.py +0 -0
  84. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/templatetags/import_export_tags.py +0 -0
  85. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/tmp_storages.py +0 -0
  86. {django-import-export-3.0.2 → django-import-export-3.2.0}/import_export/utils.py +0 -0
  87. {django-import-export-3.0.2 → django-import-export-3.2.0}/setup.cfg +0 -0
@@ -131,5 +131,12 @@ The following is a list of much appreciated contributors:
131
131
  * HaPyTeX (Willem Van Onsem)
132
132
  * nikhaldi (Nik Haldimann)
133
133
  * TheRealVizard (Eduardo Leyva)
134
+ * 1gni5 (Jules Ducange)
134
135
  * mpasternak (Michał Pasternak)
135
- * nikatlas (Nikos Atlas)
136
+ * nikatlas (Nikos Atlas)
137
+ * cocorocho (Erkan Çoban)
138
+ * bdnettleton (Brian Nettleton)
139
+ * Ptosiek (Antonin)
140
+ * samupl (Jakub Szafrański)
141
+ * smunoz-ml (Santiago Muñoz)
142
+ * carlosal0ns0 (Carlos Alonso)
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-import-export
3
- Version: 3.0.2
3
+ Version: 3.2.0
4
4
  Summary: Django application and library for importing and exporting data with included admin integration.
5
5
  Home-page: https://github.com/django-import-export/django-import-export
6
- Author: Informatika Mihelac
7
- Author-email: bmihelac@mihelac.org
6
+ Author: Bojan Mihelač
7
+ Author-email: djangoimportexport@gmail.com
8
8
  License: BSD License
9
9
  Project-URL: Documentation, https://django-import-export.readthedocs.io/en/stable/
10
10
  Project-URL: Changelog, https://django-import-export.readthedocs.io/en/stable/changelog.html
@@ -53,53 +53,13 @@ django-import-export
53
53
  .. image:: https://img.shields.io/pypi/djversions/django-import-export
54
54
  :alt: PyPI - Django Version
55
55
 
56
- django-import-export is a Django application and library for importing
57
- and exporting data with included admin integration.
58
-
59
- Features:
60
-
61
- * support multiple formats (Excel, CSV, JSON, ...
62
- and everything else that `tablib`_ supports)
63
-
64
- * admin integration for importing
65
-
66
- * preview import changes
67
-
68
- * admin integration for exporting
69
-
70
- * export data respecting admin filters
71
-
72
- .. image:: https://raw.githubusercontent.com/django-import-export/django-import-export/main/docs/_static/images/django-import-export-change.png
56
+ .. image:: https://static.pepy.tech/personalized-badge/django-import-export?period=month&units=international_system&left_color=black&right_color=blue&left_text=Downloads/month
57
+ :target: https://pepy.tech/project/django-import-export
73
58
 
59
+ django-import-export is a Django application and library for importing
60
+ and exporting data from a variety of formats. Includes Django Admin site integration.
74
61
 
75
62
  * Documentation: https://django-import-export.readthedocs.io/en/stable/
76
63
  * GitHub: https://github.com/django-import-export/django-import-export/
77
64
  * Free software: BSD license
78
65
  * PyPI: https://pypi.org/project/django-import-export/
79
-
80
- Example app
81
- -----------
82
-
83
- To run the demo app::
84
-
85
- cd tests
86
- ./manage.py makemigrations
87
- ./manage.py migrate
88
- ./manage.py createsuperuser
89
- ./manage.py loaddata category book
90
- ./manage.py runserver
91
-
92
- Contribute
93
- ----------
94
-
95
- If you'd like to contribute, simply fork `the repository`_, commit your
96
- changes to the **develop** branch (or branch off of it), and send a pull
97
- request. Make sure you add yourself to AUTHORS_.
98
-
99
- As most projects, we try to follow PEP8_ as closely as possible. Please bear
100
- in mind that most pull requests will be rejected without proper unit testing.
101
-
102
- .. _`PEP8`: https://www.python.org/dev/peps/pep-0008/
103
- .. _`tablib`: https://github.com/jazzband/tablib
104
- .. _`the repository`: https://github.com/django-import-export/django-import-export/
105
- .. _AUTHORS: https://github.com/django-import-export/django-import-export/blob/master/AUTHORS
@@ -23,53 +23,13 @@ django-import-export
23
23
  .. image:: https://img.shields.io/pypi/djversions/django-import-export
24
24
  :alt: PyPI - Django Version
25
25
 
26
- django-import-export is a Django application and library for importing
27
- and exporting data with included admin integration.
28
-
29
- Features:
30
-
31
- * support multiple formats (Excel, CSV, JSON, ...
32
- and everything else that `tablib`_ supports)
33
-
34
- * admin integration for importing
35
-
36
- * preview import changes
37
-
38
- * admin integration for exporting
39
-
40
- * export data respecting admin filters
41
-
42
- .. image:: https://raw.githubusercontent.com/django-import-export/django-import-export/main/docs/_static/images/django-import-export-change.png
26
+ .. image:: https://static.pepy.tech/personalized-badge/django-import-export?period=month&units=international_system&left_color=black&right_color=blue&left_text=Downloads/month
27
+ :target: https://pepy.tech/project/django-import-export
43
28
 
29
+ django-import-export is a Django application and library for importing
30
+ and exporting data from a variety of formats. Includes Django Admin site integration.
44
31
 
45
32
  * Documentation: https://django-import-export.readthedocs.io/en/stable/
46
33
  * GitHub: https://github.com/django-import-export/django-import-export/
47
34
  * Free software: BSD license
48
35
  * PyPI: https://pypi.org/project/django-import-export/
49
-
50
- Example app
51
- -----------
52
-
53
- To run the demo app::
54
-
55
- cd tests
56
- ./manage.py makemigrations
57
- ./manage.py migrate
58
- ./manage.py createsuperuser
59
- ./manage.py loaddata category book
60
- ./manage.py runserver
61
-
62
- Contribute
63
- ----------
64
-
65
- If you'd like to contribute, simply fork `the repository`_, commit your
66
- changes to the **develop** branch (or branch off of it), and send a pull
67
- request. Make sure you add yourself to AUTHORS_.
68
-
69
- As most projects, we try to follow PEP8_ as closely as possible. Please bear
70
- in mind that most pull requests will be rejected without proper unit testing.
71
-
72
- .. _`PEP8`: https://www.python.org/dev/peps/pep-0008/
73
- .. _`tablib`: https://github.com/jazzband/tablib
74
- .. _`the repository`: https://github.com/django-import-export/django-import-export/
75
- .. _AUTHORS: https://github.com/django-import-export/django-import-export/blob/master/AUTHORS
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-import-export
3
- Version: 3.0.2
3
+ Version: 3.2.0
4
4
  Summary: Django application and library for importing and exporting data with included admin integration.
5
5
  Home-page: https://github.com/django-import-export/django-import-export
6
- Author: Informatika Mihelac
7
- Author-email: bmihelac@mihelac.org
6
+ Author: Bojan Mihelač
7
+ Author-email: djangoimportexport@gmail.com
8
8
  License: BSD License
9
9
  Project-URL: Documentation, https://django-import-export.readthedocs.io/en/stable/
10
10
  Project-URL: Changelog, https://django-import-export.readthedocs.io/en/stable/changelog.html
@@ -53,53 +53,13 @@ django-import-export
53
53
  .. image:: https://img.shields.io/pypi/djversions/django-import-export
54
54
  :alt: PyPI - Django Version
55
55
 
56
- django-import-export is a Django application and library for importing
57
- and exporting data with included admin integration.
58
-
59
- Features:
60
-
61
- * support multiple formats (Excel, CSV, JSON, ...
62
- and everything else that `tablib`_ supports)
63
-
64
- * admin integration for importing
65
-
66
- * preview import changes
67
-
68
- * admin integration for exporting
69
-
70
- * export data respecting admin filters
71
-
72
- .. image:: https://raw.githubusercontent.com/django-import-export/django-import-export/main/docs/_static/images/django-import-export-change.png
56
+ .. image:: https://static.pepy.tech/personalized-badge/django-import-export?period=month&units=international_system&left_color=black&right_color=blue&left_text=Downloads/month
57
+ :target: https://pepy.tech/project/django-import-export
73
58
 
59
+ django-import-export is a Django application and library for importing
60
+ and exporting data from a variety of formats. Includes Django Admin site integration.
74
61
 
75
62
  * Documentation: https://django-import-export.readthedocs.io/en/stable/
76
63
  * GitHub: https://github.com/django-import-export/django-import-export/
77
64
  * Free software: BSD license
78
65
  * PyPI: https://pypi.org/project/django-import-export/
79
-
80
- Example app
81
- -----------
82
-
83
- To run the demo app::
84
-
85
- cd tests
86
- ./manage.py makemigrations
87
- ./manage.py migrate
88
- ./manage.py createsuperuser
89
- ./manage.py loaddata category book
90
- ./manage.py runserver
91
-
92
- Contribute
93
- ----------
94
-
95
- If you'd like to contribute, simply fork `the repository`_, commit your
96
- changes to the **develop** branch (or branch off of it), and send a pull
97
- request. Make sure you add yourself to AUTHORS_.
98
-
99
- As most projects, we try to follow PEP8_ as closely as possible. Please bear
100
- in mind that most pull requests will be rejected without proper unit testing.
101
-
102
- .. _`PEP8`: https://www.python.org/dev/peps/pep-0008/
103
- .. _`tablib`: https://github.com/jazzband/tablib
104
- .. _`the repository`: https://github.com/django-import-export/django-import-export/
105
- .. _AUTHORS: https://github.com/django-import-export/django-import-export/blob/master/AUTHORS
@@ -0,0 +1,3 @@
1
+ diff-match-patch
2
+ Django>=3.2
3
+ tablib[html,ods,xls,xlsx,yaml]>=3.4.0
@@ -0,0 +1 @@
1
+ __version__ = '3.2.0'
@@ -172,17 +172,17 @@ class ImportMixin(BaseImportMixin, ImportExportMixinBase):
172
172
 
173
173
  return self.process_result(result, request)
174
174
 
175
- def process_dataset(self, dataset, confirm_form, request, *args, **kwargs):
175
+ def process_dataset(self, dataset, confirm_form, request, *args, rollback_on_validation_errors=False, **kwargs):
176
176
 
177
- res_kwargs = self.get_import_resource_kwargs(request, form=confirm_form, *args, **kwargs)
177
+ res_kwargs = self.get_import_resource_kwargs(request, *args, form=confirm_form, **kwargs)
178
178
  resource = self.choose_import_resource_class(confirm_form)(**res_kwargs)
179
+ imp_kwargs = self.get_import_data_kwargs(request, *args, form=confirm_form, **kwargs)
179
180
 
180
- imp_kwargs = self.get_import_data_kwargs(request, form=confirm_form, *args, **kwargs)
181
181
  return resource.import_data(dataset,
182
182
  dry_run=False,
183
- raise_errors=True,
184
- file_name=confirm_form.cleaned_data['original_file_name'],
183
+ file_name=confirm_form.cleaned_data.get('original_file_name'),
185
184
  user=request.user,
185
+ rollback_on_validation_errors=True,
186
186
  **imp_kwargs)
187
187
 
188
188
  def process_result(self, result, request):
@@ -264,7 +264,7 @@ class ImportMixin(BaseImportMixin, ImportExportMixinBase):
264
264
  .. deprecated:: 3.0
265
265
  Use :meth:`~import_export.admin.ImportMixin.get_import_form_kwargs` or
266
266
  :meth:`~import_export.admin.ImportMixin.get_confirm_form_kwargs`
267
- instead, depending on which form you wish to customise.
267
+ instead, depending on which form you wish to customize.
268
268
  """
269
269
  warnings.warn(
270
270
  "ImportMixin.get_form_kwargs() is deprecated and will be removed "
@@ -299,7 +299,7 @@ class ImportMixin(BaseImportMixin, ImportExportMixinBase):
299
299
  category=DeprecationWarning
300
300
  )
301
301
  return form_class(formats, **kwargs)
302
- return form_class(formats, self.get_import_resource_classes(), **kwargs)
302
+ return form_class(formats, resources=self.get_import_resource_classes(), **kwargs)
303
303
 
304
304
  def get_import_form_class(self, request):
305
305
  """
@@ -451,6 +451,12 @@ class ImportMixin(BaseImportMixin, ImportExportMixinBase):
451
451
  tmp_storage.save(data)
452
452
  return tmp_storage
453
453
 
454
+ def add_data_read_fail_error_to_form(self, form, e):
455
+ form.add_error('import_file',
456
+ _(f"'{type(e).__name__}' encountered while trying to read file. "
457
+ "Ensure you have chosen the correct format for the file. "
458
+ f"{str(e)}"))
459
+
454
460
  def import_action(self, request, *args, **kwargs):
455
461
  """
456
462
  Perform a dry_run of the import to make sure the import will not
@@ -474,9 +480,9 @@ class ImportMixin(BaseImportMixin, ImportExportMixinBase):
474
480
  if issubclass(form_class, ImportExportFormBase):
475
481
  import_form = form_class(
476
482
  import_formats,
477
- self.get_import_resource_classes(),
478
483
  request.POST or None,
479
484
  request.FILES or None,
485
+ resources=self.get_import_resource_classes(),
480
486
  **form_kwargs
481
487
  )
482
488
  else:
@@ -498,54 +504,74 @@ class ImportMixin(BaseImportMixin, ImportExportMixinBase):
498
504
  if not input_format.is_binary():
499
505
  input_format.encoding = self.from_encoding
500
506
  import_file = import_form.cleaned_data['import_file']
501
- # first always write the uploaded file to disk as it may be a
502
- # memory file or else based on settings upload handlers
503
- tmp_storage = self.write_to_tmp_storage(import_file, input_format)
504
- # allows get_confirm_form_initial() to include both the
505
- # original and saved file names from form.cleaned_data
506
- import_file.tmp_storage_name = tmp_storage.name
507
-
508
- try:
509
- # then read the file, using the proper format-specific mode
510
- # warning, big files may exceed memory
511
- data = tmp_storage.read()
512
- dataset = input_format.create_dataset(data)
513
- except Exception as e:
514
- import_form.add_error('import_file',
515
- _(f"'{type(e).__name__}' encountered while trying to read file. "
516
- "Ensure you have chosen the correct format for the file. "
517
- f"{str(e)}"))
518
-
519
- if not import_form.errors:
520
- # prepare kwargs for import data, if needed
521
- res_kwargs = self.get_import_resource_kwargs(request, form=import_form, *args, **kwargs)
522
- resource = self.choose_import_resource_class(import_form)(**res_kwargs)
523
- resources = [resource]
524
-
525
- # prepare additional kwargs for import_data, if needed
526
- imp_kwargs = self.get_import_data_kwargs(request, form=import_form, *args, **kwargs)
527
- result = resource.import_data(dataset, dry_run=True,
528
- raise_errors=False,
529
- file_name=import_file.name,
530
- user=request.user,
531
- **imp_kwargs)
532
-
533
- context['result'] = result
534
-
535
- if not result.has_errors() and not result.has_validation_errors():
536
- if getattr(self.get_form_kwargs, "is_original", False):
537
- # Use new API
538
- context["confirm_form"] = self.create_confirm_form(
539
- request, import_form=import_form
540
- )
507
+
508
+ if getattr(settings, "IMPORT_EXPORT_SKIP_ADMIN_CONFIRM", False):
509
+ # This setting means we are going to skip the import confirmation step.
510
+ # Go ahead and process the file for import in a transaction
511
+ # If there are any errors, we roll back the transaction.
512
+ # rollback_on_validation_errors is set to True so that we rollback on
513
+ # validation errors. If this is not done validation errors would be
514
+ # silently skipped.
515
+ data = bytes()
516
+ for chunk in import_file.chunks():
517
+ data += chunk
518
+ try:
519
+ dataset = input_format.create_dataset(data)
520
+ except Exception as e:
521
+ self.add_data_read_fail_error_to_form(import_form, e)
522
+ if not import_form.errors:
523
+ result = self.process_dataset(dataset, import_form, request, *args, raise_errors=False,
524
+ rollback_on_validation_errors=True, **kwargs)
525
+ if not result.has_errors() and not result.has_validation_errors():
526
+ return self.process_result(result, request)
541
527
  else:
542
- confirm_form_class = self.get_confirm_form_class(request)
543
- initial = self.get_confirm_form_initial(request, import_form)
544
- context["confirm_form"] = confirm_form_class(
545
- initial=self.get_form_kwargs(form=import_form, **initial)
546
- )
528
+ context['result'] = result
529
+ else:
530
+ # first always write the uploaded file to disk as it may be a
531
+ # memory file or else based on settings upload handlers
532
+ tmp_storage = self.write_to_tmp_storage(import_file, input_format)
533
+ # allows get_confirm_form_initial() to include both the
534
+ # original and saved file names from form.cleaned_data
535
+ import_file.tmp_storage_name = tmp_storage.name
536
+
537
+ try:
538
+ # then read the file, using the proper format-specific mode
539
+ # warning, big files may exceed memory
540
+ data = tmp_storage.read()
541
+ dataset = input_format.create_dataset(data)
542
+ except Exception as e:
543
+ self.add_data_read_fail_error_to_form(import_form, e)
544
+
545
+ if not import_form.errors:
546
+ # prepare kwargs for import data, if needed
547
+ res_kwargs = self.get_import_resource_kwargs(request, *args, form=import_form, **kwargs)
548
+ resource = self.choose_import_resource_class(import_form)(**res_kwargs)
549
+ resources = [resource]
550
+
551
+ # prepare additional kwargs for import_data, if needed
552
+ imp_kwargs = self.get_import_data_kwargs(request, *args, form=import_form, **kwargs)
553
+ result = resource.import_data(dataset, dry_run=True,
554
+ raise_errors=False,
555
+ file_name=import_file.name,
556
+ user=request.user,
557
+ **imp_kwargs)
558
+
559
+ context['result'] = result
560
+
561
+ if not result.has_errors() and not result.has_validation_errors():
562
+ if getattr(self.get_form_kwargs, "is_original", False):
563
+ # Use new API
564
+ context["confirm_form"] = self.create_confirm_form(
565
+ request, import_form=import_form
566
+ )
567
+ else:
568
+ confirm_form_class = self.get_confirm_form_class(request)
569
+ initial = self.get_confirm_form_initial(request, import_form)
570
+ context["confirm_form"] = confirm_form_class(
571
+ initial=self.get_form_kwargs(form=import_form, **initial)
572
+ )
547
573
  else:
548
- res_kwargs = self.get_import_resource_kwargs(request, form=import_form, *args, **kwargs)
574
+ res_kwargs = self.get_import_resource_kwargs(request, *args, form=import_form, **kwargs)
549
575
  resource_classes = self.get_import_resource_classes()
550
576
  resources = [resource_class(**res_kwargs) for resource_class in resource_classes]
551
577
 
@@ -653,7 +679,10 @@ class ExportMixin(BaseExportMixin, ImportExportMixinBase):
653
679
  raise PermissionDenied
654
680
 
655
681
  data = self.get_data_for_export(request, queryset, *args, **kwargs)
656
- export_data = file_format.export_data(data)
682
+ export_data = file_format.export_data(data,
683
+ escape_output=self.should_escape_output,
684
+ escape_html=self.should_escape_html,
685
+ escape_formulae=self.should_escape_formulae)
657
686
  encoding = kwargs.get("encoding")
658
687
  if not file_format.is_binary() and encoding:
659
688
  export_data = export_data.encode(encoding)
@@ -697,7 +726,7 @@ class ExportMixin(BaseExportMixin, ImportExportMixinBase):
697
726
  else:
698
727
  form_type = self.get_export_form()
699
728
  formats = self.get_export_formats()
700
- form = form_type(formats, self.get_export_resource_classes(), request.POST or None)
729
+ form = form_type(formats, request.POST or None, resources=self.get_export_resource_classes())
701
730
  if form.is_valid():
702
731
  file_format = formats[
703
732
  int(form.cleaned_data['file_format'])
@@ -1,8 +1,3 @@
1
- import warnings
2
-
3
- warnings.warn("the exceptions module is deprecated and will be removed in a future release", DeprecationWarning)
4
-
5
-
6
1
  class ImportExportError(Exception):
7
2
  """A generic exception for all others to extend."""
8
3
  pass
@@ -3,6 +3,7 @@ from django.db.models.fields import NOT_PROVIDED
3
3
  from django.db.models.manager import Manager
4
4
 
5
5
  from . import widgets
6
+ from .exceptions import FieldError
6
7
 
7
8
 
8
9
  class Field:
@@ -17,7 +18,7 @@ class Field:
17
18
  this field in the export.
18
19
 
19
20
  :param widget: Defines a widget that will be used to represent this
20
- field's data in the export.
21
+ field's data in the export, or transform the value during import.
21
22
 
22
23
  :param readonly: A Boolean which defines if this field will be ignored
23
24
  during import.
@@ -27,11 +28,17 @@ class Field:
27
28
  not return an adequate value.
28
29
 
29
30
  :param saves_null_values: Controls whether null values are saved on the object
31
+ :param dehydrate_method: Lets you choose your own method for dehydration rather
32
+ than using `dehydrate_{field_name}` syntax.
33
+ :param m2m_add: changes save of this field to add the values, if they do not exist,
34
+ to a ManyToMany field instead of setting all values. Only useful if field is
35
+ a ManyToMany field.
30
36
  """
31
37
  empty_values = [None, '']
32
38
 
33
39
  def __init__(self, attribute=None, column_name=None, widget=None,
34
- default=NOT_PROVIDED, readonly=False, saves_null_values=True):
40
+ default=NOT_PROVIDED, readonly=False, saves_null_values=True,
41
+ dehydrate_method=None, m2m_add=False):
35
42
  self.attribute = attribute
36
43
  self.default = default
37
44
  self.column_name = column_name
@@ -40,6 +47,8 @@ class Field:
40
47
  self.widget = widget
41
48
  self.readonly = readonly
42
49
  self.saves_null_values = saves_null_values
50
+ self.dehydrate_method = dehydrate_method
51
+ self.m2m_add = m2m_add
43
52
 
44
53
  def __repr__(self):
45
54
  """
@@ -112,7 +121,10 @@ class Field:
112
121
  if not is_m2m:
113
122
  setattr(obj, attrs[-1], cleaned)
114
123
  else:
115
- getattr(obj, attrs[-1]).set(cleaned)
124
+ if self.m2m_add:
125
+ getattr(obj, attrs[-1]).add(*cleaned)
126
+ else:
127
+ getattr(obj, attrs[-1]).set(cleaned)
116
128
 
117
129
  def export(self, obj):
118
130
  """
@@ -123,3 +135,15 @@ class Field:
123
135
  if value is None:
124
136
  return ""
125
137
  return self.widget.render(value, obj)
138
+
139
+ def get_dehydrate_method(self, field_name=None):
140
+ """
141
+ Returns method name to be used for dehydration of the field.
142
+ Defaults to `dehydrate_{field_name}`
143
+ """
144
+ DEFAULT_DEHYDRATE_METHOD_PREFIX = "dehydrate_"
145
+
146
+ if not self.dehydrate_method and not field_name:
147
+ raise FieldError("Both dehydrate_method and field_name are not supplied.")
148
+
149
+ return self.dehydrate_method or DEFAULT_DEHYDRATE_METHOD_PREFIX + field_name
@@ -1,3 +1,6 @@
1
+ import html
2
+ import warnings
3
+
1
4
  import tablib
2
5
  from tablib.formats import registry
3
6
 
@@ -12,7 +15,7 @@ class Format:
12
15
  """
13
16
  raise NotImplementedError()
14
17
 
15
- def export_data(self, dataset, **kwargs):
18
+ def export_data(self, dataset, escape_output=False, **kwargs):
16
19
  """
17
20
  Returns format representation for given dataset.
18
21
  """
@@ -80,9 +83,15 @@ class TablibFormat(Format):
80
83
  return self.get_format().title
81
84
 
82
85
  def create_dataset(self, in_stream, **kwargs):
83
- return tablib.import_set(in_stream, format=self.get_title())
86
+ return tablib.import_set(in_stream, format=self.get_title(), **kwargs)
84
87
 
85
88
  def export_data(self, dataset, **kwargs):
89
+ # remove the deprecated `escape_output` param if present
90
+ kwargs.pop("escape_output", None)
91
+ if kwargs.pop("escape_html", None):
92
+ self._escape_html(dataset)
93
+ if kwargs.pop("escape_formulae", None):
94
+ self._escape_formulae(dataset)
86
95
  return dataset.export(self.get_title(), **kwargs)
87
96
 
88
97
  def get_extension(self):
@@ -97,6 +106,21 @@ class TablibFormat(Format):
97
106
  def can_export(self):
98
107
  return hasattr(self.get_format(), 'export_set')
99
108
 
109
+ def _escape_html(self, dataset):
110
+ for _ in dataset:
111
+ row = dataset.lpop()
112
+ row = [html.escape(str(cell)) for cell in row]
113
+ dataset.append(row)
114
+
115
+ def _escape_formulae(self, dataset):
116
+ def _do_escape(s):
117
+ return s.replace("=", "", 1) if s.startswith("=") else s
118
+
119
+ for _ in dataset:
120
+ row = dataset.lpop()
121
+ row = [_do_escape(str(cell)) for cell in row]
122
+ dataset.append(row)
123
+
100
124
 
101
125
  class TextFormat(TablibFormat):
102
126
 
@@ -145,6 +169,16 @@ class HTML(TextFormat):
145
169
  TABLIB_MODULE = 'tablib.formats._html'
146
170
  CONTENT_TYPE = 'text/html'
147
171
 
172
+ def export_data(self, dataset, escape_output=False, **kwargs):
173
+ if escape_output:
174
+ warnings.warn(
175
+ "escape_output flag now deprecated - "
176
+ "this will be removed in a future release",
177
+ DeprecationWarning,
178
+ )
179
+ super()._escape_html(dataset)
180
+ return super().export_data(dataset, **kwargs)
181
+
148
182
 
149
183
  class XLS(TablibFormat):
150
184
  TABLIB_MODULE = 'tablib.formats._xls'
@@ -192,7 +226,6 @@ class XLSX(TablibFormat):
192
226
  dataset.append(row_values)
193
227
  return dataset
194
228
 
195
-
196
229
  #: These are the default formats for import and export. Whether they can be
197
230
  #: used or not is depending on their implementation in the tablib library.
198
231
  DEFAULT_FORMATS = [fmt for fmt in (
@@ -1,4 +1,5 @@
1
1
  import os.path
2
+ import warnings
2
3
 
3
4
  from django import forms
4
5
  from django.conf import settings
@@ -13,8 +14,19 @@ class ImportExportFormBase(forms.Form):
13
14
  required=False,
14
15
  )
15
16
 
16
- def __init__(self, resources=None, *args, **kwargs):
17
+ def __init__(self, *args, resources=None, **kwargs):
17
18
  super().__init__(*args, **kwargs)
19
+ if len(args) == 1 and resources is None:
20
+ # issue 1565: definition of __init__() was incorrect
21
+ # this logic included to aid backwards compatibility,
22
+ # for cases where users are calling with the original form.
23
+ # this check can be removed in a future release
24
+ warnings.warn(
25
+ "'resources' must be supplied as a named parameter",
26
+ category=DeprecationWarning
27
+ )
28
+ resources = args
29
+
18
30
  if resources and len(resources) > 1:
19
31
  resource_choices = []
20
32
  for i, resource in enumerate(resources):
@@ -34,7 +46,8 @@ class ImportForm(ImportExportFormBase):
34
46
  )
35
47
 
36
48
  def __init__(self, import_formats, *args, **kwargs):
37
- super().__init__(*args, **kwargs)
49
+ resources = kwargs.pop("resources", None)
50
+ super().__init__(*args, resources=resources, **kwargs)
38
51
  choices = [
39
52
  (str(i), f().get_title())
40
53
  for i, f in enumerate(import_formats)
@@ -77,7 +90,8 @@ class ExportForm(ImportExportFormBase):
77
90
  )
78
91
 
79
92
  def __init__(self, formats, *args, **kwargs):
80
- super().__init__(*args, **kwargs)
93
+ resources = kwargs.pop("resources", None)
94
+ super().__init__(*args, resources=resources, **kwargs)
81
95
  choices = []
82
96
  for i, f in enumerate(formats):
83
97
  choices.append((str(i), f().get_title(),))