Export
Exporting is implemented in this project by creating an export job.
The job is represented by FileExport model, executed by export_file() celery job.
Invoking an exporter
Export is invoked by making a request to the export url. Once triggered, the view redirects to a progress view where user can view progress of the export, and a link to download a file once its procured.
Use toolbar item
The easiest way to add export to interface is by adding ExportToolbarItem to the interface
ExportToolbarItem(
gettext('Export'),
exporter=Exporter,
filters=self.filters_key,
params={
'institution_id': 1',
},
)
Link to the url
To manually construct a url to ExportView, you need to pass the following as GET parameter:
exporterpathA dotted path to the exporter class. This path must start with
export.exporters.base..filters(optional)If using a
OverviewFilterFormto filter data, pass its url hash to this argument- Additional parameters
If the export class accepts any parameters, they can be passed as additional GET arguments
url: str = reverse('export-view')
exporter: str = get_dotted_path(exporter)
export_url: str = f"{url}?exporter={exporter}&filters={filters}&institution_id={institution_id}"
Manually trigger export
Export can be triggered without using ExportView.
This allows you to add complex parameters to the export object itself.
However, the functionality needs to be re-implemented in the calling site:
Create a
FileExportinstance. Populateparameterswith all perameters required by your export class, remember to include parameters required by the base classes as well, such as audit form ids, institution slug, etc.Trigger celery job
Redirect to status page
XlsObservationExporter with whatever exporter class you’re usingdef post(request, *args, **kwargs):
exporter: FileExport = XlsObservationExporter.create_export(
user=request.user,
parameters={
# TODO: populate parameters for this export, including filters, and any custom arguments
},
)
exporter.save()
exporter.run_job()
return redirect(exporter.get_absolute_url())
Implementing new exporters
In order to implement a new type of export, you need to implement a subclass of
BaseExporter with a __call__ method.
Any export parameters will be passed as GET parameters. The export.exporters.base.BaseExporter.collect_params()
should parse those parameters into a dictionary to make it available for the export job.
export/exporters/pandas.py An example of a custom Exporter class with custom parameter and error handling 1from django.contrib.contenttypes.models import ContentType
2from django.core.files import File
3from django.core.files.base import ContentFile
4from django.db.models import Model
5from pandas import DataFrame
6
7from export.exceptions import ExportError
8from export.exporters.base import BaseExporter
9
10
11class PandasDataframeExporter(BaseExporter):
12 @classmethod
13 def collect_params(cls, request: HttpRequest) -> dict:
14 # Collect 'model' parameter from request
15 return {
16 'model': request.GET.get('model'),
17 **super().collect_params(request),
18 }
19
20 @property
21 def model(self) -> Model:
22 model: str = self.export.parameters.get('model')
23 try:
24 ct: ContentType = ContentType.objects.get(modelname=model)
25 return ct.model_class()
26 except Exception as e:
27 raise ExportError(f"'{model}' is not a valid django model") from e
28
29 def __call__(self) -> File:
30 from django_pandas.io import read_frame
31 df: DataFrame = read_frame(
32 self.model.objects.all(),
33 fieldnames=self.export.parameters.get('fieldnames', ()),
34 )
35
36 output: File = ContentFile(content="", name=f"{self.model._meta.model_name}.csv")
37 df.to_csv(output)
38 return output
The exporter can later be referenced by its classpath export.exporters.pandas.PandasDataframeExporter:
from export.exporters.pandas import PandasDataframeExporter
ExportToolbarItem(
gettext('Export all log entries as CSV'),
exporter=PandasDataframeExporter,
params={
'model': 'logentry',
},
)
Important
If your exporter requires any parameters besides the default, make sure they are handled in
export.views.export_views.ExportView.get_export_params() by reading the GET parameters and sanitizing them.