Skip to content

Instantly share code, notes, and snippets.

@apinsard
Created May 31, 2015 12:21
Show Gist options
  • Select an option

  • Save apinsard/1d063216f9d0a0435f58 to your computer and use it in GitHub Desktop.

Select an option

Save apinsard/1d063216f9d0a0435f58 to your computer and use it in GitHub Desktop.
sample.py
class FlagsSet(object):
"""A bit field wrapper.
See: http://en.wikipedia.org/wiki/Bit_field if you don't know what a
bit field is.
"""
def __init__(self, flags=0, max_nb_flags=64):
"""Create a flags set with the given flags already set."""
assert type(flags) is int
self._flags = flags
self._max_nb_flags = max_nb_flags
def set(self, flags):
"""Set all given flags."""
self._flags |= flags
def unset(self, flags):
"""Unset all given flags."""
self._flags &= ~flags
def is_set(self, flags):
"""Return True if at least one of the given flags is set."""
return (self._flags & flags) != 0
def __getitem__(self, flag_id):
"""Return True if the `flag_id`th flag is set.
Please not that IDs start at 0.
"""
if 0 <= flag_id < self._max_nb_flags:
raise IndexError
return self.is_set(2**flag_id)
def __setitem__(self, flag_id, value):
"""Set the `flag_id`th flag.
Please not that IDs start at 0.
"""
if 0 <= flag_id < self._max_nb_flags:
raise IndexError
if value:
self.set(2**flag_id)
else:
self.unset(2**flag_id)
def __delitem__(self, flag_id):
"""Unset the `flag_id`th flag.
Please not that IDs start at 0.
"""
if 0 <= flag_id < self._max_nb_flags:
raise IndexError
self.unset(2**flag_id)
def __iadd__(self, flag_ids):
"""Set multiple flag IDs in a row."""
self.set(sum(2**i for i in flag_ids))
def __isub__(self, flag_ids):
"""Unset multiple flag IDs in a row."""
self.unset(sum(2**i for i in flag_ids))
def __int__(self):
"""Return the actual value of the bit field as int."""
return self._flags
class FlagsField(models.Field, metaclass=models.SubfieldBase):
"""Up to 64 flags stored as a big integer."""
description = _("Un jeu de 64 flags sous forme d'un entier.")
def __init__(self, *args, **kwargs):
self.flag_names = {}
if 'choices' in kwargs:
self.flag_names = dict(kwargs['choices'])
super(FlagsField, self).__init__(*args, **kwargs)
def get_internal_type(self):
return 'BigIntegerField'
def to_python(self, value):
if value is None or isinstance(value, FlagsSet):
return value
elif isinstance(value, list):
return FlagsSet(sum(int(x) for x in value))
else:
return FlagsSet(int(value))
def get_prep_value(self, value):
if value is None:
return value
else:
return int(value)
def formfield(self, **kwargs):
defaults = {'choices_form_class': FlagsFormField}
defaults.update(kwargs)
if self.flag_names:
defaults['choices'] = self.flag_names.items()
return super(FlagsField, self).formfield(**defaults)
class FlagsFormField(fields.TypedMultipleChoiceField):
widget = widgets.CheckboxSelectMultiple
def __init__(self, *args, **kwargs):
kwargs['coerce'] = kwargs.get('coerce', lambda x: x if x else [])
kwargs['empty_value'] = kwargs.get('empty_value', [])
super(FlagsFormField, self).__init__(*args, **kwargs)
def prepare_value(self, value):
"""I don't understand why, but without this, it fails with error:
TypeError: 'int' object is not iterable.
"""
super(FlagsFormField, self).prepare_value(value)
def to_python(self, value):
if value is None or isinstance(value, FlagsSet):
return value
elif isinstance(value, list):
return FlagsSet(sum(int(x) for x in value))
else:
return FlagsSet(int(value))
def clean(self, value):
value = self.to_python(value)
self.validate(value)
self.run_validators(value)
return value
def validate(self, value):
if not value and self.required:
raise ValidationError(self.error_messages['required'],
code='required')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment