Skip to content

Commit

Permalink
Merge efe00c0 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 + efe00c0 commit f28f0c0
Show file tree
Hide file tree
Showing 13 changed files with 92 additions and 109 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
15 changes: 15 additions & 0 deletions Changelog.rst
Expand Up @@ -2,6 +2,21 @@
Changelog
=========

1.5.0
-----

* Dropped support for old Python/Django versions.
* Added support for ``NullBooleanField`` -- thanks to @ashwch

.. 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/
.. _docs: 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.
3 changes: 2 additions & 1 deletion docs/conf.py
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
24 changes: 13 additions & 11 deletions docs/index.rst
Expand Up @@ -39,9 +39,11 @@ Requirements
------------

Django choices is fairly simple, so most Python and Django
versions should work. Django choices is tested against Python
2.6, 2.7, 3.3, 3.4, 3.5 and PyPy. Django 1.3 until 1.9 (including)
are officially supported (= tested in Travis).
versions should work. It is tested against Python 2.7, 3.3, 3.4, 3.5 and PyPy.
Django 1.8 until and including 1.11 alpha are supported (and tested in Travis).

If you need to support older Python or Django versions, you should stick with
version ``1.4.4``. Backwards compatibility is dropped from 1.5 onwards.


Quick-start
Expand All @@ -60,39 +62,39 @@ To use it, you write a choices class, and use it in your model fields:

.. code-block:: python
from djchoices import DjangoChoices, ChoiceItem
from djchoices import ChoiceItem, DjangoChoices
class Book(models.Model):
class BookTypes(DjangoChoices):
class BookType(DjangoChoices):
short_story = ChoiceItem('short', 'Short story')
novel = ChoiceItem('novel', 'Novel')
non_fiction = ChoiceItem('non_fiction', 'Non fiction')
author = models.ForeignKey('Author')
book_type = models.CharField(
max_length=20, choices=BookTypes.choices,
default=BookTypes.novel, validators=[BookTypes.validator]
max_length=20, choices=BookType.choices,
default=BookType.novel
)
You can use this in other places like this:
You can then use the availe choices in other modules, e.g.:


.. code-block:: python
from .models import Book
Person.objects.create(author=my_author, type=Book.BookTypes.short_story)
The `DjangoChoices` classes can be located anywhere you want,
The ``DjangoChoices`` classes can be located anywhere you want,
for example you can put them outside of the model declaration if you have a
'common' set of choices for different models. Any place is valid though,
you can group them all together in `choices.py` if you want.
you can group them all together in ``choices.py`` if you want.


License
-------
Expand Down

0 comments on commit f28f0c0

Please sign in to comment.