Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,10 @@ By default, headers will use the same 'names' as they are returned by the API. T

Instead of using the field names, the export will use the labels as they are defined inside your Serializer. A serializer field defined as `title = serializers.CharField(label=_("Some title"))` would return `Some title` instead of `title`, also supporting translations. If no label is set, it will fall back to using `title`.

### Auto filter header fields

Filters can automatically be added to the header row by setting `xlsx_auto_filter = True`. The filter will include all header columns in the worksheet.

### Ignore fields

By default, all fields are exported, but you might want to exclude some fields from your export. To do so, you can set an array with fields you want to exclude: `xlsx_ignore_headers = [<excluded fields>]`.
Expand Down
7 changes: 7 additions & 0 deletions drf_excel/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ def render(self, data, accepted_media_type=None, renderer_context=None):
# Make column headers
column_titles = column_header.get("titles", [])

# Check for auto_filter
auto_filter = get_attribute(drf_view, "xlsx_auto_filter", False)

# If we have results, then flatten field names
if len(results):
# Set `xlsx_use_labels = True` inside the API View to enable labels.
Expand Down Expand Up @@ -216,6 +219,10 @@ def render(self, data, accepted_media_type=None, renderer_context=None):
self._make_body(body, row, row_count)
row_count += 1

# Enable auto filters if requested
if auto_filter and column_count:
self.ws.auto_filter.ref = f"A1:{get_column_letter(column_count)}{row_count}"

# Set sheet view options
# Example:
# sheet_view_options = {
Expand Down
6 changes: 3 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ def worksheet(workbook: Workbook) -> Worksheet:


@pytest.fixture
def workbook_reader() -> Callable[[Union[bytes, str]], Workbook]:
def reader_func(buffer: Union[bytes, str]) -> Workbook:
def workbook_reader() -> Callable[[Union[bytes, str], bool], Workbook]:
def reader_func(buffer: Union[bytes, str], read_only: bool = True) -> Workbook:
io_buffer = io.BytesIO(buffer)
return load_workbook(io_buffer, read_only=True)
return load_workbook(io_buffer, read_only=read_only)

return reader_func
13 changes: 13 additions & 0 deletions tests/test_viewset_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,16 @@ def test_dynamic_field_viewset(api_client, workbook_reader):

row_1_values = [cell.value for cell in data]
assert row_1_values == ["YUL", "CDG", "YYZ", "MAR"]


def test_auto_filter_viewset(api_client, workbook_reader):
ExampleModel.objects.create(title="test 1", description="This is a test")

response = api_client.get("/auto-filter/")
assert response.status_code == 200

# Note: auto_filter.ref is not available for read-only workbooks
wb = workbook_reader(response.content, read_only=False)
sheet = wb.worksheets[0]

assert sheet.auto_filter.ref == "A1:B2"
8 changes: 8 additions & 0 deletions tests/testapp/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,11 @@ def list(self, request, *args, **kwargs):
)
serializer.is_valid(raise_exception=True)
return Response(serializer.data)


class AutoFilterViewSet(XLSXFileMixin, ReadOnlyModelViewSet):
queryset = ExampleModel.objects.all()
serializer_class = ExampleSerializer
renderer_classes = (XLSXRenderer,)

xlsx_auto_filter = True
2 changes: 2 additions & 0 deletions tests/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from .testapp.views import (
AllFieldsViewSet,
AutoFilterViewSet,
DynamicFieldViewSet,
ExampleViewSet,
SecretFieldViewSet,
Expand All @@ -12,5 +13,6 @@
router.register(r"all-fields", AllFieldsViewSet)
router.register(r"secret-field", SecretFieldViewSet)
router.register(r"dynamic-field", DynamicFieldViewSet, basename="dynamic-field")
router.register(r"auto-filter", AutoFilterViewSet, basename="auto-filter")

urlpatterns = router.urls