Skip to content

Instantly share code, notes, and snippets.

@andreif
Last active April 22, 2018 20:49
Show Gist options
  • Select an option

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

Select an option

Save andreif/d00ffb67a98e9a9013934df6ab6f24fa to your computer and use it in GitHub Desktop.
"""
list_filter = (('article__product', SearchableFilter),
('article', SearchableFilter))
def lookup_allowed(self, lookup, value):
return lookup in [
'article__id__exact',
'article__product__id__exact',
] or super(LicenseAdmin, self).lookup_allowed(lookup, value)
"""
from django.contrib.admin import RelatedFieldListFilter
from django.core.urlresolvers import reverse
class SearchableFilter(RelatedFieldListFilter):
template = 'admin/searchable_filter.html'
def choices(self, cl):
ch = list(super(SearchableFilter, self).choices(cl))
return ch[:1] + list(sorted(ch[1:], key=lambda x: x['display'].strip()))
def admin_link(obj, name=None):
if isinstance(obj, str):
def _admin_link(instance):
from django.db.models import Manager
instance = getattr(instance, obj)
if isinstance(instance, Manager):
instance = instance.all()
return admin_link(instance)
_admin_link.allow_tags = True
_admin_link.short_description = str(obj).replace('_', ' ').capitalize()
return _admin_link
if hasattr(obj, '__iter__'):
return '<br>'.join(map(admin_link, obj))
if obj:
return '<a class="changelink" style="color: #334" href="%s">%s</a>' % (
reverse('admin:%s_%s_change' % (
obj._meta.app_label,
obj._meta.module_name,
), args=(
obj.pk,
)),
name or obj)
# coding=utf-8
from django.contrib import admin
from django.contrib.admin.models import LogEntry
from django.contrib.auth.decorators import user_passes_test, REDIRECT_FIELD_NAME
from django.contrib.contenttypes.admin import GenericTabularInline
from django.contrib.contenttypes.fields import GenericForeignKey
from django.core.urlresolvers import reverse
from django.db.models import Count, Q
from django.utils.translation import ugettext_lazy as _
GenericForeignKey('content_type', 'object_id')\
.contribute_to_class(LogEntry, 'content_object')
class LogEntryInline(GenericTabularInline):
model = LogEntry
fields = readonly_fields = ['action_time', 'user', 'change_message']
can_delete = False
extra = 0
def has_add_permission(self, request):
return False
def admin_url(instance):
if isinstance(instance, tuple):
if len(instance) == 2:
return reverse('admin:%s_%s_changelist' % instance)
app, model, pk = instance
else:
app, model, pk = (instance._meta.app_label,
instance._meta.model_name,
instance.pk)
url_name = 'admin:%s_%s_change' % (app, model)
return reverse(url_name, args=(pk,))
def admin_link(instance, text=None, query=None):
if callable(text):
text = text(instance)
if not text:
text = instance
if isinstance(text, tuple):
text = text[-1]
return '<a href="%s">%s</a>' % (admin_url(instance) + (query or ''), text)
class ChoiceFilter(admin.SimpleListFilter):
field_name = None
@property
def title(self):
return _(self.field_name.replace('_', ' '))
@property
def parameter_name(self):
return self.field_name + '__choice'
def get_filter_queryset(self, request, model_admin):
params = request.GET.copy()
if self.parameter_name in params:
del params[self.parameter_name]
fake_request = type('fake_request', (), {
'GET': params,
'resolver_match': request.resolver_match,
'user': request.user})
list_display = model_admin.get_list_display(request=request)
list_filter = [f for f in model_admin.get_list_filter(request=request)
if getattr(f, 'field_name', None) != self.field_name]
return model_admin.get_changelist(request=request)(
request=fake_request,
model=model_admin.model,
list_display=list_display,
list_display_links=model_admin.get_list_display_links(request=request, list_display=list_display),
list_filter=list_filter,
date_hierarchy=model_admin.date_hierarchy,
search_fields=model_admin.search_fields,
list_select_related=model_admin.list_select_related,
list_per_page=model_admin.list_per_page,
list_max_show_all=model_admin.list_max_show_all,
list_editable=model_admin.list_editable,
model_admin=model_admin,
).queryset
def get_label(self, value, count):
if value is None:
value = '(None)'
return "%s (%d)" % (value, count)
def lookups(self, request, model_admin):
f = self.field_name
ignore_attr = '__ignore_choice_filter_lookups'
if getattr(model_admin, ignore_attr, False):
return ()
setattr(model_admin, ignore_attr, True)
qs = self.get_filter_queryset(request, model_admin)
setattr(model_admin, ignore_attr, False)
null_count = qs.filter(**{f + '__isnull': True}).count()
qs = qs.filter(**{f + '__isnull': False})
ch = tuple(
(k, self.get_label(k, v))
for k, v in qs.values(f).annotate(Count(f)).order_by(f)
.values_list(f, f + '__count')
)
ch = sorted(ch, key=lambda x: x[1])
if null_count:
ch += (('__isnull', self.get_label(None, null_count)),)
return ch
def queryset(self, request, queryset):
# TODO: fix unselected bug
values = request.GET.getlist(self.parameter_name)
if not values:
return queryset
q = Q(**{self.field_name + '__isnull': True}) if '__isnull' in values else Q()
values = filter(lambda x: x != '__isnull', values)
q = q | Q(**{self.field_name + '__in': values}) if values else q
return queryset.filter(q)
def choice_filter(field_name, get_label=None, get_value=None):
attrs = {'field_name': field_name}
if get_value and not get_label:
get_label = lambda v, c: "%s (%s)" % (get_value(v), c)
if get_label:
attrs['get_label'] = staticmethod(get_label)
return type(field_name + 'ChoiceFilter', (ChoiceFilter,), attrs)
def staff_required(func=None, all_permissions=(), any_permissions=(),
login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
"""
Decorator for views that checks that the user is logged in, is active,
is staff and has permissions redirecting to the log-in page if necessary.
"""
actual_decorator = user_passes_test(
lambda u: u.is_authenticated() and u.is_active and u.is_staff and (
not all_permissions or all(map(all_permissions, u.has_perm))) and (
not any_permissions or any(map(any_permissions, u.has_perm))),
login_url=login_url,
redirect_field_name=redirect_field_name
)
if func:
return actual_decorator(func)
return actual_decorator
{% load i18n %}
<div class="searchable-filter" id="searchable-filter-{{ spec.lookup_kwarg }}">
<h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}
<span>({{ choices|length }})</span></h3>
<ul>
{% for choice in choices %}
{% if forloop.first or choice.selected %}
<li{% if choice.selected %} class="selected"{% endif %}>
<a href="{{ choice.query_string|iriencode }}">{{ choice.display }}</a></li>
{% endif %}
{% endfor %}
<li>
<input type="text" placeholder="Filter {{ title }} list...">
</li>
</ul>
<ul class="searchable-filter-list">
{% for choice in choices %}
{% if not forloop.first and not choice.selected %}
<li{% if choice.selected %} class="selected"{% endif %}>
<a href="{{ choice.query_string|iriencode }}">{{ choice.display }}</a></li>
{% endif %}
{% endfor %}
<li class="not-found" style="display: none">(nothing matches)</li>
</ul>
</div>
<style>
DIV.searchable-filter H3 SPAN {
font-weight: normal;
}
DIV.searchable-filter INPUT {
width: 137px;
margin-left: -3px;
}
DIV.searchable-filter UL.searchable-filter-list {
max-height: 300px;
overflow-y: scroll;
margin: -7px 0 0 !important;
padding: 5px 0 5px 10px !important;
box-shadow: 0 4px 10px -10px #000 inset,
0 -4px 10px -10px #000 inset;
background: #e9e9e9;
}
DIV.searchable-filter UL.searchable-filter-list LI.not-found {
padding: 10px;
color: #AAA;
}
</style>
<script>
document.querySelectorAll('DIV.searchable-filter INPUT').forEach(function(i) {
i.addEventListener('keyup', function(e) {
e = e || window.event;
var input = e.target;
var div = input.parentNode.parentNode.parentNode;
var v = input.value.trim();
var found = false;
div.querySelectorAll('UL.searchable-filter-list LI').forEach(function (li) {
if (li.classList.contains('not-found')) {
return
}
var show = v.length == 0 || li.querySelector('A').innerText.toLowerCase().indexOf(v.toLowerCase()) != -1;
li.style.display = show ? 'inherit' : 'none';
found = found || show;
});
div.querySelector('UL.searchable-filter-list LI.not-found').style.display = found ? 'none' : 'inherit';
return false
}, false);
})
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment