Skip to content

Instantly share code, notes, and snippets.

@ivanjr0
Created April 1, 2012 15:14
Show Gist options
  • Save ivanjr0/2276233 to your computer and use it in GitHub Desktop.
Save ivanjr0/2276233 to your computer and use it in GitHub Desktop.
Django - MultipleOptionsField - A set of boolean options stored in a positive integer field.
from django.db.models import SubfieldBase
from django.db.models.fields import PositiveIntegerField
class MultipleOptions(object):
def __init__(self, options, value=None):
self.options = options
try:
self._value = int(value)
except (ValueError, TypeError):
self._value = 0
def _get_flag_value(self, option):
return (1 << self.options.index(option))
def __getattr__(self, name):
if name in self.options:
return bool(self._value & self._get_flag_value(name))
raise AttributeError("'%s' isn't in '%s'." % (name, '\', '.join(self.options)))
def __setattr__(self, name, value):
if not name == 'options' and name in self.options:
try:
value = bool(value)
except (ValueError, TypeError):
value = False
current = getattr(self, name)
if not value == current:
self._value ^= self._get_flag_value(name)
else: return super(MultipleOptions, self).__setattr__(name, value)
class MultipleOptionsField(PositiveIntegerField):
__metaclass__ = SubfieldBase
description = "A set of boolean options stored in a positive integer field. "
def __init__(self, options=[], *args, **kwargs):
self.options = options
super(MultipleOptionsField, self).__init__(*args, **kwargs)
def to_python(self, value):
if isinstance(value, MultipleOptions):
return value
return MultipleOptions(value=value, options=self.options)
def get_prep_value(self, value):
return value._value
import unittest
from main import MultipleOptions
class MultipleOptionsTest(unittest.TestCase):
def setUp(self):
self.options = ['foo', 'bar', 'baz']
self.container = MultipleOptions(options=self.options)
def test_attrs(self):
for option in self.options:
self.assertTrue(hasattr(self.container, option))
self.assertTrue(isinstance(getattr(self.container, option), bool))
def test_get_set(self):
self.container.foo = True
self.assertTrue(self.container.foo)
self.assertEquals(2 ** self.options.index('foo'), self.container._value)
self.container.foo = False
self.assertEquals(0, self.container._value)
def test_get_set_multiple(self):
self.container.foo = True
self.assertTrue(self.container.foo)
self.container.baz = True
self.assertTrue(self.container.baz)
self.assertEquals(2 ** self.options.index('foo') \
+ 2 ** self.options.index('baz'), self.container._value)
self.container.foo = False
self.assertEquals(2 ** self.options.index('baz'), self.container._value)
self.container.baz = False
self.assertEquals(0, self.container._value)
main = unittest.main
if __name__ == '__main__': main()
@ivanjr0
Copy link
Author

ivanjr0 commented Apr 1, 2012

TODO:

  • Criar um widget para as opções
  • Definir uma maneira para setar os default values (atualmente, estou utilizando o próprio 'default' do PositiveIntegerField, porém dessa maneira as classes client tem que saber que são utilizadas flags binárias, o que não é o ideal)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment