跳转到主要内容

一个小型助手应用程序,用于改进Django选择(字段)

项目描述

PyPI Version Build Status on Travis CI Documentation Status on ReadTheDoc

django-extended-choices

一个小型应用程序,用于改进Django选择

django-extended-choices旨在提供一种更好的、更易于阅读的方法来使用选择 Django

安装

您可以直接通过pip安装(自版本0.3起)

$ pip install django-extended-choices

或从Github存储库(默认为master分支)

$ git clone git://github.com/twidi/django-extended-choices.git
$ cd django-extended-choices
$ sudo python setup.py install

使用方法

目标是替换这个

STATE_ONLINE  = 1
STATE_DRAFT   = 2
STATE_OFFLINE = 3

STATE_CHOICES = (
    (STATE_ONLINE,  'Online'),
    (STATE_DRAFT,   'Draft'),
    (STATE_OFFLINE, 'Offline'),
)

STATE_DICT = dict(STATE_CHOICES)

class Content(models.Model):
    title      = models.CharField(max_length=255)
    content    = models.TextField()
    state      = models.PositiveSmallIntegerField(choices=STATE_CHOICES, default=STATE_DRAFT)

    def __unicode__(self):
        return u'Content "%s" (state=%s)' % (self.title, STATE_DICT[self.state])

print(Content.objects.filter(state=STATE_ONLINE))

为这个

from extended_choices import Choices

STATES = Choices(
    ('ONLINE',  1, 'Online'),
    ('DRAFT',   2, 'Draft'),
    ('OFFLINE', 3, 'Offline'),
)

class Content(models.Model):
    title      = models.CharField(max_length=255)
    content    = models.TextField()
    state      = models.PositiveSmallIntegerField(choices=STATES, default=STATES.DRAFT)

    def __unicode__(self):
        return u'Content "%s" (state=%s)' % (self.title, STATES.for_value(self.state).display)

print(Content.objects.filter(state=STATES.ONLINE))

如你所见,所有状态都只有一个声明,每个状态按顺序有

  • 可以使用的伪常量名称(STATES.ONLINE替代了之前的STATE_ONLINE

  • 在数据库中使用的值——这也可以是一个字符串

  • 要显示的名称——如果您需要国际化,则可以将文本包装在ugettext_lazy()

然后,您可以使用

  • STATESSTATES.choices,在字段声明中使用 choices=

  • STATES.for_constant(constant),根据常量名称获取选项条目

  • STATES.for_value(constant),根据在数据库中使用的键获取选项条目

  • STATES.for_display(constant),根据可显示的值获取选项条目(在某些情况下可能很有用)

通过 for_constantfor_valuefor_display 获取的每个选项条目都返回一个元组,作为 Choices 构造函数提供的,但具有额外的属性

>>> entry = STATES.for_constant('ONLINE')
>>> entry == ('ONLINE', 1, 'Online')
True
>>> entry.constant
'ONLINE'
>>> entry.value
1
>>> entry.display
'Online'

这些属性是可链式的(用奇怪的例子来查看链式属性)

>>> entry.constant.value
1
>>> entry.constant.value.value.display.constant.display
'Online'

为了实现这一点,我们不得不移除对 None 值的支持。请使用空字符串代替。

请注意,如果想要与可能警告未定义属性的 IDE 作战,则可以通过字典键访问常量(例如 STATES['ONLINE']

您可以直接检查一个值是否在 Choices 对象中

>>> 1 in STATES
True
>>> 42 in STATES
False

您甚至可以迭代 Choices 对象来获取 Django 看到的选项

>>> for choice in STATES:
...     print(choice)
(1, 'Online')
(2, 'Draft')
(3, 'Offline')

要获取 Choices 对象提供的所有选项条目,可以使用 entries 属性

>>> for choice_entry in STATES.entries:
...     print(choice_entry)
('ONLINE',  1, 'Online'),
('DRAFT',   2, 'Draft'),
('OFFLINE', 3, 'Offline'),

或以下字典,使用常量、值或显示名称作为键,匹配的选项条目作为值

  • STATES.constants

  • STATES.values

  • STATES.displays

>>> STATES.constants['ONLINE'] is STATES.for_constant('ONLINE')
True
>>> STATES.values[2] is STATES.for_value(2)
True
>>> STATES.displays['Offline'] is STATES.for_display('Offline')
True

如果您想使这些字典有序,可以将要使用的字典类传递给 Choices 构造函数

from collections import OrderedDict
STATES = Choices(
    ('ONLINE',  1, 'Online'),
    ('DRAFT',   2, 'Draft'),
    ('OFFLINE', 3, 'Offline'),
    dict_class = OrderedDict
)

从版本 1.1 开始,提供了新的 OrderedChoices 类,这正是:使用 OrderedDict 默认作为 dict_classChoices。您可以直接从 extended_choices 中导入它。

您可以检查常量、值或显示名称是否存在

>>> STATES.has_constant('ONLINE')
True
>>> STATES.has_value(1)
True
>>> STATES.has_display('Online')
True

您可以在同一 Choices 实例中创建选择子集

>>> STATES.add_subset('NOT_ONLINE', ('DRAFT', 'OFFLINE',))
>>> STATES.NOT_ONLINE
(2, 'Draft')
(3, 'Offline')

现在,STATES.NOT_ONLINE 是一个真实的 Choices 实例,具有主要 STATES 常量的子集。

您可以使用它来生成当您只想使用选择子集时的选项

offline_state = models.PositiveSmallIntegerField(
    choices=STATES.NOT_ONLINE,
    default=STATES.DRAFT
)

由于子集是一个真实的 Choices 实例,因此具有相同的属性和方法

>>> STATES.NOT_ONLINE.for_constant('OFFLINE').value
3
>>> STATES.NOT_ONLINE.for_value(1).constant
Traceback (most recent call last):
...
KeyError: 3
>>> list(STATES.NOT_ONLINE.constants.keys())
['DRAFT', 'OFFLINE']
>>> STATES.NOT_ONLINE.has_display('Online')
False

您可以创建任意多的子集,如果需要,则重用相同的常量

STATES.add_subset('NOT_OFFLINE', ('ONLINE', 'DRAFT'))

如果您想检查子集中的成员资格,可以这样做

def is_online(self):
    # it's an example, we could have just tested with STATES.ONLINE
    return self.state not in STATES.NOT_ONLINE

如果您想根据子集中的值过滤查询集,则可以使用 values,但由于 values 是字典,必须使用 keys()

Content.objects.filter(state__in=STATES.NOT_ONLINE.values.keys())

您可以使用 add_choices 在多个步骤中添加选项条目,可能同时创建子集。

要构建与之前相同的 Choices,我们可能这样做

STATES = Choices()
STATES.add_choices(
    ('ONLINE', 1, 'Online')
)
STATES.add_choices(
    ('DRAFT',   2, 'Draft'),
    ('OFFLINE', 3, 'Offline'),
    name='NOT_ONLINE'
)

您还可以将 argument 传递给 Choices 构造函数以创建一个包含所有添加的选项条目的子集(它将使用名称和条目调用 add_choices

现有子集名称的列表位于父 Choices 对象的 subsets 属性中。

如果您想选择一个子集但不想在原始的Choices对象中保存它,可以使用extract_subset而不是add_subset

>>> subset = STATES.extract_subset('DRAFT', 'OFFLINE')
>>> subset
(2, 'Draft')
(3, 'Offline')

关于由add_subset创建的子集,您有一个真正的Choices对象,但无法从原始的Choices对象中访问。

请注意,在extract_subset中,您直接传递字符串,而不是像add_subset的第二个参数那样传递列表/元组。

额外属性

每个元组必须包含三个元素。但您可以将字典作为第四个参数传递,这个字典的每个条目都将作为选择条目的属性保存

>>> PLANETS = Choices(
...     ('EARTH', 'earth', 'Earth', {'color': 'blue'}),
...     ('MARS', 'mars', 'Mars', {'color': 'red'}),
... )
>>> PLANETS.EARTH.color
'blue'

自动显示/值

我们提供了两个类,以简化您的选择编写,这样您就不需要翻译显示值。

AutoChoices

这是一个更简单、更快的版本:您只需粘贴常量,

  • 数据库中保存的值将是常量的小写形式

  • 显示值将是用空格替换下划线的常量,并且首字母大写

>>> from extended_choices import AutoChoices
>>> PLANETS = AutoChoices('EARTH', 'MARS')
>>> PLANETS.EARTH.value
'earth'
>>> PLANETS.MARS.display
'Mars'

如果您想传递额外的属性,请传递一个元组,其中字典作为最后一个元素

>>> PLANETS = AutoChoices(
...     ('EARTH', {'color': 'blue'}),
...     ('MARS', {'color': 'red'}),
... )
>>> PLANETS.EARTH.value
'earth'
>>> PLANETS.EARTH.color
'blue'

您可以通过将value_transformdisplay_transform函数传递给构造函数来更改用于将常量转换为要保存的值和显示值的转换函数。

>>> PLANETS = AutoChoices(
...     'EARTH', 'MARS',
...     value_transform=lambda const: 'planet_' + const.lower().
...     display_transform=lambda const: 'Planet: ' + const.lower().
... )
>>> PLANETS.EARTH.value
'planet_earth'
>>> PLANETS.MARS.display
'Planet: mars'

如果您发现自己重复使用这些转换函数,可以有一个基类,这些函数作为类属性定义

>>> class MyAutoChoices(AutoChoices):
...     value_transform=staticmethod(lambda const: const.upper())
...     display_transform=staticmethod(lambda const: const.lower())

>>> PLANETS = MyAutoChoices('EARTH', 'MARS')
>>> PLANETS.EARTH.value
'EARTH'
>>> PLANETS.MARS.dispay
'mars'

当然,您仍然可以通过将它们传递给构造函数来覆盖这些函数。

如果您想为某个条目强制一个特定值,可以简单地将它作为第二个参数传递

>>> PLANETS = AutoChoices(
...     'EARTH',
...     ('MARS', 'red-planet'),
... )
>>> PLANETS.MARS.value
'red-planet'

然后,如果您想设置显示,请传递第三个参数

>>> PLANETS = AutoChoices(
...     'EARTH',
...     ('MARS', 'red-planet', 'Red planet'),
... )
>>> PLANETS.MARS.value
'red-planet'
>>> PLANETS.MARS.display
'Red planet'

要强制显示值但让数据库值自动计算,请将第二个参数设置为None

>>> PLANETS = AutoChoices(
...     'EARTH',
...     ('MARS', None, 'Red planet'),
... )
>>> PLANETS.MARS.value
'mars'
>>> PLANETS.MARS.display
'Red planet'

AutoDisplayChoices

在这个版本中,您必须定义要保存到数据库中的值。显示值将由AutoChoices类似的方式组成

>>> from extended_choices import AutoDisplayChoices
>>> PLANETS = AutoDisplayChoices(
...     ('EARTH', 1),
...     ('MARS', 2),
... )
>>> PLANETS.EARTH.value
1
>>> PLANETS.MARS.display
'Mars'

如果您想传递额外的属性,请传递一个元组,其中字典作为最后一个元素

>>> PLANETS = AutoDisplayChoices(
...     ('EARTH', 'earth', {'color': 'blue'}),
...     ('MARS', 'mars', {'color': 'red'}),
... )
>>> PLANETS.EARTH.value
1
>>> PLANETS.EARTH.display
'Earth'
>>> PLANETS.EARTH.color
'blue'

AutoChoices一样,您可以通过将display_transform传递给构造函数来更改用于显示值的转换函数。

如果您想为某个条目强制特定的显示,可以简单地将它作为第三个参数传递

>>> PLANETS = AutoChoices(
...     ('EARTH', 1),
...     ('MARS', 2, 'Red planet'),
... )
>>> PLANETS.MARS.display
'Red planet'

注意

  • 您在extended_choices.fields中还有一个非常基本的字段(NamedExtendedChoiceFormField`),它接受常量名称而不是值

  • 请随意阅读源代码,了解更多关于这个小的Django应用的信息。

  • 您可以在任何地方声明您的选择。我的用法是在models.py文件中,在类声明之前。

兼容性

版本1.0提供了一个全新的API,并且在1.1中删除了与之前版本(0.4.1)的兼容性。具有兼容性的最后一个版本是1.0.7

如果您需要这种兼容性,您可以通过在requirements中固定它来使用特定的版本。

许可证

BSD许可证下可用。请参阅包含的LICENSE文件

Python/Django版本支持

Django版本

Python版本

1.8, 1.9, 1.10

2.7, 3.4, 3.5

1.11

2.7, 3.4, 3.5, 3.6

2.0

3.4, 3.5, 3.6, 3.7

2.1, 2.2

3.5, 3.6, 3.7

测试

要运行代码源中的测试,请创建一个虚拟环境或激活一个环境,安装Django,然后

python -m extended_choices.tests

我们还提供了一些快速的doctests,在代码文档中。要执行它们

python -m extended_choices

注意:doctests仅在Python版本中工作,不会显示字符串前的u前缀。

源代码

源代码可在Github上找到。

开发

如果您想参与此库的开发,需要在您的虚拟环境中安装Django。如果您还没有,只需运行

pip install -r requirements-dev.txt

别忘了运行测试 ;)

请在Github上提出pull request!

在您的pull request提交后的几分钟内,将在TravisCi上为我们支持的Python和Django所有版本执行测试。

文档

您可以在ReadTheDoc上找到文档。

要更新文档,您需要一些工具

pip install -r requirements-makedoc.txt

然后进入docs目录,并运行

make html

作者

由Stéphane “Twidi” Angel撰写 <s.angel@twidi.com> (http://twidi.com), 原始版本为 http://www.liberation.fr

Bitdeli badge

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源代码分发

django-extended-choices-1.3.3.tar.gz (26.2 kB 查看哈希值)

上传时间 源代码

构建分发

django_extended_choices-1.3.3-py2.py3-none-any.whl (33.4 kB 查看哈希值)

上传时间 Python 2 Python 3

由以下机构支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面