django-bitfield-1.9.6/0000755000175000017500000000000013512212332015663 5ustar tabbotttabbott00000000000000django-bitfield-1.9.6/bitfield/0000755000175000017500000000000013512212332017445 5ustar tabbotttabbott00000000000000django-bitfield-1.9.6/bitfield/types.py0000644000175000017500000001633413512206175021202 0ustar tabbotttabbott00000000000000from __future__ import absolute_import from six import string_types def cmp(a, b): return (a > b) - (a < b) class Bit(object): """ Represents a single Bit. """ def __init__(self, number, is_set=True): self.number = number self.is_set = bool(is_set) self.mask = 2 ** int(number) self.children = [] if not self.is_set: self.mask = ~self.mask def __repr__(self): return '<%s: number=%d, is_set=%s>' % (self.__class__.__name__, self.number, self.is_set) # def __str__(self): # if self.is_set: # return 'Yes' # return 'No' def __int__(self): return self.mask def __bool__(self): return self.is_set __nonzero__ = __bool__ def __eq__(self, value): if isinstance(value, Bit): return value.number == self.number and value.is_set == self.is_set elif isinstance(value, bool): return value == self.is_set elif isinstance(value, int): return value == self.mask return value == self.is_set def __ne__(self, value): return not self == value def __coerce__(self, value): return (self.is_set, bool(value)) def __invert__(self): return self.__class__(self.number, not self.is_set) def __and__(self, value): if isinstance(value, Bit): value = value.mask return value & self.mask def __rand__(self, value): if isinstance(value, Bit): value = value.mask return self.mask & value def __or__(self, value): if isinstance(value, Bit): value = value.mask return value | self.mask def __ror__(self, value): if isinstance(value, Bit): value = value.mask return self.mask | value def __lshift__(self, value): if isinstance(value, Bit): value = value.mask return value << self.mask def __rlshift__(self, value): if isinstance(value, Bit): value = value.mask return self.mask << value def __rshift__(self, value): if isinstance(value, Bit): value = value.mask return value >> self.mask def __rrshift__(self, value): if isinstance(value, Bit): value = value.mask return self.mask >> value def __xor__(self, value): if isinstance(value, Bit): value = value.mask return value ^ self.mask def __rxor__(self, value): if isinstance(value, Bit): value = value.mask return self.mask ^ value def __sentry__(self): return repr(self) def evaluate(self, evaluator, qn, connection): return self.mask, [] def prepare(self, evaluator, query, allow_joins): return evaluator.prepare_node(self, query, allow_joins) class BitHandler(object): """ Represents an array of bits, each as a ``Bit`` object. """ def __init__(self, value, keys, labels=None): # TODO: change to bitarray? if value: self._value = int(value) else: self._value = 0 self._keys = keys self._labels = labels is not None and labels or keys def __eq__(self, other): if not isinstance(other, BitHandler): return False return self._value == other._value def __lt__(self, other): return int(self._value) < other def __le__(self, other): return int(self._value) <= other def __gt__(self, other): return int(self._value) > other def __ge__(self, other): return int(self._value) >= other def __cmp__(self, other): return cmp(self._value, other) def __repr__(self): return '<%s: %s>' % (self.__class__.__name__, ', '.join('%s=%s' % (k, self.get_bit(n).is_set) for n, k in enumerate(self._keys)),) def __str__(self): return str(self._value) def __int__(self): return self._value def __bool__(self): return bool(self._value) __nonzero__ = __bool__ def __and__(self, value): return BitHandler(self._value & int(value), self._keys) def __or__(self, value): return BitHandler(self._value | int(value), self._keys) def __add__(self, value): return BitHandler(self._value + int(value), self._keys) def __sub__(self, value): return BitHandler(self._value - int(value), self._keys) def __lshift__(self, value): return BitHandler(self._value << int(value), self._keys) def __rshift__(self, value): return BitHandler(self._value >> int(value), self._keys) def __xor__(self, value): return BitHandler(self._value ^ int(value), self._keys) def __contains__(self, key): bit_number = self._keys.index(key) return bool(self.get_bit(bit_number)) def __getattr__(self, key): if key.startswith('_'): return object.__getattribute__(self, key) if key not in self._keys: raise AttributeError('%s is not a valid flag' % key) return self.get_bit(self._keys.index(key)) def __setattr__(self, key, value): if key.startswith('_'): return object.__setattr__(self, key, value) if key not in self._keys: raise AttributeError('%s is not a valid flag' % key) self.set_bit(self._keys.index(key), value) def __iter__(self): return self.iteritems() def __sentry__(self): return repr(self) def _get_mask(self): return self._value mask = property(_get_mask) def evaluate(self, evaluator, qn, connection): return self.mask, [] def get_bit(self, bit_number): mask = 2 ** int(bit_number) return Bit(bit_number, self._value & mask != 0) def set_bit(self, bit_number, true_or_false): mask = 2 ** int(bit_number) if true_or_false: self._value |= mask else: self._value &= (~mask) return Bit(bit_number, self._value & mask != 0) def keys(self): return self._keys def iterkeys(self): return iter(self._keys) def items(self): return list(self.iteritems()) def iteritems(self): for k in self._keys: yield (k, getattr(self, k).is_set) def get_label(self, flag): if isinstance(flag, string_types): flag = self._keys.index(flag) if isinstance(flag, Bit): flag = flag.number return self._labels[flag] import django if django.VERSION[:2] >= (1, 8): from django.core.exceptions import ImproperlyConfigured # We need to register adapters in Django 1.8 in order to prevent # "ProgrammingError: can't adapt type" try: from django.db.backends.sqlite3.base import Database Database.register_adapter(Bit, lambda x: int(x)) Database.register_adapter(BitHandler, lambda x: int(x)) except ImproperlyConfigured: pass try: from django.db.backends.postgresql_psycopg2.base import Database Database.extensions.register_adapter(Bit, lambda x: Database.extensions.AsIs(int(x))) Database.extensions.register_adapter(BitHandler, lambda x: Database.extensions.AsIs(int(x))) except ImproperlyConfigured: pass django-bitfield-1.9.6/bitfield/query.py0000644000175000017500000000451213512206175021176 0ustar tabbotttabbott00000000000000from __future__ import absolute_import from bitfield.types import Bit, BitHandler class BitQueryLookupWrapper(object): def __init__(self, alias, column, bit): self.table_alias = alias self.column = column self.bit = bit def as_sql(self, qn, connection=None): """ Create the proper SQL fragment. This inserts something like "(T0.flags & value) != 0". This will be called by Where.as_sql() """ if self.bit: return ("(%s.%s | %d)" % (qn(self.table_alias), qn(self.column), self.bit.mask), []) return ("(%s.%s & %d)" % (qn(self.table_alias), qn(self.column), self.bit.mask), []) try: # Django 1.7+ from django.db.models.lookups import Exact class BitQueryLookupWrapper(Exact): # NOQA def process_lhs(self, qn, connection, lhs=None): lhs_sql, params = super(BitQueryLookupWrapper, self).process_lhs( qn, connection, lhs) if self.rhs: lhs_sql = lhs_sql + ' & %s' else: lhs_sql = lhs_sql + ' | %s' params.extend(self.get_db_prep_lookup(self.rhs, connection)[1]) return lhs_sql, params def get_db_prep_lookup(self, value, connection, prepared=False): v = value.mask if isinstance(value, (BitHandler, Bit)) else value return super(BitQueryLookupWrapper, self).get_db_prep_lookup(v, connection) def get_prep_lookup(self): return self.rhs except ImportError: pass class BitQuerySaveWrapper(BitQueryLookupWrapper): def as_sql(self, qn, connection): """ Create the proper SQL fragment. This inserts something like "(T0.flags & value) != 0". This will be called by Where.as_sql() """ engine = connection.settings_dict['ENGINE'].rsplit('.', -1)[-1] if engine.startswith('postgres'): XOR_OPERATOR = '#' elif engine.startswith('sqlite'): raise NotImplementedError else: XOR_OPERATOR = '^' if self.bit: return ("%s.%s | %d" % (qn(self.table_alias), qn(self.column), self.bit.mask), []) return ("%s.%s %s %d" % (qn(self.table_alias), qn(self.column), XOR_OPERATOR, self.bit.mask), []) django-bitfield-1.9.6/bitfield/compat.py0000644000175000017500000000072713512206175021320 0ustar tabbotttabbott00000000000000from __future__ import absolute_import __all__ = ('bitand', 'bitor') def bitand(a, b): return a.bitand(b) def bitor(a, b): return a.bitor(b) try: from django.db.models.expressions import ExpressionNode ExpressionNode.BITAND # noqa del ExpressionNode except ImportError: # Django >= 1.8 pass except AttributeError: # Django < 1.5 def bitand(a, b): # NOQA return a & b def bitor(a, b): # NOQA return a | b django-bitfield-1.9.6/bitfield/__init__.py0000644000175000017500000000035413512212277021570 0ustar tabbotttabbott00000000000000""" django-bitfield ~~~~~~~~~~~~~~~ """ from __future__ import absolute_import from bitfield.models import Bit, BitHandler, CompositeBitField, BitField # NOQA default_app_config = 'bitfield.apps.BitFieldAppConfig' VERSION = '1.9.6' django-bitfield-1.9.6/bitfield/models.py0000644000175000017500000002164613512206175021323 0ustar tabbotttabbott00000000000000from __future__ import absolute_import import six from django.db.models import signals from django.db.models.fields import Field, BigIntegerField from bitfield.forms import BitFormField from bitfield.query import BitQueryLookupWrapper from bitfield.types import BitHandler, Bit # Count binary capacity. Truncate "0b" prefix from binary form. # Twice faster than bin(i)[2:] or math.floor(math.log(i)) MAX_FLAG_COUNT = int(len(bin(BigIntegerField.MAX_BIGINT)) - 2) class BitFieldFlags(object): def __init__(self, flags): if len(flags) > MAX_FLAG_COUNT: raise ValueError('Too many flags') self._flags = flags def __repr__(self): return repr(self._flags) def __iter__(self): for flag in self._flags: yield flag def __getattr__(self, key): if key not in self._flags: raise AttributeError("flag {} is not registered".format(key)) return Bit(self._flags.index(key)) def iteritems(self): for flag in self._flags: yield flag, Bit(self._flags.index(flag)) def iterkeys(self): for flag in self._flags: yield flag def itervalues(self): for flag in self._flags: yield Bit(self._flags.index(flag)) def items(self): return list(self.iteritems()) def keys(self): return list(self.iterkeys()) def values(self): return list(self.itervalues()) class BitFieldCreator(object): """ A placeholder class that provides a way to set the attribute on the model. Descriptor for BitFields. Checks to make sure that all flags of the instance match the class. This is to handle the case when caching an older version of the instance and a newer version of the class is available (usually during deploys). """ def __init__(self, field): self.field = field def __set__(self, obj, value): obj.__dict__[self.field.name] = self.field.to_python(value) def __get__(self, obj, type=None): if obj is None: return BitFieldFlags(self.field.flags) retval = obj.__dict__[self.field.name] if self.field.__class__ is BitField: # Update flags from class in case they've changed. retval._keys = self.field.flags return retval class BitField(BigIntegerField): def contribute_to_class(self, cls, name, **kwargs): super(BitField, self).contribute_to_class(cls, name, **kwargs) setattr(cls, self.name, BitFieldCreator(self)) def __init__(self, flags, default=None, *args, **kwargs): if isinstance(flags, dict): # Get only integer keys in correct range valid_keys = (k for k in flags.keys() if isinstance(k, int) and (0 <= k < MAX_FLAG_COUNT)) if not valid_keys: raise ValueError('Wrong keys or empty dictionary') # Fill list with values from dict or with empty values flags = [flags.get(i, '') for i in range(max(valid_keys) + 1)] if len(flags) > MAX_FLAG_COUNT: raise ValueError('Too many flags') self._arg_flags = flags flags = list(flags) labels = [] for num, flag in enumerate(flags): if isinstance(flag, (tuple, list)): flags[num] = flag[0] labels.append(flag[1]) else: labels.append(flag) if isinstance(default, (list, tuple, set, frozenset)): new_value = 0 for flag in default: new_value |= Bit(flags.index(flag)) default = new_value BigIntegerField.__init__(self, default=default, *args, **kwargs) self.flags = flags self.labels = labels def south_field_triple(self): "Returns a suitable description of this field for South." from south.modelsinspector import introspector field_class = "django.db.models.fields.BigIntegerField" args, kwargs = introspector(self) return (field_class, args, kwargs) def formfield(self, form_class=BitFormField, **kwargs): choices = [(k, self.labels[self.flags.index(k)]) for k in self.flags] return Field.formfield(self, form_class, choices=choices, **kwargs) def pre_save(self, instance, add): value = getattr(instance, self.attname) return value def get_prep_value(self, value): if value is None: return None if isinstance(value, (BitHandler, Bit)): value = value.mask return int(value) # def get_db_prep_save(self, value, connection): # if isinstance(value, Bit): # return BitQuerySaveWrapper(self.model._meta.db_table, self.name, value) # return super(BitField, self).get_db_prep_save(value, connection=connection) def get_db_prep_lookup(self, lookup_type, value, connection, prepared=False): if isinstance(getattr(value, 'expression', None), Bit): value = value.expression if isinstance(value, (BitHandler, Bit)): if hasattr(self, 'class_lookups'): # Django 1.7+ return [value.mask] else: return BitQueryLookupWrapper(self.model._meta.db_table, self.db_column or self.name, value) return BigIntegerField.get_db_prep_lookup(self, lookup_type=lookup_type, value=value, connection=connection, prepared=prepared) def get_prep_lookup(self, lookup_type, value): if isinstance(getattr(value, 'expression', None), Bit): value = value.expression if isinstance(value, Bit): if lookup_type in ('exact',): return value raise TypeError('Lookup type %r not supported with `Bit` type.' % lookup_type) return BigIntegerField.get_prep_lookup(self, lookup_type, value) def to_python(self, value): if isinstance(value, Bit): value = value.mask if not isinstance(value, BitHandler): # Regression for #1425: fix bad data that was created resulting # in negative values for flags. Compute the value that would # have been visible ot the application to preserve compatibility. if isinstance(value, six.integer_types) and value < 0: new_value = 0 for bit_number, _ in enumerate(self.flags): new_value |= (value & (2 ** bit_number)) value = new_value value = BitHandler(value, self.flags, self.labels) else: # Ensure flags are consistent for unpickling value._keys = self.flags return value def deconstruct(self): name, path, args, kwargs = super(BitField, self).deconstruct() args.insert(0, self._arg_flags) return name, path, args, kwargs try: BitField.register_lookup(BitQueryLookupWrapper) except AttributeError: pass class CompositeBitFieldWrapper(object): def __init__(self, fields): self.fields = fields def __getattr__(self, attr): if attr == 'fields': return super(CompositeBitFieldWrapper, self).__getattr__(attr) for field in self.fields: if hasattr(field, attr): return getattr(field, attr) raise AttributeError('%s is not a valid flag' % attr) def __hasattr__(self, attr): if attr == 'fields': return super(CompositeBitFieldWrapper, self).__hasattr__(attr) for field in self.fields: if hasattr(field, attr): return True return False def __setattr__(self, attr, value): if attr == 'fields': super(CompositeBitFieldWrapper, self).__setattr__(attr, value) return for field in self.fields: if hasattr(field, attr): setattr(field, attr, value) return raise AttributeError('%s is not a valid flag' % attr) class CompositeBitField(object): is_relation = False many_to_many = False concrete = False def __init__(self, fields): self.fields = fields def contribute_to_class(self, cls, name): self.name = name self.model = cls cls._meta.virtual_fields.append(self) signals.class_prepared.connect(self.validate_fields, sender=cls) setattr(cls, name, self) def validate_fields(self, sender, **kwargs): cls = sender model_fields = dict([ (f.name, f) for f in cls._meta.fields if f.name in self.fields]) all_flags = sum([model_fields[f].flags for f in self.fields], []) if len(all_flags) != len(set(all_flags)): raise ValueError('BitField flags must be unique.') def __get__(self, instance, instance_type=None): fields = [getattr(instance, f) for f in self.fields] return CompositeBitFieldWrapper(fields) def __set__(self, *args, **kwargs): raise NotImplementedError('CompositeBitField cannot be set.') django-bitfield-1.9.6/bitfield/admin.py0000644000175000017500000000317113512206175021121 0ustar tabbotttabbott00000000000000import six from django.db.models import F from django.core.exceptions import ValidationError from django.utils.translation import ugettext_lazy as _ from django.contrib.admin import FieldListFilter from django.contrib.admin.options import IncorrectLookupParameters from bitfield import Bit from bitfield.compat import bitor class BitFieldListFilter(FieldListFilter): """ BitField list filter. """ def __init__(self, field, request, params, model, model_admin, field_path): self.lookup_kwarg = field_path self.lookup_val = int(request.GET.get(self.lookup_kwarg, 0)) self.flags = field.flags self.labels = field.labels super(BitFieldListFilter, self).__init__( field, request, params, model, model_admin, field_path) def queryset(self, request, queryset): filter = dict((p, bitor(F(p), v)) for p, v in six.iteritems(self.used_parameters)) try: return queryset.filter(**filter) except ValidationError as e: raise IncorrectLookupParameters(e) def expected_parameters(self): return [self.lookup_kwarg] def choices(self, cl): yield { 'selected': self.lookup_val == 0, 'query_string': cl.get_query_string({}, [self.lookup_kwarg]), 'display': _('All'), } for number, flag in enumerate(self.flags): bit_mask = Bit(number).mask yield { 'selected': self.lookup_val == bit_mask, 'query_string': cl.get_query_string({self.lookup_kwarg: bit_mask}), 'display': self.labels[number], } django-bitfield-1.9.6/bitfield/tests/0000755000175000017500000000000013512212332020607 5ustar tabbotttabbott00000000000000django-bitfield-1.9.6/bitfield/tests/tests.py0000644000175000017500000003731313512206175022342 0ustar tabbotttabbott00000000000000from __future__ import absolute_import import pickle from django.db import connection, models from django.db.models import F from django.test import TestCase from bitfield import BitHandler, Bit, BitField from bitfield.compat import bitand, bitor from .forms import BitFieldTestModelForm from .models import BitFieldTestModel, CompositeBitFieldTestModel class BitHandlerTest(TestCase): def test_comparison(self): bithandler_1 = BitHandler(0, ('FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3')) bithandler_2 = BitHandler(1, ('FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3')) bithandler_3 = BitHandler(0, ('FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3')) assert bithandler_1 == bithandler_1 assert bithandler_1 != bithandler_2 assert bithandler_1 == bithandler_3 def test_defaults(self): bithandler = BitHandler(0, ('FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3')) # Default value of 0. self.assertEquals(int(bithandler), 0) # Test bit numbers. self.assertEquals(int(bithandler.FLAG_0.number), 0) self.assertEquals(int(bithandler.FLAG_1.number), 1) self.assertEquals(int(bithandler.FLAG_2.number), 2) self.assertEquals(int(bithandler.FLAG_3.number), 3) # Negative test non-existant key. self.assertRaises(AttributeError, lambda: bithandler.FLAG_4) # Test bool(). self.assertEquals(bool(bithandler.FLAG_0), False) self.assertEquals(bool(bithandler.FLAG_1), False) self.assertEquals(bool(bithandler.FLAG_2), False) self.assertEquals(bool(bithandler.FLAG_3), False) def test_nonzero_default(self): bithandler = BitHandler(1, ('FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3')) self.assertEquals(bool(bithandler.FLAG_0), True) self.assertEquals(bool(bithandler.FLAG_1), False) self.assertEquals(bool(bithandler.FLAG_2), False) self.assertEquals(bool(bithandler.FLAG_3), False) bithandler = BitHandler(2, ('FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3')) self.assertEquals(bool(bithandler.FLAG_0), False) self.assertEquals(bool(bithandler.FLAG_1), True) self.assertEquals(bool(bithandler.FLAG_2), False) self.assertEquals(bool(bithandler.FLAG_3), False) bithandler = BitHandler(3, ('FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3')) self.assertEquals(bool(bithandler.FLAG_0), True) self.assertEquals(bool(bithandler.FLAG_1), True) self.assertEquals(bool(bithandler.FLAG_2), False) self.assertEquals(bool(bithandler.FLAG_3), False) bithandler = BitHandler(4, ('FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3')) self.assertEquals(bool(bithandler.FLAG_0), False) self.assertEquals(bool(bithandler.FLAG_1), False) self.assertEquals(bool(bithandler.FLAG_2), True) self.assertEquals(bool(bithandler.FLAG_3), False) def test_mutation(self): bithandler = BitHandler(0, ('FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3')) self.assertEquals(bool(bithandler.FLAG_0), False) self.assertEquals(bool(bithandler.FLAG_1), False) self.assertEquals(bool(bithandler.FLAG_2), False) self.assertEquals(bool(bithandler.FLAG_3), False) bithandler = BitHandler(bithandler | 1, bithandler._keys) self.assertEquals(bool(bithandler.FLAG_0), True) self.assertEquals(bool(bithandler.FLAG_1), False) self.assertEquals(bool(bithandler.FLAG_2), False) self.assertEquals(bool(bithandler.FLAG_3), False) bithandler ^= 3 self.assertEquals(int(bithandler), 2) self.assertEquals(bool(bithandler & 1), False) bithandler.FLAG_0 = False self.assertEquals(bithandler.FLAG_0, False) bithandler.FLAG_1 = True self.assertEquals(bithandler.FLAG_0, False) self.assertEquals(bithandler.FLAG_1, True) bithandler.FLAG_2 = False self.assertEquals(bithandler.FLAG_0, False) self.assertEquals(bithandler.FLAG_1, True) self.assertEquals(bithandler.FLAG_2, False) class BitTest(TestCase): def test_int(self): bit = Bit(0) self.assertEquals(int(bit), 1) self.assertEquals(bool(bit), True) self.assertFalse(not bit) def test_comparison(self): self.assertEquals(Bit(0), Bit(0)) self.assertNotEquals(Bit(1), Bit(0)) self.assertNotEquals(Bit(0, 0), Bit(0, 1)) self.assertEquals(Bit(0, 1), Bit(0, 1)) self.assertEquals(Bit(0), 1) def test_and(self): self.assertEquals(1 & Bit(2), 0) self.assertEquals(1 & Bit(0), 1) self.assertEquals(1 & ~Bit(0), 0) self.assertEquals(Bit(0) & Bit(2), 0) self.assertEquals(Bit(0) & Bit(0), 1) self.assertEquals(Bit(0) & ~Bit(0), 0) def test_or(self): self.assertEquals(1 | Bit(2), 5) self.assertEquals(1 | Bit(5), 33) self.assertEquals(1 | ~Bit(2), -5) self.assertEquals(Bit(0) | Bit(2), 5) self.assertEquals(Bit(0) | Bit(5), 33) self.assertEquals(Bit(0) | ~Bit(2), -5) def test_xor(self): self.assertEquals(1 ^ Bit(2), 5) self.assertEquals(1 ^ Bit(0), 0) self.assertEquals(1 ^ Bit(1), 3) self.assertEquals(1 ^ Bit(5), 33) self.assertEquals(1 ^ ~Bit(2), -6) self.assertEquals(Bit(0) ^ Bit(2), 5) self.assertEquals(Bit(0) ^ Bit(0), 0) self.assertEquals(Bit(0) ^ Bit(1), 3) self.assertEquals(Bit(0) ^ Bit(5), 33) self.assertEquals(Bit(0) ^ ~Bit(2), -6) class BitFieldTest(TestCase): def test_basic(self): # Create instance and make sure flags are working properly. instance = BitFieldTestModel.objects.create(flags=1) self.assertTrue(instance.flags.FLAG_0) self.assertFalse(instance.flags.FLAG_1) self.assertFalse(instance.flags.FLAG_2) self.assertFalse(instance.flags.FLAG_3) def test_regression_1425(self): # Creating new instances shouldn't allow negative values. instance = BitFieldTestModel.objects.create(flags=-1) self.assertEqual(instance.flags._value, 15) self.assertTrue(instance.flags.FLAG_0) self.assertTrue(instance.flags.FLAG_1) self.assertTrue(instance.flags.FLAG_2) self.assertTrue(instance.flags.FLAG_3) cursor = connection.cursor() flags_field = BitFieldTestModel._meta.get_field('flags') flags_db_column = flags_field.db_column or flags_field.name cursor.execute("INSERT INTO %s (%s) VALUES (-1)" % (BitFieldTestModel._meta.db_table, flags_db_column)) # There should only be the one row we inserted through the cursor. instance = BitFieldTestModel.objects.get(flags=-1) self.assertTrue(instance.flags.FLAG_0) self.assertTrue(instance.flags.FLAG_1) self.assertTrue(instance.flags.FLAG_2) self.assertTrue(instance.flags.FLAG_3) instance.save() self.assertEqual(BitFieldTestModel.objects.filter(flags=15).count(), 2) self.assertEqual(BitFieldTestModel.objects.filter(flags__lt=0).count(), 0) def test_select(self): BitFieldTestModel.objects.create(flags=3) self.assertTrue(BitFieldTestModel.objects.filter(flags=BitFieldTestModel.flags.FLAG_1).exists()) self.assertTrue(BitFieldTestModel.objects.filter(flags=BitFieldTestModel.flags.FLAG_0).exists()) self.assertFalse(BitFieldTestModel.objects.exclude(flags=BitFieldTestModel.flags.FLAG_0).exists()) self.assertFalse(BitFieldTestModel.objects.exclude(flags=BitFieldTestModel.flags.FLAG_1).exists()) def test_update(self): instance = BitFieldTestModel.objects.create(flags=0) self.assertFalse(instance.flags.FLAG_0) BitFieldTestModel.objects.filter(pk=instance.pk).update(flags=bitor(F('flags'), BitFieldTestModel.flags.FLAG_1)) instance = BitFieldTestModel.objects.get(pk=instance.pk) self.assertTrue(instance.flags.FLAG_1) BitFieldTestModel.objects.filter(pk=instance.pk).update(flags=bitor(F('flags'), ((~BitFieldTestModel.flags.FLAG_0 | BitFieldTestModel.flags.FLAG_3)))) instance = BitFieldTestModel.objects.get(pk=instance.pk) self.assertFalse(instance.flags.FLAG_0) self.assertTrue(instance.flags.FLAG_1) self.assertTrue(instance.flags.FLAG_3) self.assertFalse(BitFieldTestModel.objects.filter(flags=BitFieldTestModel.flags.FLAG_0).exists()) BitFieldTestModel.objects.filter(pk=instance.pk).update(flags=bitand(F('flags'), ~BitFieldTestModel.flags.FLAG_3)) instance = BitFieldTestModel.objects.get(pk=instance.pk) self.assertFalse(instance.flags.FLAG_0) self.assertTrue(instance.flags.FLAG_1) self.assertFalse(instance.flags.FLAG_3) def test_update_with_handler(self): instance = BitFieldTestModel.objects.create(flags=0) self.assertFalse(instance.flags.FLAG_0) instance.flags.FLAG_1 = True BitFieldTestModel.objects.filter(pk=instance.pk).update(flags=bitor(F('flags'), instance.flags)) instance = BitFieldTestModel.objects.get(pk=instance.pk) self.assertTrue(instance.flags.FLAG_1) def test_negate(self): BitFieldTestModel.objects.create(flags=BitFieldTestModel.flags.FLAG_0 | BitFieldTestModel.flags.FLAG_1) BitFieldTestModel.objects.create(flags=BitFieldTestModel.flags.FLAG_1) self.assertEqual(BitFieldTestModel.objects.filter(flags=~BitFieldTestModel.flags.FLAG_0).count(), 1) self.assertEqual(BitFieldTestModel.objects.filter(flags=~BitFieldTestModel.flags.FLAG_1).count(), 0) self.assertEqual(BitFieldTestModel.objects.filter(flags=~BitFieldTestModel.flags.FLAG_2).count(), 2) def test_default_value(self): instance = BitFieldTestModel.objects.create() self.assertTrue(instance.flags.FLAG_0) self.assertTrue(instance.flags.FLAG_1) self.assertFalse(instance.flags.FLAG_2) self.assertFalse(instance.flags.FLAG_3) def test_binary_capacity(self): import math from django.db.models.fields import BigIntegerField # Local maximum value, slow canonical algorithm MAX_COUNT = int(math.floor(math.log(BigIntegerField.MAX_BIGINT, 2))) # Big flags list flags = ['f' + str(i) for i in range(100)] try: BitField(flags=flags[:MAX_COUNT]) except ValueError: self.fail("It should work well with these flags") self.assertRaises(ValueError, BitField, flags=flags[:(MAX_COUNT + 1)]) def test_dictionary_init(self): flags = { 0: 'zero', 1: 'first', 10: 'tenth', 2: 'second', 'wrongkey': 'wrongkey', 100: 'bigkey', -100: 'smallkey', } try: bf = BitField(flags) except ValueError: self.fail("It should work well with these flags") self.assertEquals(bf.flags, ['zero', 'first', 'second', '', '', '', '', '', '', '', 'tenth']) self.assertRaises(ValueError, BitField, flags={}) self.assertRaises(ValueError, BitField, flags={'wrongkey': 'wrongkey'}) self.assertRaises(ValueError, BitField, flags={'1': 'non_int_key'}) def test_defaults_as_key_names(self): class TestModel(models.Model): flags = BitField(flags=( 'FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3', ), default=('FLAG_1', 'FLAG_2')) field = TestModel._meta.get_field('flags') self.assertEquals(field.default, TestModel.flags.FLAG_1 | TestModel.flags.FLAG_2) class BitFieldSerializationTest(TestCase): def test_can_unserialize_bithandler(self): bf = BitFieldTestModel() bf.flags.FLAG_0 = 1 bf.flags.FLAG_1 = 0 data = pickle.dumps(bf) inst = pickle.loads(data) self.assertTrue(inst.flags.FLAG_0) self.assertFalse(inst.flags.FLAG_1) def test_pickle_integration(self): inst = BitFieldTestModel.objects.create(flags=1) data = pickle.dumps(inst) inst = pickle.loads(data) self.assertEquals(type(inst.flags), BitHandler) self.assertEquals(int(inst.flags), 1) def test_added_field(self): bf = BitFieldTestModel() bf.flags.FLAG_0 = 1 bf.flags.FLAG_1 = 0 bf.flags.FLAG_3 = 0 data = pickle.dumps(bf) inst = pickle.loads(data) self.assertTrue('FLAG_3' in inst.flags.keys()) class CompositeBitFieldTest(TestCase): def test_get_flag(self): inst = CompositeBitFieldTestModel() self.assertEqual(inst.flags.FLAG_0, inst.flags_1.FLAG_0) self.assertEqual(inst.flags.FLAG_4, inst.flags_2.FLAG_4) self.assertRaises(AttributeError, lambda: inst.flags.flag_NA) def test_set_flag(self): inst = CompositeBitFieldTestModel() flag_0_original = bool(inst.flags.FLAG_0) self.assertEqual(bool(inst.flags_1.FLAG_0), flag_0_original) flag_4_original = bool(inst.flags.FLAG_4) self.assertEqual(bool(inst.flags_2.FLAG_4), flag_4_original) # flip flags' bits inst.flags.FLAG_0 = not flag_0_original inst.flags.FLAG_4 = not flag_4_original # check to make sure the bit flips took effect self.assertNotEqual(bool(inst.flags.FLAG_0), flag_0_original) self.assertNotEqual(bool(inst.flags_1.FLAG_0), flag_0_original) self.assertNotEqual(bool(inst.flags.FLAG_4), flag_4_original) self.assertNotEqual(bool(inst.flags_2.FLAG_4), flag_4_original) def set_flag(): inst.flags.flag_NA = False self.assertRaises(AttributeError, set_flag) def test_hasattr(self): inst = CompositeBitFieldTestModel() self.assertEqual(hasattr(inst.flags, 'flag_0'), hasattr(inst.flags_1, 'flag_0')) self.assertEqual(hasattr(inst.flags, 'flag_4'), hasattr(inst.flags_2, 'flag_4')) class BitFormFieldTest(TestCase): def test_form_new_invalid(self): invalid_data_dicts = [ {'flags': ['FLAG_0', 'FLAG_FLAG']}, {'flags': ['FLAG_4']}, {'flags': [1, 2]} ] for invalid_data in invalid_data_dicts: form = BitFieldTestModelForm(data=invalid_data) self.assertFalse(form.is_valid()) def test_form_new(self): data_dicts = [ {'flags': ['FLAG_0', 'FLAG_1']}, {'flags': ['FLAG_3']}, {'flags': []}, {} ] for data in data_dicts: form = BitFieldTestModelForm(data=data) self.failUnless(form.is_valid()) instance = form.save() flags = data['flags'] if 'flags' in data else [] for k in BitFieldTestModel.flags: self.assertEquals(bool(getattr(instance.flags, k)), k in flags) def test_form_update(self): instance = BitFieldTestModel.objects.create(flags=0) for k in BitFieldTestModel.flags: self.assertFalse(bool(getattr(instance.flags, k))) data = {'flags': ['FLAG_0', 'FLAG_1']} form = BitFieldTestModelForm(data=data, instance=instance) self.failUnless(form.is_valid()) instance = form.save() for k in BitFieldTestModel.flags: self.assertEquals(bool(getattr(instance.flags, k)), k in data['flags']) data = {'flags': ['FLAG_2', 'FLAG_3']} form = BitFieldTestModelForm(data=data, instance=instance) self.failUnless(form.is_valid()) instance = form.save() for k in BitFieldTestModel.flags: self.assertEquals(bool(getattr(instance.flags, k)), k in data['flags']) data = {'flags': []} form = BitFieldTestModelForm(data=data, instance=instance) self.failUnless(form.is_valid()) instance = form.save() for k in BitFieldTestModel.flags: self.assertFalse(bool(getattr(instance.flags, k))) django-bitfield-1.9.6/bitfield/tests/__init__.py0000644000175000017500000000000013512206175022716 0ustar tabbotttabbott00000000000000django-bitfield-1.9.6/bitfield/tests/models.py0000644000175000017500000000117213512206175022455 0ustar tabbotttabbott00000000000000from django.db import models from bitfield import BitField, CompositeBitField class BitFieldTestModel(models.Model): flags = BitField(flags=( 'FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3', ), default=3, db_column='another_name') class CompositeBitFieldTestModel(models.Model): flags_1 = BitField(flags=( 'FLAG_0', 'FLAG_1', 'FLAG_2', 'FLAG_3', ), default=0) flags_2 = BitField(flags=( 'FLAG_4', 'FLAG_5', 'FLAG_6', 'FLAG_7', ), default=0) flags = CompositeBitField(( 'flags_1', 'flags_2', )) django-bitfield-1.9.6/bitfield/tests/forms.py0000644000175000017500000000036213512206175022320 0ustar tabbotttabbott00000000000000from __future__ import absolute_import from django import forms from bitfield.tests.models import BitFieldTestModel class BitFieldTestModelForm(forms.ModelForm): class Meta: model = BitFieldTestModel exclude = tuple() django-bitfield-1.9.6/bitfield/forms.py0000644000175000017500000000437013512206175021161 0ustar tabbotttabbott00000000000000from __future__ import absolute_import from django.forms import CheckboxSelectMultiple, IntegerField, ValidationError try: from django.utils.encoding import force_text except ImportError: from django.utils.encoding import force_unicode as force_text from bitfield.types import BitHandler class BitFieldCheckboxSelectMultiple(CheckboxSelectMultiple): def render(self, name, value, attrs=None, choices=(), renderer=None): if isinstance(value, BitHandler): value = [k for k, v in value if v] elif isinstance(value, int): real_value = [] div = 2 for (k, v) in self.choices: if value % div != 0: real_value.append(k) value -= (value % div) div *= 2 value = real_value return super(BitFieldCheckboxSelectMultiple, self).render( name, value, attrs=attrs) def _has_changed(self, initial, data): if initial is None: initial = [] if data is None: data = [] if initial != data: return True initial_set = set([force_text(value) for value in initial]) data_set = set([force_text(value) for value in data]) return data_set != initial_set class BitFormField(IntegerField): def __init__(self, choices=(), widget=BitFieldCheckboxSelectMultiple, *args, **kwargs): if isinstance(kwargs['initial'], int): iv = kwargs['initial'] iv_list = [] for i in range(0, min(len(choices), 63)): if (1 << i) & iv > 0: iv_list += [choices[i][0]] kwargs['initial'] = iv_list self.widget = widget super(BitFormField, self).__init__(widget=widget, *args, **kwargs) self.choices = self.widget.choices = choices def clean(self, value): if not value: return 0 # Assume an iterable which contains an item per flag that's enabled result = BitHandler(0, [k for k, v in self.choices]) for k in value: try: setattr(result, str(k), True) except AttributeError: raise ValidationError('Unknown choice: %r' % (k,)) return int(result) django-bitfield-1.9.6/bitfield/apps.py0000644000175000017500000000017513512206175020775 0ustar tabbotttabbott00000000000000from django.apps import AppConfig class BitFieldAppConfig(AppConfig): name = 'bitfield' verbose_name = "Bit Field" django-bitfield-1.9.6/PKG-INFO0000644000175000017500000001065513512212332016767 0ustar tabbotttabbott00000000000000Metadata-Version: 2.1 Name: django-bitfield Version: 1.9.6 Summary: BitField in Django Home-page: https://github.com/disqus/django-bitfield Author: Disqus Author-email: opensource@disqus.com License: UNKNOWN Description: django-bitfield --------------- .. image:: https://api.travis-ci.org/disqus/django-bitfield.png?branch=master :target: https://travis-ci.org/disqus/django-bitfield Provides a BitField like class (using a BigIntegerField) for your Django models. (If you're upgrading from a version before 1.2 the API has changed greatly and is backwards incompatible!) Requirements ============ * Django >= 1.4 * PostgreSQL (see notes) **Notes:** - SQLite does not support save operations using a ``Bit`` (per the example under Usage). - MySQL fails on most queries related to BitField's. Installation ============ Install it with pip (or easy_install):: pip install django-bitfield Usage ===== First you'll need to attach a BitField to your class. This acts as a BigIntegerField (BIGINT) in your database:: from bitfield import BitField class MyModel(models.Model): flags = BitField(flags=( 'awesome_flag', 'flaggy_foo', 'baz_bar', )) Flags can also be defined with labels:: class MyModel(models.Model): flags = BitField(flags=( ('awesome_flag', 'Awesome Flag!'), ('flaggy_foo', 'Flaggy Foo'), ('baz_bar', 'Baz (bar)'), )) Now you can use the field using very familiar Django operations:: # Create the model o = MyModel.objects.create(flags=0) # Add awesome_flag (does not work in SQLite) MyModel.objects.filter(pk=o.pk).update(flags=F('flags').bitor(MyModel.flags.awesome_flag)) # Set flags manually to [awesome_flag, flaggy_foo] MyModel.objects.filter(pk=o.pk).update(flags=MyModel.flags.awesome_flag | MyModel.flags.flaggy_foo) # Remove awesome_flag (does not work in SQLite) MyModel.objects.filter(pk=o.pk).update(flags=F('flags').bitand(~MyModel.flags.awesome_flag)) # Find by awesome_flag MyModel.objects.filter(flags=MyModel.flags.awesome_flag) # Exclude by awesome_flag MyModel.objects.filter(flags=~MyModel.flags.awesome_flag) # Test awesome_flag if o.flags.awesome_flag: print "Happy times!" # List all flags on the field for f in o.flags: print f # Get a flag label print o.flags.get_label('awesome_flag') Enjoy! Admin ===== To use the widget in the admin, you'll need to update your ModelAdmin. Add the following lines to your ModelAdmin:: formfield_overrides = { BitField: {'widget': BitFieldCheckboxSelectMultiple}, } Make sure you've imported the classes by adding these lines to the top of the file:: from bitfield import BitField from bitfield.forms import BitFieldCheckboxSelectMultiple There is also a ``BitFieldListFilter`` list filter (Django 1.4 or newer). To use it set ``list_filter`` ModelAdmin option:: list_filter = ( ('flags', BitFieldListFilter,) ) BitFieldListFilter is in ``bitfield.admin`` module:: from bitfield.admin import BitFieldListFilter Platform: UNKNOWN Classifier: Framework :: Django Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: Operating System :: OS Independent Classifier: Topic :: Software Development Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Description-Content-Type: text/x-rst Provides-Extra: tests django-bitfield-1.9.6/LICENSE0000644000175000017500000002511513512206175016704 0ustar tabbotttabbott00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2010 DISQUS Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.django-bitfield-1.9.6/setup.cfg0000644000175000017500000000046513512212332017511 0ustar tabbotttabbott00000000000000[tool:pytest] python_files = test*.py addopts = --tb=native -p no:doctest norecursedirs = bin dist docs htmlcov hooks node_modules .* {args} [flake8] ignore = F999,E501,E128,E124,E402,W503,E731,F841 max-line-length = 100 exclude = .tox,.git,docs [wheel] universal = 1 [egg_info] tag_build = tag_date = 0 django-bitfield-1.9.6/django_bitfield.egg-info/0000755000175000017500000000000013512212332022461 5ustar tabbotttabbott00000000000000django-bitfield-1.9.6/django_bitfield.egg-info/top_level.txt0000644000175000017500000000001113512212332025203 0ustar tabbotttabbott00000000000000bitfield django-bitfield-1.9.6/django_bitfield.egg-info/dependency_links.txt0000644000175000017500000000000113512212332026527 0ustar tabbotttabbott00000000000000 django-bitfield-1.9.6/django_bitfield.egg-info/not-zip-safe0000644000175000017500000000000113512206373024717 0ustar tabbotttabbott00000000000000 django-bitfield-1.9.6/django_bitfield.egg-info/PKG-INFO0000644000175000017500000001065513512212332023565 0ustar tabbotttabbott00000000000000Metadata-Version: 2.1 Name: django-bitfield Version: 1.9.6 Summary: BitField in Django Home-page: https://github.com/disqus/django-bitfield Author: Disqus Author-email: opensource@disqus.com License: UNKNOWN Description: django-bitfield --------------- .. image:: https://api.travis-ci.org/disqus/django-bitfield.png?branch=master :target: https://travis-ci.org/disqus/django-bitfield Provides a BitField like class (using a BigIntegerField) for your Django models. (If you're upgrading from a version before 1.2 the API has changed greatly and is backwards incompatible!) Requirements ============ * Django >= 1.4 * PostgreSQL (see notes) **Notes:** - SQLite does not support save operations using a ``Bit`` (per the example under Usage). - MySQL fails on most queries related to BitField's. Installation ============ Install it with pip (or easy_install):: pip install django-bitfield Usage ===== First you'll need to attach a BitField to your class. This acts as a BigIntegerField (BIGINT) in your database:: from bitfield import BitField class MyModel(models.Model): flags = BitField(flags=( 'awesome_flag', 'flaggy_foo', 'baz_bar', )) Flags can also be defined with labels:: class MyModel(models.Model): flags = BitField(flags=( ('awesome_flag', 'Awesome Flag!'), ('flaggy_foo', 'Flaggy Foo'), ('baz_bar', 'Baz (bar)'), )) Now you can use the field using very familiar Django operations:: # Create the model o = MyModel.objects.create(flags=0) # Add awesome_flag (does not work in SQLite) MyModel.objects.filter(pk=o.pk).update(flags=F('flags').bitor(MyModel.flags.awesome_flag)) # Set flags manually to [awesome_flag, flaggy_foo] MyModel.objects.filter(pk=o.pk).update(flags=MyModel.flags.awesome_flag | MyModel.flags.flaggy_foo) # Remove awesome_flag (does not work in SQLite) MyModel.objects.filter(pk=o.pk).update(flags=F('flags').bitand(~MyModel.flags.awesome_flag)) # Find by awesome_flag MyModel.objects.filter(flags=MyModel.flags.awesome_flag) # Exclude by awesome_flag MyModel.objects.filter(flags=~MyModel.flags.awesome_flag) # Test awesome_flag if o.flags.awesome_flag: print "Happy times!" # List all flags on the field for f in o.flags: print f # Get a flag label print o.flags.get_label('awesome_flag') Enjoy! Admin ===== To use the widget in the admin, you'll need to update your ModelAdmin. Add the following lines to your ModelAdmin:: formfield_overrides = { BitField: {'widget': BitFieldCheckboxSelectMultiple}, } Make sure you've imported the classes by adding these lines to the top of the file:: from bitfield import BitField from bitfield.forms import BitFieldCheckboxSelectMultiple There is also a ``BitFieldListFilter`` list filter (Django 1.4 or newer). To use it set ``list_filter`` ModelAdmin option:: list_filter = ( ('flags', BitFieldListFilter,) ) BitFieldListFilter is in ``bitfield.admin`` module:: from bitfield.admin import BitFieldListFilter Platform: UNKNOWN Classifier: Framework :: Django Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: Operating System :: OS Independent Classifier: Topic :: Software Development Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Description-Content-Type: text/x-rst Provides-Extra: tests django-bitfield-1.9.6/django_bitfield.egg-info/requires.txt0000644000175000017500000000011313512212332025054 0ustar tabbotttabbott00000000000000Django>=1.4.22 six [tests] flake8 mysqlclient psycopg2>=2.3 pytest-django django-bitfield-1.9.6/django_bitfield.egg-info/SOURCES.txt0000644000175000017500000000102113512212332024337 0ustar tabbotttabbott00000000000000LICENSE MANIFEST.in README.rst setup.cfg setup.py bitfield/__init__.py bitfield/admin.py bitfield/apps.py bitfield/compat.py bitfield/forms.py bitfield/models.py bitfield/query.py bitfield/types.py bitfield/tests/__init__.py bitfield/tests/forms.py bitfield/tests/models.py bitfield/tests/tests.py django_bitfield.egg-info/PKG-INFO django_bitfield.egg-info/SOURCES.txt django_bitfield.egg-info/dependency_links.txt django_bitfield.egg-info/not-zip-safe django_bitfield.egg-info/requires.txt django_bitfield.egg-info/top_level.txtdjango-bitfield-1.9.6/MANIFEST.in0000644000175000017500000000007513512206175017433 0ustar tabbotttabbott00000000000000include setup.py README MANIFEST.in LICENSE global-exclude *~django-bitfield-1.9.6/README.rst0000644000175000017500000000543513512206175017371 0ustar tabbotttabbott00000000000000django-bitfield --------------- .. image:: https://api.travis-ci.org/disqus/django-bitfield.png?branch=master :target: https://travis-ci.org/disqus/django-bitfield Provides a BitField like class (using a BigIntegerField) for your Django models. (If you're upgrading from a version before 1.2 the API has changed greatly and is backwards incompatible!) Requirements ============ * Django >= 1.4 * PostgreSQL (see notes) **Notes:** - SQLite does not support save operations using a ``Bit`` (per the example under Usage). - MySQL fails on most queries related to BitField's. Installation ============ Install it with pip (or easy_install):: pip install django-bitfield Usage ===== First you'll need to attach a BitField to your class. This acts as a BigIntegerField (BIGINT) in your database:: from bitfield import BitField class MyModel(models.Model): flags = BitField(flags=( 'awesome_flag', 'flaggy_foo', 'baz_bar', )) Flags can also be defined with labels:: class MyModel(models.Model): flags = BitField(flags=( ('awesome_flag', 'Awesome Flag!'), ('flaggy_foo', 'Flaggy Foo'), ('baz_bar', 'Baz (bar)'), )) Now you can use the field using very familiar Django operations:: # Create the model o = MyModel.objects.create(flags=0) # Add awesome_flag (does not work in SQLite) MyModel.objects.filter(pk=o.pk).update(flags=F('flags').bitor(MyModel.flags.awesome_flag)) # Set flags manually to [awesome_flag, flaggy_foo] MyModel.objects.filter(pk=o.pk).update(flags=MyModel.flags.awesome_flag | MyModel.flags.flaggy_foo) # Remove awesome_flag (does not work in SQLite) MyModel.objects.filter(pk=o.pk).update(flags=F('flags').bitand(~MyModel.flags.awesome_flag)) # Find by awesome_flag MyModel.objects.filter(flags=MyModel.flags.awesome_flag) # Exclude by awesome_flag MyModel.objects.filter(flags=~MyModel.flags.awesome_flag) # Test awesome_flag if o.flags.awesome_flag: print "Happy times!" # List all flags on the field for f in o.flags: print f # Get a flag label print o.flags.get_label('awesome_flag') Enjoy! Admin ===== To use the widget in the admin, you'll need to update your ModelAdmin. Add the following lines to your ModelAdmin:: formfield_overrides = { BitField: {'widget': BitFieldCheckboxSelectMultiple}, } Make sure you've imported the classes by adding these lines to the top of the file:: from bitfield import BitField from bitfield.forms import BitFieldCheckboxSelectMultiple There is also a ``BitFieldListFilter`` list filter (Django 1.4 or newer). To use it set ``list_filter`` ModelAdmin option:: list_filter = ( ('flags', BitFieldListFilter,) ) BitFieldListFilter is in ``bitfield.admin`` module:: from bitfield.admin import BitFieldListFilter django-bitfield-1.9.6/setup.py0000644000175000017500000000341113512211161017373 0ustar tabbotttabbott00000000000000#!/usr/bin/env python import ast import os.path from setuptools import setup, find_packages class GetVersion(ast.NodeVisitor): def __init__(self, path): with open(path) as f: self.visit(ast.parse(f.read(), path)) def visit_Assign(self, node): if any(target.id == 'VERSION' for target in node.targets): assert not hasattr(self, 'VERSION') self.VERSION = node.value.s this_directory = os.path.abspath(os.path.dirname(__file__)) with open(os.path.join(this_directory, 'README.rst')) as f: long_description = f.read() setup( name='django-bitfield', version=GetVersion(os.path.join(os.path.dirname(__file__), 'bitfield', '__init__.py')).VERSION, author='Disqus', author_email='opensource@disqus.com', url='https://github.com/disqus/django-bitfield', description='BitField in Django', long_description=long_description, long_description_content_type='text/x-rst', packages=find_packages(), zip_safe=False, install_requires=[ 'Django>=1.4.22', 'six', ], extras_require={ 'tests': [ 'flake8', 'mysqlclient', 'psycopg2>=2.3', 'pytest-django', ], }, include_package_data=True, classifiers=[ 'Framework :: Django', 'Intended Audience :: Developers', 'Intended Audience :: System Administrators', 'Operating System :: OS Independent', 'Topic :: Software Development', "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", ], )