Last active
April 16, 2018 09:27
-
-
Save andreif/78a3f6b721618f43d722885b145781b0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @admin.register(models.Some) | |
| class SomeAdmin(ReadOnlyAdminMixin, admin.ModelAdmin): | |
| list_filter = ( | |
| 'related__field', | |
| input_filter('related_id'), | |
| ('other', select_filter(admin.RelatedFieldListFilter)), | |
| ('country', select_filter(admin.AllValuesFieldListFilter)), | |
| ) | |
| list_display = ( | |
| 'id', | |
| value_link('related_id', 'related_name'), | |
| value_link('other_id', 'other', lookup='other__id__exact'), | |
| value_link('country'), | |
| related_value('related__field'), | |
| ) | |
| actions = ('export_to_csv',) | |
| ordering = ('-date_created',) | |
| def export_to_csv(self, request, queryset): | |
| response = StreamingHttpResponse(api.export_to_csv(queryset), | |
| content_type='text/csv') | |
| response['Content-Disposition'] = 'attachment; filename="export.csv"' | |
| return response | |
| export_to_csv.short_description = 'Export to CSV' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import logging | |
| from datetime import datetime, timedelta | |
| from django.contrib import admin | |
| from django.utils import timezone | |
| from django.utils.html import escape | |
| from django.utils.safestring import mark_safe | |
| _log = logging.getLogger(__name__) | |
| class InputListFilter(admin.SimpleListFilter): | |
| template = 'admin/input_list_filter.html' | |
| def lookups(self, request, model_admin): | |
| v = self.value() | |
| return [(v, v)] if v else [('', '')] | |
| def queryset(self, request, queryset): | |
| v = self.value() | |
| if v: | |
| queryset = queryset.filter( | |
| **{'%s__exact' % self.parameter_name: v}) | |
| return queryset | |
| class DateRangeListFilter(admin.SimpleListFilter): | |
| template = 'admin/date_range_filter.html' | |
| is_datetime = False | |
| def has_output(self): | |
| return True | |
| def lookups(self, request, model_admin): | |
| return | |
| def value(self): | |
| fr = to = None | |
| try: | |
| val = super().value() | |
| if val: | |
| fr, _, to = val.partition(' ') | |
| except: | |
| _log.exception('Invalid date range format') | |
| return (fr, to) | |
| def get_range(self): | |
| fr, to = self.value() | |
| if fr: | |
| try: | |
| fr = datetime.strptime(fr, '%Y-%m-%d') | |
| if self.is_datetime: | |
| fr = fr.replace(tzinfo=timezone.utc) | |
| else: | |
| fr = fr.date() | |
| except Exception: | |
| _log.exception('Invalid date format') | |
| fr = None | |
| if to: | |
| try: | |
| to = datetime.strptime(to, '%Y-%m-%d') | |
| if self.is_datetime: | |
| to = to.replace(tzinfo=timezone.utc) + timedelta(days=1) | |
| else: | |
| to = to.date() | |
| except Exception: | |
| _log.exception('Invalid date format') | |
| to = None | |
| return (fr or None, to or None) | |
| def queryset(self, request, queryset): | |
| fr, to = self.get_range() | |
| if fr: | |
| queryset = queryset.filter( | |
| **{'%s__gte' % self.parameter_name: fr}) | |
| if to: | |
| lookup = '__lt' if self.is_datetime else '__lte' | |
| queryset = queryset.filter( | |
| **{(self.parameter_name + lookup): to}) | |
| return queryset | |
| def choices(self, changelist): | |
| params = changelist.params.copy() | |
| params.pop(self.parameter_name, None) | |
| fr, to = self.value() | |
| return [ | |
| {'display': fr or '', 'query_string': changelist.get_query_string( | |
| {}, [self.parameter_name]), 'params': params}, | |
| {'display': to or ''}, | |
| ] | |
| def date_range_filter(parameter, title=None, is_time=False): | |
| class DateRangeFilter(DateRangeListFilter): | |
| pass | |
| DateRangeFilter.parameter_name = parameter | |
| DateRangeFilter.title = title or parameter.replace('_', ' ') | |
| DateRangeFilter.is_datetime = is_time | |
| return DateRangeFilter | |
| def input_filter(parameter, title=None): | |
| # using class definition instead of type() so that PyCharm can find it | |
| class InputFilter(InputListFilter): | |
| pass | |
| InputFilter.parameter_name = parameter | |
| InputFilter.title = title or parameter.replace('_', ' ') | |
| return InputFilter | |
| def select_filter(cls): | |
| class SelectListFitler(cls): | |
| pass | |
| SelectListFitler.template = 'admin/select_list_filter.html' | |
| return SelectListFitler | |
| def related_value(key, title=None): | |
| keys = key.split('__') | |
| def related_value_inner(obj): | |
| for k in keys: | |
| obj = getattr(obj, k) | |
| return obj | |
| related_value_inner.admin_order_field = key | |
| related_value_inner.short_description = \ | |
| (title or keys[-1]).replace('_', ' ') | |
| return related_value_inner | |
| def value_link(key, title_key=None, lookup=None): | |
| title_key = title_key or key | |
| def value_link_inner(obj): | |
| return mark_safe( | |
| '<a href="?{}={}" style="color: inherit">{}</a>'.format( | |
| lookup or key, | |
| escape(getattr(obj, key)), | |
| escape(getattr(obj, title_key)), | |
| ) | |
| ) | |
| value_link_inner.admin_order_field = key | |
| value_link_inner.short_description = title_key.replace('_', ' ') | |
| return value_link_inner |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| {% load i18n %} | |
| <h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}</h3> | |
| <script> | |
| django.jQuery(function ($) { | |
| $('FORM.admin-filter-date-range-form INPUT').on('change', function () { | |
| $(this).parents('FORM.admin-filter-date-range-form').submit(); | |
| }); | |
| $('FORM.admin-filter-date-range-form').on('submit', function () { | |
| var form = $(this); | |
| form.find('INPUT.date-range').val( | |
| form.find('INPUT.range-from').val() + ' ' + | |
| form.find('INPUT.range-to').val() | |
| ); | |
| }); | |
| }); | |
| </script> | |
| <form action="{{ choices.0.query_string|iriencode }}" | |
| class="admin-filter-date-range-form"> | |
| <input type="hidden" name="{{ spec.parameter_name }}" value="" class="date-range"> | |
| {% for k, v in choices.0.params.items %} | |
| <input type="hidden" name="{{ k }}" value="{{ v }}"> | |
| {% endfor %} | |
| <ul class="admin-filter-date-range | |
| admin-filter-date-range-{{ spec.parameter_name }}" | |
| data-name="{{ spec.parameter_name }}" | |
| data-url="{{ choices.0.query_string|iriencode }}"> | |
| <li> | |
| <label for="">From:</label> | |
| <input type="date" style="width: 90%" value="{{ choices.0.display }}" | |
| pattern="\d{4}-\d{2}-\d{2}" | |
| placeholder="YYYY-MM-DD" class="range-from"> | |
| </li> | |
| <li> | |
| <label for="">To:</label> | |
| <input type="date" style="width: 90%" value="{{ choices.1.display }}" | |
| pattern="\d{4}-\d{2}-\d{2}" | |
| placeholder="YYYY-MM-DD" class="range-to"> | |
| </li> | |
| </ul> | |
| </form> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| {% load i18n %} | |
| <h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}</h3> | |
| <script> | |
| function add_url_param(url, name, value) { | |
| var sep = (url.slice(-1) === '?') ? '' : | |
| (url.indexOf('?') >= 0 ? '&' : '?'); | |
| return url + sep + name + '=' + encodeURI(value) | |
| } | |
| </script> | |
| <ul class="admin-filter-input-{{ spec.parameter_name }}"> | |
| <li{% if choices.0.selected %} class="selected"{% endif %}> | |
| <a href="{{ choices.0.query_string|iriencode }}" | |
| title="{{ choices.0.display }}">{{ choices.0.display }}</a> | |
| </li> | |
| <li{% if choices.1.selected %} class="selected"{% endif %}> | |
| <input id="admin-filter-input-{{ spec.parameter_name }}" | |
| type="text" style="width: 90%" | |
| data-name="{{ spec.parameter_name }}" | |
| data-url="{{ choices.0.query_string|iriencode }}" | |
| value="{{ choices.1.display }}" | |
| onchange="this.disabled = true; | |
| window.location = window.location.pathname + | |
| add_url_param(this.getAttribute('data-url'), | |
| this.getAttribute('data-name'), | |
| this.value);"> | |
| </li> | |
| </ul> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| {% load i18n %} | |
| <h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}</h3> | |
| <ul class="admin-filter-select-{{ spec.field_path }}"> | |
| <li> | |
| <select name="" id="" class="form-control" style="width: 100%" | |
| onchange="this.disabled = true; | |
| window.location = window.location.pathname + | |
| this.options[this.selectedIndex].value"> | |
| {% for choice in choices %} | |
| <option value="{{ choice.query_string|iriencode }}" | |
| {% if choice.selected %} selected="selected"{% endif %}>{{ choice.display }}</option> | |
| {% endfor %} | |
| </select> | |
| </li> | |
| </ul> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment