Adding an intermediate date range to Django Import/Export

How to add a "from" and "to" date range to the Django Import/Export export form

Adding an intermediate date range to Django Import/Export
Pawel Czerwinski

Django Import/Export is an excellent library for easily getting data in-and-out of your database via the Django Admin panel. You can export individual items via the Django Admin action, or you can export the entire table via an "export" button.

Screenshot of Django admin changelist pageThe user clicks the export button in the top right to export all their data

When you have a table with a lot of data in it, there's a middle ground where you want to export subset of data based on dates, but you don't want to have to manually select that data.

In this situation, it is useful to allow the user to filter the data by date range instead:

Screenshot of Django Import Export with date range intermediate pageThe user can then filter the entire table by a 'from' and 'to' date

In this post I'll run through how you can set this up on your own project.

Overview

Django Import/Export makes it relatively easy for us to add this functionality. It already has an intermediate admin page that presents the user with a form for exporting the data.

We need to override the default form that is used on this page, including a "from" and "to" date field. We can then use these additional fields to filter the exported data by date.

Django Model

In your model, you need to make sure you have some sort of timestamp field to filter against, for example created_on:

models.py
class MyModel(models.Model):
	# ... other fields
	created_on = models.DateTimeField(auto_now_add=True)

Import/Export Resource

To export this model from the admin, you'll have a Django Import/Export resource. Here is what it will look like to begin with:

resources.py
from django import forms as django_forms
from import_export import fields, resources
from import_export import forms as import_export_forms
from .models import MyModel
 
class MyModelResource(resources.ModelResource):
	# ... other fields
	created_on = fields.Field(attribute="created_on", column_name="Date")
 
	class Meta:
        model = MyModel
        fields = ("created_on",)
 
	def get_queryset(self):
        # I always include the queryset here as you regularly have to
        # improve the performance when exporting large tables.
        return MyModel.objects.all()

Now we're going to add a few things:

  • A new form to include "from date" and "to date" fields
  • A mixin that can be added to your admin to include this new form by default
  • Logic to filter the data when exporting according to our new dates
resources.py
from django import forms as django_forms
from import_export import fields, resources
from import_export import forms as import_export_forms
from .models import MyModel
 
class ToFromDateFilterExportForm(import_export_forms.ExportForm):
	"""A Django Import/Export export form with additional fields"""
    from_date = django_forms.DateField(
        label="Date from",
        required=False,
        widget=django_forms.DateInput(attrs={"type": "date"}),
    )
    to_date = django_forms.DateField(
        label="Date to",
        required=False,
        widget=django_forms.DateInput(attrs={"type": "date"}),
    )
 
class ToFromDateFilterAdminMixin:
	"""A Django admin model mixin to include our form and logic"""
    export_form_class = ToFromDateFilterExportForm
 
    def get_export_resource_kwargs(self, request, **kwargs):
        # Filter objects by to/from date
        export_form = kwargs.get("export_form")
        if export_form:
            from_date = export_form.cleaned_data.get("from_date", None)
            to_date = export_form.cleaned_data.get("to_date", None)
            if from_date:
                kwargs.update(from_date=from_date)
            if to_date:
                kwargs.update(to_date=to_date)
        return kwargs
 
class MyModelResource(resources.ModelResource):
	# ... other fields
	created_on = fields.Field(attribute="created_on", column_name="Date")
 
	class Meta:
        model = MyModel
        fields = ("created_on",)
 
	def __init__(self, **kwargs):
        super().__init__()
        # Save the form values
        self.to_date = kwargs.get("to_date", None)
        self.from_date = kwargs.get("from_date", None)
 
    def filter_export(self, queryset, **kwargs):
        # Filter rows between the two dates
        if self.to_date:
            queryset = queryset.filter(created_on__lte=self.to_date)
        if self.from_date:
            queryset = queryset.filter(created_on__gte=self.from_date)
        return super().filter_export(queryset, **kwargs)
 
	def get_queryset(self):
        # I always include the queryset here as you regularly have to
        # improve the performance when exporting large tables.
        return MyModel.objects.all()

Django Admin

Now finally, here's our admin.py where we configure Import/Export:

admin.py
from django.contrib import admin
from import_export.admin import ExportMixin
 
from .models import MyModel
from .resources import (
    MyModelResource,
    ToFromDateFilterAdminMixin,
)
 
@admin.register(MyModel)
class MyModelAdmin(ExportActionModelAdmin, ToFromDateFilterAdminMixin, ExportMixin):
    # ... rest of admin
 
    def get_export_resource_classes(self, request):
        return (MyModelResource,)
 

Now your users can filter all of their data to a certain date range.