{"id":22308499,"url":"https://github.com/mbourqui/django-echoices","last_synced_at":"2025-07-29T06:30:36.306Z","repository":{"id":47671459,"uuid":"88984217","full_name":"mbourqui/django-echoices","owner":"mbourqui","description":"Django model choices as Enum","archived":false,"fork":false,"pushed_at":"2021-08-18T17:33:03.000Z","size":149,"stargazers_count":3,"open_issues_count":9,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-26T13:48:40.918Z","etag":null,"topics":["django","django-models","enum","python","python3"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mbourqui.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-04-21T13:01:14.000Z","updated_at":"2021-08-18T17:30:15.000Z","dependencies_parsed_at":"2022-08-22T03:41:21.791Z","dependency_job_id":null,"html_url":"https://github.com/mbourqui/django-echoices","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/mbourqui/django-echoices","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbourqui%2Fdjango-echoices","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbourqui%2Fdjango-echoices/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbourqui%2Fdjango-echoices/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbourqui%2Fdjango-echoices/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mbourqui","download_url":"https://codeload.github.com/mbourqui/django-echoices/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mbourqui%2Fdjango-echoices/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267639569,"owners_count":24119780,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-29T02:00:12.549Z","response_time":2574,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["django","django-models","enum","python","python3"],"created_at":"2024-12-03T20:14:12.439Z","updated_at":"2025-07-29T06:30:36.025Z","avatar_url":"https://github.com/mbourqui.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Python](https://img.shields.io/badge/Python-3.5,3.6,3.7,3.8-blue.svg?style=flat-square)](/)\n[![Django](https://img.shields.io/badge/Django-1.11,2.2,3.0-blue.svg?style=flat-square)](/)\n[![License](https://img.shields.io/badge/License-GPLv3-blue.svg?style=flat-square)](/LICENSE)\n[![PyPI](https://img.shields.io/pypi/v/django_echoices.svg?style=flat-square)](https://pypi.python.org/pypi/django-echoices)\n[![Build Status](https://travis-ci.org/mbourqui/django-echoices.svg?branch=master)](https://travis-ci.org/mbourqui/django-echoices)\n[![Coverage Status](https://coveralls.io/repos/github/mbourqui/django-echoices/badge.svg?branch=master)](https://coveralls.io/github/mbourqui/django-echoices?branch=master)\n\n\n# Django-EChoices\n\nChoices for Django model fields as enumeration\n\n\n## Features\n\n* Specialized [enum types](#enum)\n* Specialized [model fields](#modelfield)\n* Accessible in [templates](#templates)\n\n\n## Requirements\n\n* [Python][] \u003e= 3.5\n* [Django][] \u003e= 1.11.29\n\n\n## Installation\n\n### Using [PyPI](https://pypi.python.org/pypi/django-echoices)\n1. Run `pip install django-echoices`.\n\n### Using the source code\n1. Make sure [`pandoc`](http://pandoc.org/index.html) is installed\n1. Run `./pypi_packager.sh`\n1. Run `pip install dist/django_echoices-x.y.z-[...].wheel`, where `x.y.z` must be replaced by the actual\n   version number and `[...]` depends on your packaging configuration\n\n\n## Usage\n\n### Enumeration\nFirst, define your choices enumeration (in your `models.py` for example):\n```\nfrom echoices.enums import EChoice\n\nclass EStates(EChoice):\n    # format is: (value -\u003e char or str or int, label -\u003e str)\n    CREATED = ('c', 'Created')\n    SUBMITTED = ('s', 'Submitted')\n\n```\n\n### Model field\n#### Regular model field\nThen, either use a regular model field:\n```\nfrom django.db import models\n\nclass MyModel(models.Model):\n    state = models.CharField(max_length=EStates.max_value_length(),\n                             choices=EStates.choices(),\n                             default=EStates.CREATED.value)\n```\n\n**Note**: If your value is an `int`, you can use `models.IntegerField` instead.\n\n#### Specialized field\nYou can also use specialized field. Using such a field, you will then only handle `Echoice` instances.\n```\nfrom django.db import models\nfrom echoices.fields import make_echoicefield\n\nclass MyModel(models.Model):\n    # `max_length` is set automatically\n    state = make_echoicefield(EStates, default=EStates.CREATED)\n```\n\n**Note**: `MyModel.state` will be `Estates` instance stored in a `EStatesField` field. See [documentation](#modelfield)\nfor more details.\n\n**WARNING**: requires special handling of migrations. Read more in the [documentation](#migrations).\n\n### Derivation\n\nYou can add your own fields to the `value` and `label` ones. To do so, you have to override the __init__() and your\nsignature must look like: `self, value, label, *args` where you replace `*args` with your own positional arguments, as\nyou would do when defining a custom Enum. Do *not* call the super().__init__(), as `value` and `label` are already set\ninternally by `EChoice`.\n\nAs when dealing with a derived Enum, you can also add your own methods.\n```\nfrom echoices.enums import EChoice\n\nclass EMyChoices(EChoice):\n    \"\"\"Another variant of EChoice with additionnal content\"\"\"\n\n    MY_CHOICE = (1, 'First choice', 'my additional value')\n\n    def __init__(self, value, label, my_arg):\n        self.my_arg = my_arg\n        # Note: super().__init__() shall *not* be called!\n\n    def show_myarg(self):\n        \"\"\"Used as: EMyChoices.MY_CHOICE.show_myarg()\"\"\"\n        print(self.my_arg)\n\n    @classmethod\n    def show_all(cls):\n        \"\"\"Used as: EMyChoices.show_all()\"\"\"\n        print(\", \".join([e.my_arg for e in list(cls)]))\n```\n\n### In templates\nAssume a `Context(dict(estates=myapp.models.EStates))` is provided to the following templates.\n\n* Fields of the `EChoice` can be accessed in the templates as:\n    ```\n    {{ estates.CREATED.value }}\n    {{ estates.CREATED.label }}\n    ```\n\n* `EChoice` can also be enumerated:\n    ```\n    {% for state in estates %}\n        {{ state.value }}\n        {{ state.label }}\n    {% endfor %}\n    ```\n\n## Short documentation\n\n### \u003ca name=\"enum\"\u003e\u003c/a\u003eSpecialized enum types\n\n#### `enums.EChoice`\nBase enum type. Each enum element is a tuple `(value, label)`, where \u003ccite\u003e[t]he first element\nin each tuple is the actual value to be set on the model, and the second element is the human-readable name\u003c/cite\u003e\u0026nbsp;\n\u003csup\u003e[doc](https://docs.djangoproject.com/en/1.11/ref/models/fields/#choices)\u003c/sup\u003e. Values **must** be unique. Can be\nderived for further customization.\n\n#### `enums.EOrderedChoice`\nSupports ordering of elements. `EOrderedChoice.choices()` takes an extra optional argument,\n`order`, which supports three values: 'sorted', 'reverse' or 'natural' (default). If `sorted`, the choices are ordered\naccording to their value. If `reverse`, the choices are ordered according to their value as if each comparison were\nreversed. If `natural`, the order is the one used when instantiating the enumeration.\n\n#### `enums.EAutoChoice`\nGenerates auto-incremented numeric values. It's then used like:\n```\nfrom echoices.enums import EAutoChoice\n\nclass EStates(EAutoChoice):\n    # format is: label -\u003e str\n    CREATED = 'Created'\n    SUBMITTED = 'Submitted'\n```\n\n#### API\n##### Overriden EnumMeta methods\n* `EChoice.__getitem__()`, such that you can retrieve an `EChoice` instance using `EChoice['my_value']`\n\n##### Additional classmethods\n* `choices()` generates the choices as expected by a Django model field\n* `max_value_length()` returns the max length for the Django model field, if the values are strings\n* `values()` returns a list of all the values\n* `get(value, default=None)` returns the EChoice instance having that value, else returns the default\n\n### \u003ca name=\"modelfield\"\u003e\u003c/a\u003eSpecialized model fields\n\n#### `fields.EChoiceField` via `fields.make_echoicefield()`\nDeal directly with the enum instances instead of their DB storage value. The specialized field will be derived from a\n`models.Field` subclass, the internal representation is deduced from the value type. So for example if the values are\nstrings, then the the `EChoiceField` will subclass `models.CharField`; and if the values are integers then it will be\n`models.IntegerField`. Actually supports `str`, `int`, `float` and (non-null) `bool` as enum values.\n\n`make_echoicefield()` will return an instance of `EChoiceField` which subclasses a field type from `models.CharField`.\nThe exact name of the field type will be `MyEnumNameField` in Django \u003e= 1.9, note the suffixed 'Field'. For earlier\nversions of Django, it will be `EChoiceField`.\n\nThus, `MyModel.my_echoice_field` will be an `EChoice` instance stored in an `EChoiceField` field.\n\n##### \u003ca name=\"migrations\"\u003e\u003c/a\u003eMigrations\nSince the field is generated with the help of a factory function, it does not exist as is as a field class in\n`echoices.fields`. But, when generating a migration file, Django will set the class of the field as the resulting class\nfrom `make_echoicefield()`, which does not exist in `echoices.fields`. This will cause the Django server to crash, as\nan `AttributeError: module 'echoices.fields' has no attribute 'MyEnumNameField'` exception will be raised.\n\nTo prevent this, you have to edit the migration file and replace the instantiation of the non-existing class with a call\nto `make_echoicefield()`, with the same parameters as when defining the field in your model.\n\nFor example, assume you have the following model defined in `models.py`:\n```\nfrom django.db import models\nfrom echoices.fields import make_echoicefield\n\nclass MyModel(models.Model):\n    state = make_echoicefield(EStates, default=EStates.CREATED)\n```\n\nThen you would replace the generated field instantiation statement in `migrations/0001_initial.py`\n```\nmigrations.CreateModel(\n    name='MyModel',\n    fields=[\n        # Replace the statement below\n        ('state', echoices.fields.EStatesField(\n                        echoices=app.models.EStates,\n                        default=app.models.EStates(1))\n        ),\n    ],\n```\n\nwith\n```\n        ('state', echoices.fields.make_echoicefield(\n                        echoices=app.models.EStates,\n                        default=app.models.EStates.CREATED)\n        ),\n```\n\n#### `fields.MultipleEChoiceField`\nSimilar to previous fields, but supports multiple values to be selected.\n[**Not yet implemented**][i03].\n\n### \u003ca name=\"templates\"\u003e\u003c/a\u003eUsage in templates\nAssume a `Context(dict(estates=myapp.models.EStates))` is provided to the following templates.\n\n* Fields of the `EChoice` can be accessed in the templates as:\n    ```\n    {{ estates.CREATED.value }}\n    {{ estates.CREATED.label }}\n    ```\n\n* `EChoice` can also be enumerated:\n    ```\n    {% for state in estates %}\n        {{ state.value }}\n        {{ state.label }}\n    {% endfor %}\n    ```\n\n  [django]:     https://www.djangoproject.com/      \"Django\"\n  [python]:     https://www.python.org/             \"Python\"\n  [i03]:        https://github.com/mbourqui/django-echoices/issues/3","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmbourqui%2Fdjango-echoices","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmbourqui%2Fdjango-echoices","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmbourqui%2Fdjango-echoices/lists"}