========== Extensions ========== This package is an extension of the original ``django-import-export``, so for more information on the import/export process and advanced resource usage, refer `the official django-import-export documentation `_. The following section describes the features added by this package. -------------------------- ImportJob/ExportJob models -------------------------- ``django-import-export-extensions`` provides the ``ImportJob`` and ``ExportJob`` models to store all information related to the import/export process. Additionally, these models are involved in the background import/export tasks. The job models are already registered in Django Admin and come with custom forms that display all relevant job information, including the current import/export status. .. figure:: _static/images/import-job-admin.png Example of custom form for ImportJob details in Django Admin ``ImportJob``/``ExportJob`` models contain useful properties and methods for managing the import/export process. To learn more, see the :ref:`Models API documentation`. ------------------- Celery admin mixins ------------------- The admin mixins have been completely rewritten, although they still inherit from the base mixins: ``mixins.BaseImportMixin``, ``mixins.BaseExportMixin``, and ``admin.ImportExportMixinBase``. The new Celery admin mixins add pages for displaying import/export status and use custom templates for both the status and results pages. After starting the import/export process, you will be redirected to the status page. Once the import/export is complete, you will then be redirected to the results page. .. figure:: _static/images/export-status.png A screenshot of Django Admin export status page -------- ViewSets -------- The ``ImportJobViewSet`` and ``ExportJobViewSet`` viewsets make it easy to implement import/export functionality via API. To use them, simply create a custom class and set the ``resource_class`` attribute: **api/views.py** .. code-block:: python from import_export_extensions.api import views as import_export_views from . import resources class BandImportViewSet(import_export_views.ImportJobViewSet): resource_class = resources.BandResource class BandExportViewSet(import_export_views.ExportJobViewSet): resource_class = resources.BandResource **urls.py** .. code-block:: python from rest_framework.routers import DefaultRouter from .api import views band_import_export_router = DefaultRouter() band_import_export_router.register( "import-band", views.BandImportViewSet, basename="import-band", ) band_import_export_router.register( "export-band", views.BandExportViewSet, basename="export-band", ) urlpatterns = band_import_export_router.urls By default, all import/export jobs for the set ``resource_class`` will be available, but you can override the ``get_queryset`` method to customize this behavior. Additionally, you can override the ``get_resource_kwargs`` method to provide custom values in the resource class (e.g., for the start action). These viewsets provide all the methods required for the full import/export workflow: start, details, confirm, cancel, and list actions. There is also integration with `drf-spectacular `_. If you have it installed, you can view the generated OpenAPI UI for these viewsets. .. figure:: _static/images/bands-openapi.png A screenshot of autogenerated OpenAPI specification ------- Filters ------- The ``CeleryResource`` and ``CeleryModelResource`` classes also support `django-filter `_ to filter the queryset for export. To use this feature, set the ``filterset_class`` attribute on your resource class and pass filter parameters as the ``filter_kwargs`` argument to the resource: **filters.py** .. code-block:: python from django_filters import rest_framework as filters from . import models class BandFilterSet(filters.FilterSet): class Meta: model = models.Band fields = [ "id", "title", ] **resources.py** .. code-block:: python from import_export_extensions import resources from . import filters from . import models class BandResource(resources.CeleryModelResource): filterset_class = filters.BandFilterSet class Meta: model = models.Band fields = ["id", "title"] If ``filterset_class`` is set for your resource, you can pass ``filter_kwargs`` to filter export queryset: .. code-block:: python :linenos: >>> from .resources import BandResource >>> from .models import Band >>> Band.objects.bulk_create([Band(title=title) for title in "ABC"]) >>> BandResource().get_queryset().count() 3 >>> filter_kwargs = {"title": "A"} >>> band_resource_with_filters = BandResource(filter_kwargs=filter_kwargs) >>> band_resource_with_filters.get_queryset().count() 1 Pass ``filter_kwargs`` in ``resource_kwargs`` argument to create ``ExportJob`` with filtered queryset: .. code-block:: python :linenos: >>> export_job = ExportJob.objects.create( resource_path=BandResource.class_path, file_format_path=file_format_path, resource_kwargs={"filter_kwargs": filter_kwargs}, ) >>> export_job.refresh_from_db() >>> len(export_job.result) 1 Since we are using the Django REST Framework filter set, the ``ExportJobViewSet`` also supports it. It automatically uses the filter set defined in the ``resource_class``. You can see that the start action expects query parameters for filtering: .. figure:: _static/images/filters-openapi.png ------------ Force import ------------ This package includes a "force import" feature. When enabled, rows with errors will be skipped, and the remaining rows will be processed. Admin page ^^^^^^^^^^ This functionality available in admin: .. figure:: _static/images/force_import_admin.png If any rows contain errors, they will be reported during the parse/import stage: .. figure:: _static/images/force_import_results.png API ^^^ In the API, there are two additional fields: ``force_import`` and ``skip_parse_step``. * ``force_import`` - Allows you to skip rows with errors. * ``skip_parse_step`` - Enables you to run the import task immediately, without needing to call the ``confirm`` endpoint. .. image:: _static/images/start_api.png ------- Widgets ------- This package also provides additional widgets for some types of data. FileWidget ^^^^^^^^^^ Working with file fields is a common task. The ``FileWidget`` allows you to import/export files, including links to external resources, and saves via picked ``STORAGE``. This widget loads a file from a URL into the media directory and correctly renders the link for export. It also supports the ``AWS_STORAGE_BUCKET_NAME`` setting. IntermediateManyToManyWidget ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``IntermediateManyToManyWidget`` allows to import/export objects with related items. Default M2M widget store just IDs of related objects. With intermediate widget additional data may be stored. Should be used with ``IntermediateManyToManyField``. The ``IntermediateManyToManyWidget`` is an advanced widget that allows you to import/export objects with related items in a Many-to-Many relationship, while also supporting additional data beyond just the relationship IDs. Unlike the default M2M widget, which only stores the IDs of related objects, the ``IntermediateManyToManyWidget`` enables you to store additional information for each related object, making it more flexible for use cases where extra attributes or fields need to be saved alongside the relationship. This widget is designed to be used with the ``IntermediateManyToManyField`` in your model. Usage: **resources.py** .. code-block:: python class ArtistResourceWithM2M(CeleryModelResource): """Artist resource with Many2Many field.""" bands = IntermediateManyToManyField( attribute="bands", column_name="Bands he played in", widget=IntermediateManyToManyWidget( rem_model=Band, rem_field="title", extra_fields=["date_joined"], instance_separator=";", ), ) class Meta: model = Artist clean_model_instances = True fields = ["id", "name", "bands", "instrument"] def get_queryset(self): """Reduce DB queries number.""" return Artist.objects.all().prefetch_related( "membership_set__band", "bands", ) **result.xlsx** +----+----------------+--------------------------------------------------+------------+ | id | name | Bands he played in | instrument | +====+================+==================================================+============+ | 1 | Rachel Schmidt | Walter-Hodges:1971-09-22;Ortiz-Hughes:2018-02-02 | 1 | +----+----------------+--------------------------------------------------+------------+ ------ Fields ------ IntermediateManyToManyField ^^^^^^^^^^^^^^^^^^^^^^^^^^^ This is resource field for M2M with custom ``through`` model. By default, ``django-import-export`` set up object attributes using ``setattr(obj, attribute_name, value)``, where ``value`` is ``QuerySet`` of related model objects. But django forbid this when ``ManyToManyField`` used with custom ``through`` model. This field expects be used with custom ``IntermediateManyToManyWidget`` widget that return not simple value, but dict with intermediate model attributes.