Skip to content

Instantly share code, notes, and snippets.

@andreif
Last active April 16, 2018 09:27
Show Gist options
  • Select an option

  • Save andreif/78a3f6b721618f43d722885b145781b0 to your computer and use it in GitHub Desktop.

Select an option

Save andreif/78a3f6b721618f43d722885b145781b0 to your computer and use it in GitHub Desktop.
@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'
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
{% 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>
{% 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>
{% 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