Added choice type
This commit is contained in:
@@ -19,6 +19,8 @@ from .generic import generic_relationship
|
|||||||
from .proxy_dict import ProxyDict, proxy_dict
|
from .proxy_dict import ProxyDict, proxy_dict
|
||||||
from .types import (
|
from .types import (
|
||||||
ArrowType,
|
ArrowType,
|
||||||
|
Choice,
|
||||||
|
ChoiceType,
|
||||||
ColorType,
|
ColorType,
|
||||||
CountryType,
|
CountryType,
|
||||||
Country,
|
Country,
|
||||||
@@ -65,6 +67,8 @@ __all__ = (
|
|||||||
table_name,
|
table_name,
|
||||||
with_backrefs,
|
with_backrefs,
|
||||||
ArrowType,
|
ArrowType,
|
||||||
|
Choice,
|
||||||
|
ChoiceType,
|
||||||
ColorType,
|
ColorType,
|
||||||
CountryType,
|
CountryType,
|
||||||
Country,
|
Country,
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
from functools import wraps
|
from functools import wraps
|
||||||
from sqlalchemy.orm.collections import InstrumentedList as _InstrumentedList
|
from sqlalchemy.orm.collections import InstrumentedList as _InstrumentedList
|
||||||
from .arrow import ArrowType
|
from .arrow import ArrowType
|
||||||
|
from .choice import ChoiceType, Choice
|
||||||
from .color import ColorType
|
from .color import ColorType
|
||||||
from .country import CountryType, Country
|
from .country import CountryType, Country
|
||||||
from .email import EmailType
|
from .email import EmailType
|
||||||
@@ -24,6 +25,8 @@ from .weekdays import WeekDay, WeekDays, WeekDaysType
|
|||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
ArrowType,
|
ArrowType,
|
||||||
|
Choice,
|
||||||
|
ChoiceType,
|
||||||
ColorType,
|
ColorType,
|
||||||
CountryType,
|
CountryType,
|
||||||
Country,
|
Country,
|
||||||
|
56
sqlalchemy_utils/types/choice.py
Normal file
56
sqlalchemy_utils/types/choice.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
from sqlalchemy import types
|
||||||
|
import six
|
||||||
|
from ..exceptions import ImproperlyConfigured
|
||||||
|
from .scalar_coercible import ScalarCoercible
|
||||||
|
|
||||||
|
|
||||||
|
class Choice(object):
|
||||||
|
def __init__(self, code, value):
|
||||||
|
self.code = code
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if isinstance(other, Choice):
|
||||||
|
return self.code == other.code
|
||||||
|
return other == self.code
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not (self == other)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return six.text_type(self.value)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'Choice(code={code}, value={value})'.format(
|
||||||
|
code=self.code,
|
||||||
|
value=self.value
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ChoiceType(types.TypeDecorator, ScalarCoercible):
|
||||||
|
impl = types.Unicode(255)
|
||||||
|
|
||||||
|
def __init__(self, choices):
|
||||||
|
if not choices:
|
||||||
|
raise ImproperlyConfigured(
|
||||||
|
'ChoiceType needs list of choices defined.'
|
||||||
|
)
|
||||||
|
self.choices = choices
|
||||||
|
self.choices_dict = dict(choices)
|
||||||
|
|
||||||
|
def _coerce(self, value):
|
||||||
|
if value is None:
|
||||||
|
return value
|
||||||
|
if isinstance(value, Choice):
|
||||||
|
return value
|
||||||
|
return Choice(value, self.choices_dict[value])
|
||||||
|
|
||||||
|
def process_bind_param(self, value, dialect):
|
||||||
|
if value:
|
||||||
|
return value.code
|
||||||
|
return value
|
||||||
|
|
||||||
|
def process_result_value(self, value, dialect):
|
||||||
|
if value:
|
||||||
|
return Choice(value, self.choices_dict[value])
|
||||||
|
return value
|
56
tests/types/test_choice_type.py
Normal file
56
tests/types/test_choice_type.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
from pytest import raises
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy_utils import ChoiceType, Choice, ImproperlyConfigured
|
||||||
|
from tests import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestChoice(object):
|
||||||
|
# def test_init(self):
|
||||||
|
# assert Choice(1, 1) == Choice(Choice(1, 1))
|
||||||
|
|
||||||
|
def test_equality_operator(self):
|
||||||
|
assert Choice(1, 1) == 1
|
||||||
|
assert 1 == Choice(1, 1)
|
||||||
|
assert Choice(1, 1) == Choice(1, 1)
|
||||||
|
|
||||||
|
def test_non_equality_operator(self):
|
||||||
|
assert Choice(1, 1) != 2
|
||||||
|
assert not (Choice(1, 1) != 1)
|
||||||
|
|
||||||
|
|
||||||
|
class TestChoiceType(TestCase):
|
||||||
|
def create_models(self):
|
||||||
|
class User(self.Base):
|
||||||
|
TYPES = [
|
||||||
|
('admin', 'Admin'),
|
||||||
|
('regular-user', 'Regular user')
|
||||||
|
]
|
||||||
|
|
||||||
|
__tablename__ = 'user'
|
||||||
|
id = sa.Column(sa.Integer, primary_key=True)
|
||||||
|
type = sa.Column(ChoiceType(TYPES))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'User(%r)' % self.id
|
||||||
|
|
||||||
|
self.User = User
|
||||||
|
|
||||||
|
def test_parameter_processing(self):
|
||||||
|
user = self.User(
|
||||||
|
type=u'admin'
|
||||||
|
)
|
||||||
|
|
||||||
|
self.session.add(user)
|
||||||
|
self.session.commit()
|
||||||
|
|
||||||
|
user = self.session.query(self.User).first()
|
||||||
|
assert user.type.value == u'Admin'
|
||||||
|
|
||||||
|
def test_scalar_attributes_get_coerced_to_objects(self):
|
||||||
|
user = self.User(type=u'admin')
|
||||||
|
|
||||||
|
assert isinstance(user.type, Choice)
|
||||||
|
|
||||||
|
def test_throws_exception_if_no_choices_given(self):
|
||||||
|
with raises(ImproperlyConfigured):
|
||||||
|
ChoiceType([])
|
Reference in New Issue
Block a user