Skip to content
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

UI Changes; Support for lt, lte, gt, gte; and Advanced Filter Admin queryset filtering by User #25

Merged
merged 10 commits into from
Jun 7, 2016
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, I don't believe I was not aware of this issue. Do you mind adding a unit test for this method?


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