Skip to content

Commit 43f087d

Browse files
ortholeviatortimabbott
authored andcommitted
admin: use BitHandler instead of bitor operator
`FieldListFilter.__init__` populates `used_parameters` as follows. for p in self.expected_parameters(): if p in params: value = params.pop(p) self.used_parameters[p] = prepare_lookup_value(p, value) `FieldListFilter.expected_parameters` simply returns a singleton list containing `self.lookup_kwarg`, which is simply a copy of `field_path`. `used_parameters` therefore has at most one entry with key set to `field_path` and value from the parameters, since `prepare_lookup_value` returns the value as-is unless the key ends with either `'__in'` or `'__isnull'`. `BitHandler` "represents an array of bits, each as a `Bit` object." It is one of two classes (the other being `Bit`) that is accepted by the specialized `exact` lookup. Here, we would only like to filter out records which has all of the bits in the given integer parameter set. The lookup uses the value in the given `BitHandler` as a mask, in the form `field & mask = mask`. The keys are not used here, since they are simply used for named bit enumeration and manipulation. This is justified for the following reasons: 1. The user may supply an integer with multiple bits set which `Bit` cannot handle. 2. The class is unlikely to change to adopt a strict behavior to filter out anything not in `_keys` 3. This commit only aims to fix the bug and it would be out of the scope. Later version may make the `keys` parameter of `BitHandler optional or introduce some new wrapper object or a custom lookup for achieving this. This is a bit more efficient implementation compared to the intended one since instead of doing `field = field | mask`, it instead chooses the straightforward approach of `field & mask = mask`. SQL databases are more likely to recognize the last pattern for optimization, although they should in all way be equivalent. Plus, empty keyword arguments for `filter` essentially means a no-op, so in this case we just return `queryset` directly since they are meant to be immutable anyway. Fixes #61, #64, and #89
1 parent beadee0 commit 43f087d

File tree

1 file changed

+8
-4
lines changed

1 file changed

+8
-4
lines changed

bitfield/admin.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import six
22

3-
from django.db.models import F
43
from django.core.exceptions import ValidationError
54
from django.utils.translation import ugettext_lazy as _
65
from django.contrib.admin import FieldListFilter
76
from django.contrib.admin.options import IncorrectLookupParameters
87

9-
from bitfield import Bit
8+
from bitfield import Bit, BitHandler
109

1110

1211
class BitFieldListFilter(FieldListFilter):
@@ -23,9 +22,14 @@ def __init__(self, field, request, params, model, model_admin, field_path):
2322
field, request, params, model, model_admin, field_path)
2423

2524
def queryset(self, request, queryset):
26-
filter = dict((p, F(p).bitor(v)) for p, v in six.iteritems(self.used_parameters))
25+
filter_kwargs = dict(
26+
(p, BitHandler(v, ()))
27+
for p, v in six.iteritems(self.used_parameters)
28+
)
29+
if not filter_kwargs:
30+
return queryset
2731
try:
28-
return queryset.filter(**filter)
32+
return queryset.filter(**filter_kwargs)
2933
except ValidationError as e:
3034
raise IncorrectLookupParameters(e)
3135

0 commit comments

Comments
 (0)