Skip to content

Commit

Permalink
Merge 4487d3f into 4c2cf22
Browse files Browse the repository at this point in the history
  • Loading branch information
sergei-maertens committed Feb 13, 2017
2 parents 4c2cf22 + 4487d3f commit 682d1b5
Show file tree
Hide file tree
Showing 14 changed files with 102 additions and 115 deletions.
28 changes: 8 additions & 20 deletions .travis.yml
@@ -1,4 +1,6 @@
language: python
python:
- "3.5"
sudo: false

install:
Expand All @@ -9,44 +11,30 @@ after_success:
- coveralls

env:
- TOXENV=py26-django13
- TOXENV=py26-django14
- TOXENV=py26-django15
- TOXENV=py26-django16

- TOXENV=py27-django13
- TOXENV=py27-django14
- TOXENV=py27-django15
- TOXENV=py27-django16
- TOXENV=py27-django17
- TOXENV=py27-django18
- TOXENV=py27-django19
- TOXENV=py27-django110
- TOXENV=py27-django111
- TOXENV=py27-djangolatest

- TOXENV=py33-django15
- TOXENV=py33-django16
- TOXENV=py33-django17
- TOXENV=py33-django18
- TOXENV=py33-djangolatest

- TOXENV=py34-django15
- TOXENV=py34-django16
- TOXENV=py34-django17
- TOXENV=py34-django18
- TOXENV=py34-django19
- TOXENV=py34-django110
- TOXENV=py34-django111
- TOXENV=py34-djangolatest

- TOXENV=py35-django18
- TOXENV=py35-django19
- TOXENV=py35-django110
- TOXENV=py35-django111
- TOXENV=py35-djangolatest

- TOXENV=pypy-django15
- TOXENV=pypy-django16
- TOXENV=pypy-django17
- TOXENV=pypy-django18
- TOXENV=pypy-django19
- TOXENV=pypy-django110
- TOXENV=pypy-django111
- TOXENV=pypy-djangolatest

- TOXENV=docs
16 changes: 16 additions & 0 deletions Changelog.rst
Expand Up @@ -2,6 +2,22 @@
Changelog
=========

1.5.0
-----

* Dropped support for old Python/Django versions.
* Added support for ``NullBooleanField`` -- thanks to @ashwch
* Added retention of choices order in ``DjangoChoices.values`` -- thanks to @merwok

.. warning::
Dropped support for Python versions < 2.7 and 3.3, and Django < 1.8. If you
need explicit support for these versions, you should stick to version 1.4.4.

1.4.4
-----

* Bugfix for better IPython support (125d523e1c94e4edb344e3bb3ea1eab6f7d073ed)

1.4.3
-----

Expand Down
28 changes: 12 additions & 16 deletions README.rst
Expand Up @@ -9,7 +9,7 @@ Django-Choices
:target: https://coveralls.io/github/bigjason/django-choices?branch=master

.. image:: https://readthedocs.org/projects/django-choices/badge/?version=latest
:target: http://django-choices.readthedocs.org/en/latest/
:target: http://django-choices.readthedocs.io/en/latest/
:alt: Documentation Status

.. image:: https://img.shields.io/pypi/v/django-choices.svg
Expand All @@ -19,7 +19,7 @@ Order and sanity for django model choices.
------------------------------------------------------

Django choices provides a declarative way of using the choices_ option on django_
fields.
fields. Read the full `documentation`_ on ReadTheDocs.

------------
Installation
Expand Down Expand Up @@ -62,31 +62,26 @@ With this::
class Person(models.Model):
# Choices
class PersonType(DjangoChoices):
Customer = ChoiceItem("C")
Employee = ChoiceItem("E")
Groundhog = ChoiceItem("G")
customer = ChoiceItem("C")
employee = ChoiceItem("E")
groundhog = ChoiceItem("G")

# Fields
name = models.CharField(max_length=32)
type = models.CharField(max_length=1,
choices=PersonType.choices,
validators=[PersonType.validator])
type = models.CharField(max_length=1, choices=PersonType.choices)

You can use this elsewhere like this::

# Other code
Person.create(name="Phil", type=Person.PersonType.Groundhog)
Person.create(name="Phil", type=Person.PersonType.groundhog)

You can use without value, and the label will be used as value::
You can use them without value, and the label will be used as value::

class Sample(DjangoChoices):
OptionA = ChoiceItem()
OptionB = ChoiceItem()
option_a = ChoiceItem()
option_b = ChoiceItem()

print(Sample.OptionA) # "OptionA"

The `DjangoChoices` classes can be located anywhere you want. If I have a lot of
declarations I will sometimes place them in a `const.py` or `choices.py`.
print(Sample.option_a) # "option_a"

-------
License
Expand All @@ -103,3 +98,4 @@ The source code can be found on github_.
.. _django: http://www.djangoproject.com/
.. _github: https://github.com/bigjason/django-choices
.. _PyPi: http://pypi.python.org/pypi/django-choices/
.. _documentation: http://django-choices.readthedocs.io/en/latest/
2 changes: 2 additions & 0 deletions djchoices/__init__.py
@@ -1,3 +1,5 @@
from __future__ import absolute_import, unicode_literals

from pkg_resources import get_distribution

from djchoices.choices import ChoiceItem, DjangoChoices, C
Expand Down
11 changes: 7 additions & 4 deletions djchoices/choices.py
@@ -1,8 +1,11 @@
from __future__ import absolute_import, unicode_literals

import re
from collections import OrderedDict

from django.core.exceptions import ValidationError

from .compat import deconstructible, OrderedDict, six
from django.utils import six
from django.utils.deconstruct import deconstructible


__all__ = ["ChoiceItem", "DjangoChoices", "C"]
Expand Down Expand Up @@ -57,7 +60,7 @@ def __init__(self, value=sentinel, label=None, order=None):
self.order = ChoiceItem.order

# Shorter convenience alias.
C = ChoiceItem
C = ChoiceItem # noqa


class DjangoChoicesMeta(type):
Expand All @@ -69,7 +72,7 @@ class DjangoChoicesMeta(type):
def __new__(cls, name, bases, attrs):
fields = {}
labels = Labels()
values = {}
values = OrderedDict()
choices = []

# Get all the fields from parent classes.
Expand Down
21 changes: 0 additions & 21 deletions djchoices/compat.py

This file was deleted.

14 changes: 6 additions & 8 deletions djchoices/tests/test_choices.py
@@ -1,12 +1,7 @@
try:
import unittest2 as unittest
except ImportError:
import unittest
import unittest

from djchoices import DjangoChoices, C, ChoiceItem

from .utils import has_new_migrations


class NumericTestClass(DjangoChoices):
Item_0 = C(0)
Expand Down Expand Up @@ -37,6 +32,7 @@ class EmptyValueClass(DjangoChoices):
Option2 = ChoiceItem()
Option3 = ChoiceItem()


class NullBooleanValueClass(DjangoChoices):
Option1 = ChoiceItem(None, "Pending")
Option2 = ChoiceItem(True, "Successful")
Expand Down Expand Up @@ -71,6 +67,9 @@ def test_class_values(self):
self.assertEqual(SubClass1.values[SubClass1.Item_4], "Item 4")
self.assertEqual(SubClass1.values[SubClass1.Item_5], "Item 5")

def test_class_values_order(self):
self.assertEqual(list(StringTestClass.values), ["", "O", "T", "H"])

def test_numeric_class_order(self):
choices = NumericTestClass.choices
self.assertEqual(choices[0][0], 0)
Expand Down Expand Up @@ -136,7 +135,7 @@ def test_validation_error_message(self):
message = ("Select a valid choice. 4 is not "
"one of the available choices.")

self.assertRaisesRegexp(ValidationError, message,
self.assertRaisesRegexp(ValidationError, message,
NumericTestClass.validator, 4)

def test_subclass1_validator(self):
Expand Down Expand Up @@ -179,7 +178,6 @@ def test_null_boolean_value_class(self):
self.assertEqual(choices[1][1], "Successful")
self.assertEqual(choices[2][1], "Failed")

@unittest.skipUnless(*has_new_migrations())
def test_deconstructible_validator(self):
deconstructed = NumericTestClass.validator.deconstruct()
self.assertEqual(deconstructed, (
Expand Down
8 changes: 2 additions & 6 deletions djchoices/tests/test_private_api.py
@@ -1,9 +1,6 @@
try:
import unittest2 as unittest
except ImportError:
import unittest
import unittest

from djchoices import DjangoChoices, C, ChoiceItem
from djchoices import DjangoChoices, C, ChoiceItem # noqa


class PrivateAPITests(unittest.TestCase):
Expand All @@ -23,4 +20,3 @@ class Choices(DjangoChoices):

keys = Choices.labels.keys()
self.assertEqual(set(keys), set(['a', 'b']))

5 changes: 0 additions & 5 deletions djchoices/tests/utils.py

This file was deleted.

19 changes: 10 additions & 9 deletions docs/choices.rst
@@ -1,7 +1,7 @@
Choice items
============

The `ChoiceItem` class is what drives the choices. Each instance
The ``ChoiceItem`` class is what drives the choices. Each instance
corresponds to a possible choice for your field.


Expand Down Expand Up @@ -113,8 +113,8 @@ value as well, and it will be determined from the label.
)
`DjangoChoices` class attributes
--------------------------------
``DjangoChoices`` class attributes
----------------------------------

The choices class itself has a few useful attributes. Most notably `choices`,
which returns the choices as a tuple.
Expand Down Expand Up @@ -169,12 +169,13 @@ Returns a dictionary with a mapping from value to label:
validator
+++++++++

Returns a validator that can be used in your model field. This validator checks
that the value passed to the field is indeed a value specified in your choices
class.
.. note::

At least since Django 1.3, there is model and form-level validation of the
choices. Unless you have a reason to explicitly specify/override the validator,
you can skip specifying this validator.

.. note::

This validator had issues in Django 1.7 and up with the new migrations.
validators have to be deconstructible. This was fixed in the 1.4 release.
Returns a validator that can be used in your model field. This validator checks
that the value passed to the field is indeed a value specified in your choices
class.
7 changes: 4 additions & 3 deletions docs/conf.py
Expand Up @@ -55,9 +55,9 @@
# built documents.
#
# The short X.Y version.
version = '1.4'
version = '1.5'
# The full version, including alpha/beta/rc tags.
release = '1.4.2'
release = '1.5.0'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down Expand Up @@ -99,6 +99,7 @@

# If true, keep warnings as "system message" paragraphs in the built documents.
#keep_warnings = False
suppress_warnings = ['image.nonlocal_uri']

# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
Expand Down Expand Up @@ -137,7 +138,7 @@
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# html_static_path = ['_static']

# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied
Expand Down

0 comments on commit 682d1b5

Please sign in to comment.