Skip to content

Add xlsx_auto_filter option #115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
8 changes: 8 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,11 @@ 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
4 changes: 2 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ def worksheet(workbook: Workbook) -> Worksheet:

@pytest.fixture
def workbook_reader() -> Callable[[Union[bytes, str]], Workbook]:
def reader_func(buffer: Union[bytes, str]) -> 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 when read_only=True
wb = workbook_reader(response.content, 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
Loading