# django-enumfield
Provides an enumeration Django model field (using `IntegerField`) with reusable enums and transition validation.
[![Build Status](https://github.com/5monkeys/django-enumfield/workflows/Test/badge.svg)](https://github.com/5monkeys/django-enumfield/actions)
[![PyPi Version](https://img.shields.io/pypi/v/django-enumfield.svg)](https://pypi.python.org/pypi/django-enumfield)
[![License](https://img.shields.io/pypi/l/django-enumfield.svg)](https://pypi.python.org/pypi/django-enumfield)
[![Python Versions](https://img.shields.io/pypi/pyversions/django-enumfield.svg)](https://pypi.python.org/pypi/django-enumfield)
[![Wheel](https://img.shields.io/pypi/wheel/django-enumfield.svg)](https://pypi.python.org/pypi/django-enumfield)
![Coveralls github](https://img.shields.io/coveralls/github/5monkeys/django-enumfield)
Installation
------------
Currently, [we test](https://github.com/5monkeys/django-enumfield/actions) Django versions 2.2-4.1 and Python versions 3.7-3.11.
Install `django-enumfield` in your Python environment:
```sh
$ pip install django-enumfield
```
**Upgrading from django-enumfield 1.x?** [See the migration guide](docs/migrate-to-20.md)
For use with Django versions prior to 1.8 use version
[`1.2.1`](https://github.com/5monkeys/django-enumfield/tree/1.2.1)
For use with Django versions prior to 1.11 use version
[`1.5`](https://github.com/5monkeys/django-enumfield/tree/1.5)
Usage
-----
Create an `Enum`-class and pass it as first argument to the Django model `EnumField`.
```python
from django.db import models
from django_enumfield import enum
class BeerStyle(enum.Enum):
LAGER = 0
STOUT = 1
WEISSBIER = 2
class Beer(models.Model):
style = enum.EnumField(BeerStyle, default=BeerStyle.LAGER)
# Use .get to get enum values from either name or ints
print(BeerStyle.get("LAGER")) # <BeerStyle.LAGER: 0>
print(BeerStyle.get(1)) # <BeerStyle.STOUT: 1>
print(BeerStyle.get(BeerStyle.WEISSBIER)) # <BeerStyle.WEISSBIER: 2>
# It's also possible to use the normal enum way to get the value
print(BeerStyle(1)) # <BeerStyle.STOUT: 1>
print(BeerStyle["LAGER"]) # <BeerStyle.LAGER: 0>
# The enum value has easy access to their value and name
print(BeerStyle.LAGER.value) # 0
print(BeerStyle.LAGER.name) # "LAGER"
```
For more information about Python 3 enums
(which our `Enum` inherits, `IntEnum` to be specific)
checkout the [docs](https://docs.python.org/3/library/enum.html).
### Setting the default value
You can also set default value on your enum class using `__default__`
attribute
```python
from django.db import models
from django_enumfield import enum
class BeerStyle(enum.Enum):
LAGER = 0
STOUT = 1
WEISSBIER = 2
__default__ = LAGER
class BeerStyleNoDefault(enum.Enum):
LAGER = 0
class Beer(models.Model):
style_default_lager = enum.EnumField(BeerStyle)
style_default_stout = enum.EnumField(BeerStyle, default=BeerStyle.STOUT)
style_default_null = enum.EnumField(BeerStyleNoDefault, null=True, blank=True)
# When you set __default__ attribute, you can access default value via
# `.default()` method of your enum class
assert BeerStyle.default() == BeerStyle.LAGER
beer = Beer.objects.create()
assert beer.style_default_larger == BeerStyle.LAGER
assert beer.style_default_stout == BeerStyle.STOUT
assert beer.style_default_null is None
```
### Labels
You can use your own labels for `Enum` items
```python
from django.utils.translation import gettext_lazy
from django_enumfield import enum
class Animals(enum.Enum):
CAT = 1
DOG = 2
SHARK = 3
__labels__ = {
CAT: gettext_lazy("Cat"),
DOG: gettext_lazy("Dog"),
}
print(Animals.CAT.label) # "Cat"
print(Animals.SHARK.label) # "SHARK"
# There's also classmethods for getting the label
print(Animals.get_label(2)) # "Dog"
print(Animals.get_label("DOG")) # "Dog"
```
### Validate transitions
The `Enum`-class provides the possibility to use transition validation.
```python
from django.db import models
from django_enumfield import enum
from django_enumfield.exceptions import InvalidStatusOperationError
class PersonStatus(enum.Enum):
ALIVE = 1
DEAD = 2
REANIMATED = 3
__transitions__ = {
DEAD: (ALIVE,), # Can go from ALIVE to DEAD
REANIMATED: (DEAD,) # Can go from DEAD to REANIMATED
}
class Person(models.Model):
status = enum.EnumField(PersonStatus)
# These transitions state that a PersonStatus can only go to DEAD from ALIVE and to REANIMATED from DEAD.
person = Person.objects.create(status=PersonStatus.ALIVE)
try:
person.status = PersonStatus.REANIMATED
except InvalidStatusOperationError:
print("Person status can not go from ALIVE to REANIMATED")
else:
# All good
person.save()
```
### In forms
The `Enum`-class can also be used without the `EnumField`. This is very useful in Django form `ChoiceField`s.
```python
from django import forms
from django_enumfield import enum
from django_enumfield.forms.fields import EnumChoiceField
class GenderEnum(enum.Enum):
MALE = 1
FEMALE = 2
__labels__ = {
MALE: "Male",
FEMALE: "Female",
}
class PersonForm(forms.Form):
gender = EnumChoiceField(GenderEnum)
```
Rendering `PersonForm` in a template will generate a select-box with "Male" and "Female" as option labels for the gender field.
Local Development Environment
-----------------------------
Make sure black and isort is installed in your env with `pip install -e .[dev]`.
Before committing run `make format` to apply black and isort to all files.
Raw data
{
"_id": null,
"home_page": "http://github.com/5monkeys/django-enumfield",
"name": "django-enumfield",
"maintainer": "",
"docs_url": null,
"requires_python": "",
"maintainer_email": "",
"keywords": "django,enum,field,status,state,choices,form,model",
"author": "Hannes Ljungberg",
"author_email": "hannes@5monkeys.se",
"download_url": "https://files.pythonhosted.org/packages/11/1b/5ddfea406fbae8d32115f9f51241de76966b28f0ac91efb90badfac1ac97/django-enumfield-3.1.tar.gz",
"platform": "any",
"description": "# django-enumfield\n\nProvides an enumeration Django model field (using `IntegerField`) with reusable enums and transition validation.\n\n[![Build Status](https://github.com/5monkeys/django-enumfield/workflows/Test/badge.svg)](https://github.com/5monkeys/django-enumfield/actions)\n[![PyPi Version](https://img.shields.io/pypi/v/django-enumfield.svg)](https://pypi.python.org/pypi/django-enumfield)\n[![License](https://img.shields.io/pypi/l/django-enumfield.svg)](https://pypi.python.org/pypi/django-enumfield)\n[![Python Versions](https://img.shields.io/pypi/pyversions/django-enumfield.svg)](https://pypi.python.org/pypi/django-enumfield)\n[![Wheel](https://img.shields.io/pypi/wheel/django-enumfield.svg)](https://pypi.python.org/pypi/django-enumfield)\n![Coveralls github](https://img.shields.io/coveralls/github/5monkeys/django-enumfield)\n\nInstallation\n------------\n\nCurrently, [we test](https://github.com/5monkeys/django-enumfield/actions) Django versions 2.2-4.1 and Python versions 3.7-3.11.\n\nInstall `django-enumfield` in your Python environment:\n\n```sh\n$ pip install django-enumfield\n```\n\n**Upgrading from django-enumfield 1.x?** [See the migration guide](docs/migrate-to-20.md)\n\nFor use with Django versions prior to 1.8 use version\n[`1.2.1`](https://github.com/5monkeys/django-enumfield/tree/1.2.1)\n\nFor use with Django versions prior to 1.11 use version\n[`1.5`](https://github.com/5monkeys/django-enumfield/tree/1.5)\n\nUsage\n-----\n\nCreate an `Enum`-class and pass it as first argument to the Django model `EnumField`.\n\n```python\nfrom django.db import models\nfrom django_enumfield import enum\n\n\nclass BeerStyle(enum.Enum):\n LAGER = 0\n STOUT = 1\n WEISSBIER = 2\n\n\nclass Beer(models.Model):\n style = enum.EnumField(BeerStyle, default=BeerStyle.LAGER)\n\n\n# Use .get to get enum values from either name or ints\nprint(BeerStyle.get(\"LAGER\")) # <BeerStyle.LAGER: 0>\nprint(BeerStyle.get(1)) # <BeerStyle.STOUT: 1>\nprint(BeerStyle.get(BeerStyle.WEISSBIER)) # <BeerStyle.WEISSBIER: 2>\n\n# It's also possible to use the normal enum way to get the value\nprint(BeerStyle(1)) # <BeerStyle.STOUT: 1>\nprint(BeerStyle[\"LAGER\"]) # <BeerStyle.LAGER: 0>\n\n# The enum value has easy access to their value and name\nprint(BeerStyle.LAGER.value) # 0\nprint(BeerStyle.LAGER.name) # \"LAGER\"\n```\n\nFor more information about Python 3 enums\n(which our `Enum` inherits, `IntEnum` to be specific)\ncheckout the [docs](https://docs.python.org/3/library/enum.html).\n\n\n### Setting the default value\n\nYou can also set default value on your enum class using `__default__`\nattribute\n\n```python\nfrom django.db import models\nfrom django_enumfield import enum\n\n\nclass BeerStyle(enum.Enum):\n LAGER = 0\n STOUT = 1\n WEISSBIER = 2\n\n __default__ = LAGER\n\n\nclass BeerStyleNoDefault(enum.Enum):\n LAGER = 0\n\n\nclass Beer(models.Model):\n style_default_lager = enum.EnumField(BeerStyle)\n style_default_stout = enum.EnumField(BeerStyle, default=BeerStyle.STOUT)\n style_default_null = enum.EnumField(BeerStyleNoDefault, null=True, blank=True)\n\n\n# When you set __default__ attribute, you can access default value via\n# `.default()` method of your enum class\nassert BeerStyle.default() == BeerStyle.LAGER\n\nbeer = Beer.objects.create()\nassert beer.style_default_larger == BeerStyle.LAGER\nassert beer.style_default_stout == BeerStyle.STOUT\nassert beer.style_default_null is None\n```\n\n### Labels\n\nYou can use your own labels for `Enum` items\n\n```python\nfrom django.utils.translation import gettext_lazy\nfrom django_enumfield import enum\n\n\nclass Animals(enum.Enum):\n CAT = 1\n DOG = 2\n SHARK = 3\n\n __labels__ = {\n CAT: gettext_lazy(\"Cat\"),\n DOG: gettext_lazy(\"Dog\"),\n }\n\n\nprint(Animals.CAT.label) # \"Cat\"\nprint(Animals.SHARK.label) # \"SHARK\"\n\n# There's also classmethods for getting the label\nprint(Animals.get_label(2)) # \"Dog\"\nprint(Animals.get_label(\"DOG\")) # \"Dog\"\n```\n\n### Validate transitions\n\nThe `Enum`-class provides the possibility to use transition validation.\n\n```python\nfrom django.db import models\nfrom django_enumfield import enum\nfrom django_enumfield.exceptions import InvalidStatusOperationError\n\n\nclass PersonStatus(enum.Enum):\n ALIVE = 1\n DEAD = 2\n REANIMATED = 3\n\n __transitions__ = {\n DEAD: (ALIVE,), # Can go from ALIVE to DEAD\n REANIMATED: (DEAD,) # Can go from DEAD to REANIMATED\n }\n\n\nclass Person(models.Model):\n status = enum.EnumField(PersonStatus)\n\n# These transitions state that a PersonStatus can only go to DEAD from ALIVE and to REANIMATED from DEAD.\nperson = Person.objects.create(status=PersonStatus.ALIVE)\ntry:\n person.status = PersonStatus.REANIMATED\nexcept InvalidStatusOperationError:\n print(\"Person status can not go from ALIVE to REANIMATED\")\nelse:\n # All good\n person.save()\n```\n\n### In forms\n\nThe `Enum`-class can also be used without the `EnumField`. This is very useful in Django form `ChoiceField`s.\n\n```python\nfrom django import forms\nfrom django_enumfield import enum\nfrom django_enumfield.forms.fields import EnumChoiceField\n\n\nclass GenderEnum(enum.Enum):\n MALE = 1\n FEMALE = 2\n\n __labels__ = {\n MALE: \"Male\",\n FEMALE: \"Female\",\n }\n\n\nclass PersonForm(forms.Form):\n gender = EnumChoiceField(GenderEnum)\n```\n\nRendering `PersonForm` in a template will generate a select-box with \"Male\" and \"Female\" as option labels for the gender field.\n\n\nLocal Development Environment\n-----------------------------\n\nMake sure black and isort is installed in your env with `pip install -e .[dev]`.\n\nBefore committing run `make format` to apply black and isort to all files.\n\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Custom Django field for using enumerations of named constants",
"version": "3.1",
"project_urls": {
"Download": "https://github.com/5monkeys/django-enumfield/tarball/3.1",
"Homepage": "http://github.com/5monkeys/django-enumfield"
},
"split_keywords": [
"django",
"enum",
"field",
"status",
"state",
"choices",
"form",
"model"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "1b1b8eb7a8f32db6c1ced36d3f9b6cfb829459adfe0145f91262414594588063",
"md5": "c9647b11d6c23cac451daf4cdb2227f6",
"sha256": "e6a9e14f3a7cfbf15a14f3809117eacd9c0c4257e1730030f22ae07cd1793bbd"
},
"downloads": -1,
"filename": "django_enumfield-3.1-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "c9647b11d6c23cac451daf4cdb2227f6",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": null,
"size": 18043,
"upload_time": "2023-02-27T10:43:27",
"upload_time_iso_8601": "2023-02-27T10:43:27.143055Z",
"url": "https://files.pythonhosted.org/packages/1b/1b/8eb7a8f32db6c1ced36d3f9b6cfb829459adfe0145f91262414594588063/django_enumfield-3.1-py2.py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "111b5ddfea406fbae8d32115f9f51241de76966b28f0ac91efb90badfac1ac97",
"md5": "6d1db76732f4fedffd98f0bfab4a11f3",
"sha256": "abbb361507ad481bb2ef50a86ee4dc9ec770adbbabbe53c1242815defbf81889"
},
"downloads": -1,
"filename": "django-enumfield-3.1.tar.gz",
"has_sig": false,
"md5_digest": "6d1db76732f4fedffd98f0bfab4a11f3",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 19491,
"upload_time": "2023-02-27T10:43:29",
"upload_time_iso_8601": "2023-02-27T10:43:29.251507Z",
"url": "https://files.pythonhosted.org/packages/11/1b/5ddfea406fbae8d32115f9f51241de76966b28f0ac91efb90badfac1ac97/django-enumfield-3.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-02-27 10:43:29",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "5monkeys",
"github_project": "django-enumfield",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "django-enumfield"
}