-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
Closes: #9583 - Add column specific search field to tables #15073
base: feature
Are you sure you want to change the base?
Changes from all commits
4c39516
e762755
f7294f7
664a0eb
cc423f5
50557c0
03f67f3
3243ebd
b54cfd6
5f69666
06c1aff
4ae6683
c3f1a96
f81f76f
0309796
84151cb
77bfd62
25a4e94
f257f4a
8a7df0b
a422a3c
8ad79a6
a9aa0cb
35cff12
1c995fa
479c69b
5830ae9
fd38255
30e6531
f130678
0ad1db9
b12ce97
77cb8ac
d6ab7b7
012e815
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ | |
from django.forms import ModelMultipleChoiceField, MultipleHiddenInput | ||
from django.http import HttpResponse | ||
from django.shortcuts import get_object_or_404, redirect, render | ||
from django.template.loader import render_to_string | ||
from django.urls import reverse | ||
from django.utils.safestring import mark_safe | ||
from django.utils.translation import gettext as _ | ||
|
@@ -170,15 +171,40 @@ def get(self, request): | |
# Render the objects table | ||
table = self.get_table(self.queryset, request, has_bulk_actions) | ||
|
||
# Check for filterset_form(s) on this view and/or the table, if a form exists: | ||
# * If both exist, initialize both | ||
# * If a filterset form for the table exists, only initialize the table filterset_form | ||
# * If a filterset form exists for the view, initialize the filterset form | ||
# * Apply to the table for use by the table and initialize a separate instance of the form for use by the table | ||
# column filters | ||
# * Otherwise set to None | ||
if self.filterset_form and table.filterset_form: | ||
filterset_form = self.filterset_form(request.GET) | ||
table.filterset_form = table.filterset_form(request.GET) | ||
elif self.filterset_form and not table.filterset_form: | ||
filterset_form = self.filterset_form(request.GET) | ||
table.filterset_form = self.filterset_form(request.GET) | ||
elif not self.filterset_form and table.filterset_form: | ||
filterset_form = None | ||
table.filterset_form = table.filterset_form(request.GET) | ||
else: | ||
filterset_form = None | ||
table.filterset_form = None | ||
|
||
# If this is an HTMX request, return only the rendered table HTML | ||
if htmx_partial(request): | ||
if request.GET.get('embedded', False): | ||
table.embedded = True | ||
# Hide selection checkboxes | ||
if 'pk' in table.base_columns: | ||
table.columns.hide('pk') | ||
filter_chits = render_to_string('inc/applied_filters_pane.html', { | ||
'model': model, | ||
'filter_form': filterset_form, | ||
}, request) | ||
Comment on lines
+201
to
+204
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO all rendering logic should go within the table itself, and should be consistent for both natural and HTMX requests. |
||
return render(request, 'htmx/table.html', { | ||
'table': table, | ||
'filter_chits': filter_chits, | ||
'model': model, | ||
'actions': actions, | ||
}) | ||
|
@@ -187,7 +213,7 @@ def get(self, request): | |
'model': model, | ||
'table': table, | ||
'actions': actions, | ||
'filter_form': self.filterset_form(request.GET) if self.filterset_form else None, | ||
'filter_form': filterset_form, | ||
'prerequisite_model': get_prerequisite_model(self.queryset), | ||
**self.get_extra_context(request), | ||
} | ||
|
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{% load helpers %} | ||
|
||
<div id="applied_filters_pane" hx-swap-oob="true"> | ||
{# Applied filters #} | ||
{% if filter_form %} | ||
{% applied_filters model filter_form request.GET %} | ||
{% endif %} | ||
</div> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{% load form_helpers %} | ||
{% if form_field %} | ||
<div class="column-filter dropdown"> | ||
<a href="#" class="dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="outside"><i class="mdi mdi-filter-settings"></i></a> | ||
<div class="dropdown-menu"> | ||
<div class="px-3 py-3"> | ||
{% render_table_filter_field form_field table=table request=request %} | ||
</div> | ||
</div> | ||
</div> | ||
{% endif %} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
{% load django_tables2 %} | ||
{% load form_helpers %} | ||
{% load i18n %} | ||
<table{% if table.attrs %} {{ table.attrs.as_html }}{% endif %} hx-disinherit="hx-target hx-select" hx-swap="outerHTML"> | ||
{% if table.show_header %} | ||
|
@@ -15,12 +16,17 @@ | |
<a href="#" | ||
hx-get="{{ table.htmx_url }}{% querystring table.prefixed_order_by_field='' %}" | ||
class="text-danger" | ||
{% if not table.embedded %}hx-push-url="true"{% endif %} | ||
title="{% trans "Clear ordering" %}" | ||
><i class="mdi mdi-close"></i></a> | ||
</div> | ||
{% endif %} | ||
{% if table.filterset_form %} | ||
{% include 'inc/table_header_filter_dropdown.html' with form_field=table.filterset_form|get_filter_field:column.name %} | ||
{% endif %} | ||
Comment on lines
+24
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we associate filter field names directly with their corresponding table columns? That would simplify things a bit. |
||
<a href="#" | ||
hx-get="{{ table.htmx_url }}{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}" | ||
hx-get="{{ table.htmx_url }}{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}" | ||
{% if not table.embedded %}hx-push-url="true"{% endif %} | ||
>{{ column.header }}</a> | ||
</th> | ||
{% else %} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,13 +4,15 @@ | |
|
||
__all__ = ( | ||
'getfield', | ||
'get_filter_field', | ||
'render_custom_fields', | ||
'render_errors', | ||
'render_field', | ||
'render_form', | ||
DanSheps marked this conversation as resolved.
Show resolved
Hide resolved
|
||
'widget_type', | ||
) | ||
|
||
from utilities.templatetags.helpers import querystring | ||
|
||
register = template.Library() | ||
|
||
|
@@ -30,6 +32,14 @@ def getfield(form, fieldname): | |
return None | ||
|
||
|
||
@register.filter() | ||
def get_filter_field(form, fieldname): | ||
# Check for a table form column map attribute and use that to map form fields if set | ||
if hasattr(form, '_table_form_column_map') and form._table_form_column_map.get(fieldname): | ||
return getfield(form, form._table_form_column_map.get(fieldname)) | ||
Comment on lines
+37
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not set on this, but also wondering if this should be a default on the form or if it should move to the Meta class There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where is |
||
return getfield(form, f'{fieldname}') or getfield(form, f'{fieldname}_id') | ||
|
||
|
||
@register.filter(name='widget_type') | ||
def widget_type(field): | ||
""" | ||
|
@@ -109,13 +119,47 @@ def render_field(field, bulk_nullable=False, label=None): | |
""" | ||
Render a single form field from template | ||
""" | ||
|
||
return { | ||
'field': field, | ||
'label': label or field.label, | ||
'bulk_nullable': bulk_nullable or getattr(field, '_nullable', False), | ||
} | ||
|
||
|
||
@register.inclusion_tag('form_helpers/render_field.html') | ||
def render_table_filter_field(field, table, request): | ||
""" | ||
Render a single form field for table column filters from template | ||
""" | ||
url = "" | ||
|
||
# Handle filter forms | ||
if table: | ||
# Build kwargs for querystring function | ||
kwargs = {field.name: None} | ||
# Build request url | ||
if request and table.htmx_url: | ||
url = table.htmx_url + querystring(request, **kwargs) | ||
elif request: | ||
url = querystring(request, **kwargs) | ||
|
||
# Set HTMX args | ||
if hasattr(field.field, 'widget'): | ||
field.field.widget.attrs.update({ | ||
'id': f'table_filter_id_{field.name}', | ||
'hx-get': url if url else '#', | ||
'hx-push-url': "true", | ||
'hx-trigger': 'hidden.bs.dropdown from:closest .dropdown' | ||
}) | ||
|
||
return { | ||
'field': field, | ||
'label': None, | ||
'bulk_nullable': False, | ||
} | ||
|
||
|
||
@register.inclusion_tag('form_helpers/render_custom_fields.html') | ||
def render_custom_fields(form): | ||
""" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we ditch the association of a filterset form on the table directly, and just use the one associated with the view? That would make things a lot cleaner.