Skip to content

Commit

Permalink
Merge pull request #25 from worthwhile/develop
Browse files Browse the repository at this point in the history
UI Changes; Support for lt, lte, gt, gte; and Advanced Filter Admin queryset filtering by User
  • Loading branch information
asfaltboy committed Jun 7, 2016
2 parents 5fc8c65 + 4f928da commit d3ee9f4
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 6 deletions.
22 changes: 22 additions & 0 deletions advanced_filters/admin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging

from django.conf import settings
from django.contrib import admin, messages
from django.http import HttpResponseRedirect
from django.utils.translation import ugettext_lazy as _
Expand Down Expand Up @@ -138,5 +139,26 @@ def change_view(self, request, object_id, form_url='', extra_context=None):
return HttpResponseRedirect(url)
return orig_response

@staticmethod
def user_has_permission(user):
"""Filters by user if not superuser or explicitly allowed in settings"""
return user.is_superuser or not getattr(settings, "ADVANCED_FILTER_EDIT_BY_USER", True)

def get_queryset(self, request):
if self.user_has_permission(request.user):
return super(AdvancedFilterAdmin, self).get_queryset(request)
else:
return self.model.objects.filter_by_user(request.user)

def has_change_permission(self, request, obj=None):
if obj is None:
return super(AdvancedFilterAdmin, self).has_change_permission(request)
return self.user_has_permission(request.user) or obj in self.model.objects.filter_by_user(request.user)

def has_delete_permission(self, request, obj=None):
if obj is None:
return super(AdvancedFilterAdmin, self).has_delete_permission(request)
return self.user_has_permission(request.user) or obj in self.model.objects.filter_by_user(request.user)


admin.site.register(AdvancedFilter, AdvancedFilterAdmin)
12 changes: 9 additions & 3 deletions advanced_filters/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ class AdvancedFilterQueryForm(CleanWhiteSpacesMixin, forms.Form):
("isnull", _("Is NULL")),
("istrue", _("Is TRUE")),
("isfalse", _("Is FALSE")),
("lt", _("Less Than")),
("gt", _("Greater Than")),
("lte", _("Less Than or Equal To")),
("gte", _("Greater Than or Equal To")),
)

FIELD_CHOICES = (
Expand Down Expand Up @@ -102,8 +106,9 @@ def _parse_query_dict(query_data, model):
"""
Take a list of query field dict and return data for form initialization
"""
operator = 'iexact'
if query_data['field'] == '_OR':
query_data['operator'] = 'iexact'
query_data['operator'] = operator
return query_data

parts = query_data['field'].split('__')
Expand All @@ -112,6 +117,7 @@ def _parse_query_dict(query_data, model):
else:
if parts[-1] in dict(AdvancedFilterQueryForm.OPERATORS).keys():
field = '__'.join(parts[:-1])
operator = parts[-1]
else:
field = query_data['field']

Expand All @@ -134,7 +140,7 @@ def _parse_query_dict(query_data, model):
# this is a date/datetime field
query_data['operator'] = "range" # default
else:
query_data['operator'] = "iexact" # default
query_data['operator'] = operator # default

if isinstance(query_data.get('value'),
list) and query_data['operator'] == 'range':
Expand Down Expand Up @@ -355,7 +361,7 @@ def initialize_form(self, instance, model, data=None, extra=None):
AdvancedFilterQueryForm._parse_query_dict(
field_data, model))

formset = AFQFormSetNoExtra if extra is None else AFQFormSet
formset = AFQFormSetNoExtra if not extra else AFQFormSet
self.fields_formset = formset(
data=data,
initial=forms or None,
Expand Down
10 changes: 7 additions & 3 deletions advanced_filters/tests/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ def test_change_page_renders(self):
codename='change_advancedfilter'))
url = reverse('admin:advanced_filters_advancedfilter_change',
args=(self.a.pk,))
res = self.client.get(url)

with self.settings(ADVANCED_FILTER_EDIT_BY_USER=False):
res = self.client.get(url)
assert res.status_code == 200

def test_change_and_goto(self):
Expand All @@ -38,7 +40,8 @@ def test_change_and_goto(self):
args=(self.a.pk,))
form_data = {'form-TOTAL_FORMS': 1, 'form-INITIAL_FORMS': 0,
'_save_goto': 1}
res = self.client.post(url, data=form_data)
with self.settings(ADVANCED_FILTER_EDIT_BY_USER=False):
res = self.client.post(url, data=form_data)
assert res.status_code == 302
# django == 1.5 support
if hasattr(res, 'url'):
Expand Down Expand Up @@ -71,7 +74,8 @@ def test_changelist_includes_form(self):
self.user.user_permissions.add(Permission.objects.get(
codename='change_client'))
url = reverse('admin:customers_client_changelist')
res = self.client.get(url)
with self.settings(ADVANCED_FILTER_EDIT_BY_USER=False):
res = self.client.get(url)
assert res.status_code == 200
title = ['Create advanced filter']
fields = ['First name', 'Language', 'Sales Rep']
Expand Down
45 changes: 45 additions & 0 deletions advanced_filters/tests/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,51 @@ def test_restore_stored_complex_query(self):
assert isinstance(res, dict)
assert res == expected[i]

def test_all_operators_are_restored(self):
Rep = get_user_model()
date_range = (datetime(1980, 1, 1), datetime(1986, 1, 1))
complex_query = (Q(first_name='bar') |
(Q(first_name__iexact='fez') &
Q(is_staff=False) &
Q(date_joined__range=date_range) &
Q(is_superuser=True) &
Q(is_active=None) &
Q(is_staff=False) &
Q(last_name__icontains='foo') &
Q(last_name__lt='q') &
Q(last_name__lte='r') &
Q(last_name__gt='b') &
Q(last_name__gte='c') &
Q(email__iregex=r'^(foo|bar)$')
))
af = AdvancedFilter(query=complex_query)

expected = [
{'field': 'first_name', 'negate': False, 'operator': 'iexact', 'value': 'bar'},
{'field': '_OR', 'operator': 'iexact', 'value': 'null'},
{'field': 'first_name', 'negate': False, 'operator': 'iexact', 'value': 'fez'},
{'field': 'is_staff', 'negate': False, 'operator': 'isfalse', 'value': False},
{'field': 'date_joined',
'negate': False,
'operator': 'range',
'value': '1980-01-01,1986-01-01',
'value_from': time.mktime(date_range[0].timetuple()),
'value_to': time.mktime(date_range[1].timetuple())},
{'field': 'is_superuser', 'negate': False, 'operator': 'istrue', 'value': True},
{'field': 'is_active', 'negate': False, 'operator': 'isnull', 'value': None},
{'field': 'is_staff', 'negate': False, 'operator': 'isfalse', 'value': False},
{'field': 'last_name', 'negate': False, 'operator': 'icontains', 'value': 'foo'},
{'field': 'last_name', 'negate': False, 'operator': 'lt', 'value': 'q'},
{'field': 'last_name', 'negate': False, 'operator': 'lte', 'value': 'r'},
{'field': 'last_name', 'negate': False, 'operator': 'gt', 'value': 'b'},
{'field': 'last_name', 'negate': False, 'operator': 'gte', 'value': 'c'},
{'field': 'email', 'negate': False, 'operator': 'iregex', 'value': r'^(foo|bar)$'}
]
for i, field in enumerate(af.list_fields()):
res = AdvancedFilterQueryForm._parse_query_dict(field, Rep)
assert isinstance(res, dict)
assert res == expected[i]


class CommonFormTest(TestCase):
mgmg_form_data = {
Expand Down

0 comments on commit d3ee9f4

Please sign in to comment.