Metadata-Version: 2.0
Name: django-echoices
Version: 2.4.2
Summary: Choices for Django model fields as enumeration
Home-page: https://github.com/mbourqui/django-echoices
Author: Marc Bourqui
Author-email: pypi.kemar@bourqui.org
License: GNU GPLv3
Keywords: django choices models enum
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 1.9
Classifier: Framework :: Django :: 1.10
Classifier: Framework :: Django :: 1.11
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Utilities
Requires-Dist: Django (>=1.9.13)

|Python| |Django| |License| |PyPI| |Build Status| |Coverage Status|

Django-EChoices
===============

Choices for Django model fields as enumeration

Features
--------

-  Specialized `enum types <#enum>`__
-  Specialized `model fields <#modelfield>`__
-  Accessible in `templates <#templages>`__

Requirements
------------

-  Django >= 1.9.13

Installation
------------

1. Run ``pip install django-echoices``

Usage
-----

Enumeration
~~~~~~~~~~~

First, define your choices enumeration (in your ``models.py`` for
example):

::

    from echoices.enums import EChoice

    class EStates(EChoice):
        # format is: (value -> char or str or int, label -> str)
        CREATED = ('c', 'Created')
        SUBMITTED = ('s', 'Submitted')

Model field
~~~~~~~~~~~

Regular model field
^^^^^^^^^^^^^^^^^^^

Then, either use a regular model field:

::

    from django.db import models

    class MyModel(models.Model):
        state = models.CharField(max_length=EStates.max_value_length(),
                                 choices=EStates.choices(),
                                 default=EStates.CREATED.value)

**Note**: If your value is an ``int``, you can use
``models.IntegerField`` instead.

Specialized field
^^^^^^^^^^^^^^^^^

You can also use specialized field. Using such a field, you will then
only handle ``Echoice`` instances.

::

    from django.db import models
    from echoices.fields import make_echoicefield

    class MyModel(models.Model):
        # `max_length` is set automatically
        state = make_echoicefield(EStates, default=EStates.CREATED)

**Note**: ``MyModel.state`` will be ``Estates`` instance stored in a
``EStatesField`` field. See `documentation <#modelfield>`__ for more
details.

**WARNING**: requires special handling of migrations. Read more in the
`doc <#migrations>`__.

Derivation
~~~~~~~~~~

You can add your own fields to the ``value`` and ``label`` ones. To do
so, you have to override the **init**\ () and your signature must look
like: ``self, value, label, *args`` where you replace ``*args`` with
your own positional arguments, as you would do when defining a custom
Enum. Do *not* call the super().\ **init**\ (), as ``value`` and
``label`` are already set internally by ``EChoice``.

As when dealing with a derived Enum, you can also add your own methods.

::

    from echoices.enums import EChoice

    class EMyChoices(EChoice):
        """Another variant of EChoice with additionnal content"""

        MY_CHOICE = (1, 'First choice', 'my additional value')

        def __init__(self, value, label, my_arg):
            self.my_arg = my_arg
            # Note: super().__init__() shall *not* be called!

        def show_myarg(self):
            """Used as: EMyChoices.MY_CHOICE.show_myarg()"""
            print(self.my_arg)

        @classmethod
        def show_all(cls):
            """Used as: EMyChoices.show_all()"""
            print(", ".join([e.my_arg for e in list(cls)]))

In templates
~~~~~~~~~~~~

Assume a ``Context(dict(estates=myapp.models.EStates))`` is provided to
the following templates.

-  Fields of the ``EChoice`` can be accessed in the templates as:

   ::

       {{ estates.CREATED.value }}
       {{ estates.CREATED.label }}

-  ``EChoice`` can also be enumerated:

   ::

       {% for state in estates %}
           {{ state.value }}
           {{ state.label }}
       {% endfor %}

Short documentation
-------------------

Specialized enum types
~~~~~~~~~~~~~~~~~~~~~~

``enums.EChoice``
^^^^^^^^^^^^^^^^^

Base enum type. Each enum element is a tuple ``(value, label)``, where
[t]he first element in each tuple is the actual value to be set on the
model, and the second element is the human-readable name 
\ `doc <https://docs.djangoproject.com/en/1.11/ref/models/fields/#choices>`__\ .
Values **must** be unique. Can be derived for further customization.

``enums.EOrderedChoice``
^^^^^^^^^^^^^^^^^^^^^^^^

Supports ordering of elements. ``EOrderedChoice.choices()`` takes an
extra optional argument, ``order``, which supports three values:
'sorted', 'reverse' or 'natural' (default). If ``sorted``, the choices
are ordered according to their value. If ``reverse``, the choices are
ordered according to their value as if each comparison were reversed. If
``natural``, the order is the one used when instantiating the
enumeration.

``enums.EAutoChoice``
^^^^^^^^^^^^^^^^^^^^^

Generates auto-incremented numeric values. It's then used like:

::

    from echoices.enums import EAutoChoice

    class EStates(EAutoChoice):
        # format is: label -> str
        CREATED = 'Created'
        SUBMITTED = 'Submitted'

API
^^^

Overriden EnumMeta methods
''''''''''''''''''''''''''

-  ``EChoice.__getitem__()``, such that you can retrieve an ``EChoice``
   instance using ``EChoice['my_value']``

Additional classmethods
'''''''''''''''''''''''

-  ``choices()`` generates the choices as expected by a Django model
   field
-  ``max_value_length()`` returns the max length for the Django model
   field, if the values are strings
-  ``values()`` returns a list of all the values
-  ``get(value, default=None)`` returns the EChoice instance having that
   value, else returns the default

Specialized model fields
~~~~~~~~~~~~~~~~~~~~~~~~

``fields.EChoiceField`` via ``fields.make_echoicefield()``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Deal directly with the enum instances instead of their DB storage value.
The specialized field will be derived from a ``models.Field`` subclass,
the internal representation is deduced from the value type. So for
example if the values are strings, then the the ``EChoiceField`` will
subclass ``models.CharField``; and if the values are integers then it
will be ``models.IntegerField``. Actually supports ``str``, ``int``,
``float`` and (non-null) ``bool`` as enum values.

``make_echoicefield()`` will return an instance of ``EChoiceField``
which subclasses a field type from ``models.CharField``. The exact name
of the field type will be ``MyEnumNameField`` in Django >= 1.9, note the
suffixed 'Field'. For earlier versions of Django, it will be
``EChoiceField``.

Thus, ``MyModel.my_echoice_field`` will be an ``EChoice`` instance
stored in an ``EChoiceField`` field.

Migrations
''''''''''

Since the field is generated with the help of a factory function, it
does not exist as is as a field class in ``echoices.fields``. But, when
generating a migration file, Django will set the class of the field as
the resulting class from ``make_echoicefield()``, which does not exist
in ``echoices.fields``. This will cause the Django server to crash, as
an
``AttributeError: module 'echoices.fields' has no attribute 'MyEnumNameField'``
exception will be raised.

To prevent this, you have to edit the migration file and replace the
instantiation of the non-existing class with a call to
``make_echoicefield()``, with the same parameters as when defining the
field in your model.

For example, assume you have the following model defined in
``models.py``:

::

    from django.db import models
    from echoices.fields import make_echoicefield

    class MyModel(models.Model):
        state = make_echoicefield(EStates, default=EStates.CREATED)

Then you would replace the generated field instantiation statement in
``migrations/0001_initial.py``

::

    migrations.CreateModel(
        name='MyModel',
        fields=[
            # Replace the statement below
            ('state', echoices.fields.EStatesField(
                            echoices=app.models.EStates,
                            default=app.models.EStates(1))
            ),
        ],

with

::

            ('state', echoices.fields.make_echoicefield(
                            echoices=app.models.EStates,
                            default=app.models.EStates.CREATED)
            ),

``fields.MultipleEChoiceField``
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Similar to previous fields, but supports multiple values to be selected.
`**Not yet implemented** <#3>`__.

Usage in templates
~~~~~~~~~~~~~~~~~~

Assume a ``Context(dict(estates=myapp.models.EStates))`` is provided to
the following templates.

-  Fields of the ``EChoice`` can be accessed in the templates as:

   ::

       {{ estates.CREATED.value }}
       {{ estates.CREATED.label }}

-  ``EChoice`` can also be enumerated:

   ::

       {% for state in estates %}
           {{ state.value }}
           {{ state.label }}
       {% endfor %}

.. |Python| image:: https://img.shields.io/badge/Python-3.4,3.5,3.6-blue.svg?style=flat-square
   :target: /
.. |Django| image:: https://img.shields.io/badge/Django-1.9,1.10,1.11-blue.svg?style=flat-square
   :target: /
.. |License| image:: https://img.shields.io/badge/License-GPLv3-blue.svg?style=flat-square
   :target: /LICENSE
.. |PyPI| image:: https://img.shields.io/pypi/v/django_echoices.svg?style=flat-square
   :target: https://pypi.python.org/pypi/django-echoices
.. |Build Status| image:: https://travis-ci.org/mbourqui/django-echoices.svg?branch=master
   :target: https://travis-ci.org/mbourqui/django-echoices
.. |Coverage Status| image:: https://coveralls.io/repos/github/mbourqui/django-echoices/badge.svg?branch=master
   :target: https://coveralls.io/github/mbourqui/django-echoices?branch=master


