Merge pull request #93 from tucarga/choices-name

Added choices_name parameter to StatusField
This commit is contained in:
Carl Meyer 2013-11-14 12:13:46 -08:00
commit ccd7086112
5 changed files with 36 additions and 9 deletions

View file

@ -4,6 +4,9 @@ CHANGES
master (unreleased) master (unreleased)
------------------- -------------------
* Can pass `choices_name` to `StatusField` to use a different name for
choices class attribute. ``STATUS`` is used by default.
* Can pass model subclasses, rather than strings, into * Can pass model subclasses, rather than strings, into
`select_subclasses()`. Thanks Keryn Knight. Merge of GH-79. `select_subclasses()`. Thanks Keryn Knight. Merge of GH-79.

View file

@ -8,9 +8,10 @@ StatusField
A simple convenience for giving a model a set of "states." A simple convenience for giving a model a set of "states."
``StatusField`` is a ``CharField`` subclass that expects to find a ``StatusField`` is a ``CharField`` subclass that expects to find a
``STATUS`` class attribute on its model, and uses that as its class attribute called ``STATUS`` on its model or you can pass
``choices``. Also sets a default ``max_length`` of 100, and sets its ``choices_name`` to use a different attribute name, and uses that as
default value to the first item in the ``STATUS`` choices: its ``choices``. Also sets a default ``max_length`` of 100, and sets
its default value to the first item in the ``STATUS`` choices:
.. code-block:: python .. code-block:: python
@ -25,6 +26,18 @@ default value to the first item in the ``STATUS`` choices:
(The ``STATUS`` class attribute does not have to be a :ref:`Choices` (The ``STATUS`` class attribute does not have to be a :ref:`Choices`
instance, it can be an ordinary list of two-tuples). instance, it can be an ordinary list of two-tuples).
Using a different name for the model's choices class attribute
.. code-block:: python
from model_utils.fields import StatusField
from model_utils import Choices
class Article(models.Model):
ANOTHER_CHOICES = Choices('draft', 'published')
# ...
another_field = StatusField(choices_name='ANOTHER_CHOICES')
``StatusField`` does not set ``db_index=True`` automatically; if you ``StatusField`` does not set ``db_index=True`` automatically; if you
expect to frequently filter on your status field (and it will have expect to frequently filter on your status field (and it will have
enough selectivity to make an index worthwhile) you may want to add this enough selectivity to make an index worthwhile) you may want to add this

View file

@ -5,6 +5,8 @@ from django.conf import settings
from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import python_2_unicode_compatible
from django.utils.timezone import now from django.utils.timezone import now
DEFAULT_CHOICES_NAME = 'STATUS'
class AutoCreatedField(models.DateTimeField): class AutoCreatedField(models.DateTimeField):
""" """
@ -48,16 +50,17 @@ class StatusField(models.CharField):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
kwargs.setdefault('max_length', 100) kwargs.setdefault('max_length', 100)
self.check_for_status = not kwargs.pop('no_check_for_status', False) self.check_for_status = not kwargs.pop('no_check_for_status', False)
self.choices_name = kwargs.pop('choices_name', DEFAULT_CHOICES_NAME)
super(StatusField, self).__init__(*args, **kwargs) super(StatusField, self).__init__(*args, **kwargs)
def prepare_class(self, sender, **kwargs): def prepare_class(self, sender, **kwargs):
if not sender._meta.abstract and self.check_for_status: if not sender._meta.abstract and self.check_for_status:
assert hasattr(sender, 'STATUS'), \ assert hasattr(sender, self.choices_name), \
"To use StatusField, the model '%s' must have a STATUS choices class attribute." \ "To use StatusField, the model '%s' must have a %s choices class attribute." \
% sender.__name__ % (sender.__name__, self.choices_name)
self._choices = sender.STATUS self._choices = getattr(sender, self.choices_name)
if not self.has_default(): if not self.has_default():
self.default = tuple(sender.STATUS)[0][0] # set first as default self.default = tuple(getattr(sender, self.choices_name))[0][0] # set first as default
def contribute_to_class(self, cls, name): def contribute_to_class(self, cls, name):
models.signals.class_prepared.connect(self.prepare_class, sender=cls) models.signals.class_prepared.connect(self.prepare_class, sender=cls)

View file

@ -352,3 +352,8 @@ class StatusFieldDefaultFilled(models.Model):
class StatusFieldDefaultNotFilled(models.Model): class StatusFieldDefaultNotFilled(models.Model):
STATUS = Choices((0, "no", "No"), (1, "yes", "Yes")) STATUS = Choices((0, "no", "No"), (1, "yes", "Yes"))
status = StatusField() status = StatusField()
class StatusFieldChoicesName(models.Model):
NAMED_STATUS = Choices((0, "no", "No"), (1, "yes", "Yes"))
status = StatusField(choices_name='NAMED_STATUS')

View file

@ -28,7 +28,7 @@ from model_utils.tests.models import (
ModelTracked, ModelTrackedFK, ModelTrackedNotDefault, ModelTrackedMultiple, InheritedModelTracked, ModelTracked, ModelTrackedFK, ModelTrackedNotDefault, ModelTrackedMultiple, InheritedModelTracked,
Tracked, TrackedFK, TrackedNotDefault, TrackedNonFieldAttr, TrackedMultiple, Tracked, TrackedFK, TrackedNotDefault, TrackedNonFieldAttr, TrackedMultiple,
InheritedTracked, StatusFieldDefaultFilled, StatusFieldDefaultNotFilled, InheritedTracked, StatusFieldDefaultFilled, StatusFieldDefaultNotFilled,
InheritanceManagerTestChild3) InheritanceManagerTestChild3, StatusFieldChoicesName)
class GetExcerptTests(TestCase): class GetExcerptTests(TestCase):
@ -259,6 +259,9 @@ class StatusFieldTests(TestCase):
instance = StatusFieldDefaultFilled() instance = StatusFieldDefaultFilled()
self.assertEqual(instance.get_status_display(), "Yes") self.assertEqual(instance.get_status_display(), "Yes")
def test_choices_name(self):
StatusFieldChoicesName()
class ChoicesTests(TestCase): class ChoicesTests(TestCase):
def setUp(self): def setUp(self):