├── .gitignore ├── LICENSE.md ├── README.md ├── __init__.py ├── drf_enum_field ├── __init__.py ├── fields.py └── serializers.py ├── requirements.txt ├── setup.py └── tests ├── __init__.py ├── manage.py └── testproject ├── __init__.py ├── migrations ├── 0001_initial.py └── __init__.py ├── models.py ├── serializers.py ├── settings.py ├── tests.py ├── urls.py └── views.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | 59 | **/.idea/* 60 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Sebastian Bredehöft 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | drf-enum-field 2 | ============== 3 | Extension for Django REST Framework 3 which allows for using EnumField from https://github.com/hzdg/django-enumfields. 4 | 5 | ## Setup ## 6 | 7 | pip install drf-enum-field 8 | 9 | ## Requirements ## 10 | 11 | * Python 2.7+ 12 | * Django 1.6+ 13 | * Django REST Framework 3.9 14 | * django-enumfields 0.9+ 15 | 16 | ## Features ## 17 | 18 | By using the **EnumFieldSerializerMixin** EnumFields are serialized and deserialized correctly. 19 | 20 | ## Example ## 21 | 22 | ### Model ### 23 | 24 | class TestResource(models.Model): 25 | class Type(Enum): 26 | DEFAULT = 'DEFAULT' 27 | NON_DEFAULT = 'NON_DEFAULT' 28 | 29 | type = EnumField(Type) 30 | name = models.CharField(max_length=255) 31 | 32 | ### Serializer ### 33 | 34 | class TestResourceSerializer(EnumFieldSerializerMixin, ModelSerializer): 35 | class Meta: 36 | model = TestResource 37 | 38 | ### View ### 39 | 40 | class TestResourceViewSet(ModelViewSet): 41 | serializer_class = TestResourceSerializer 42 | queryset = TestResource.objects.all() 43 | 44 | ### Result ### 45 | 46 | { 47 | "id": 1, 48 | "name": "Test-Resource", 49 | "type": "DEFAULT" 50 | } 51 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /drf_enum_field/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /drf_enum_field/fields.py: -------------------------------------------------------------------------------- 1 | from django.utils.translation import ugettext_lazy as _ 2 | from rest_framework.fields import ChoiceField 3 | 4 | 5 | class EnumField(ChoiceField): 6 | default_error_messages = { 7 | 'invalid': _("No matching enum type.") 8 | } 9 | 10 | def __init__(self, **kwargs): 11 | self.enum_type = kwargs.pop("enum_type") 12 | kwargs.pop("choices", None) 13 | super(EnumField, self).__init__(self.enum_type.choices(), **kwargs) 14 | 15 | def to_internal_value(self, data): 16 | for choice in self.enum_type: 17 | if choice.name == data or choice.value == data: 18 | return choice 19 | self.fail("invalid") 20 | 21 | def to_representation(self, value): 22 | # this can be either an enum member or a "choice", i.e. a str 23 | if hasattr(value, "value"): 24 | return value.value 25 | return value 26 | -------------------------------------------------------------------------------- /drf_enum_field/serializers.py: -------------------------------------------------------------------------------- 1 | from enumfields.fields import EnumFieldMixin 2 | from rest_framework.fields import ChoiceField 3 | 4 | from drf_enum_field.fields import EnumField as RestEnumField 5 | 6 | 7 | class EnumFieldSerializerMixin(object): 8 | def build_standard_field(self, field_name, model_field): 9 | field_class, field_kwargs = super(EnumFieldSerializerMixin, self).build_standard_field(field_name, model_field) 10 | if field_class == ChoiceField and isinstance(model_field, EnumFieldMixin): 11 | field_class = RestEnumField 12 | field_kwargs['enum_type'] = model_field.enum 13 | return field_class, field_kwargs 14 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django==2.2.2 2 | djangorestframework==3.9.4 3 | django-enumfields==0.9.2 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from setuptools import setup, find_packages 3 | 4 | setup( 5 | name='drf-enum-field', 6 | version="0.9.3", 7 | url='https://github.com/seebass/drf-enum-field', 8 | license='MIT', 9 | description='EnumField support for Django REST Framework 3', 10 | author='Sebastian Bredehöft', 11 | author_email='bredehoeft.sebastian@gmail.com', 12 | packages=find_packages(exclude=['tests*']), 13 | install_requires=[ 14 | 'django>=1.6', 15 | 'djangorestframework>=3.0.0', 16 | 'django-enumfields>=0.7.0' 17 | ], 18 | zip_safe=False, 19 | classifiers=[ 20 | 'Development Status :: 4 - Beta', 21 | 'Environment :: Web Environment', 22 | 'Framework :: Django', 23 | 'Intended Audience :: Developers', 24 | 'License :: OSI Approved :: MIT License', 25 | 'Operating System :: OS Independent', 26 | 'Programming Language :: Python', 27 | 'Programming Language :: Python :: 3', 28 | 'Topic :: Internet :: WWW/HTTP', 29 | ] 30 | ) 31 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dimm.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /tests/testproject/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/testproject/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | import enumfields.fields 6 | from ..models import TestResource 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='TestResource', 17 | fields=[ 18 | ('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)), 19 | ('type', enumfields.fields.EnumField(max_length=10, enum=TestResource.Type)), 20 | ('name', models.CharField(max_length=255)), 21 | ], 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /tests/testproject/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/testproject/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from enumfields import Enum, EnumField 3 | 4 | 5 | class TestResource(models.Model): 6 | class Type(Enum): 7 | DEFAULT = 'DEFAULT' 8 | NON_DEFAULT = 'NON_DEFAULT' 9 | 10 | type = EnumField(Type) 11 | name = models.CharField(max_length=255) 12 | -------------------------------------------------------------------------------- /tests/testproject/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework.serializers import ModelSerializer 2 | from drf_enum_field.serializers import EnumFieldSerializerMixin 3 | from .models import TestResource 4 | 5 | 6 | class TestResourceSerializer(EnumFieldSerializerMixin, ModelSerializer): 7 | class Meta: 8 | model = TestResource 9 | -------------------------------------------------------------------------------- /tests/testproject/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | DEBUG = True 4 | TEMPLATE_DEBUG = True 5 | 6 | TIME_ZONE = 'Europe/Berlin' 7 | 8 | INSTALLED_APPS = ( 9 | 'django.contrib.admin', 10 | 'django.contrib.auth', 11 | 'django.contrib.contenttypes', 12 | 'django.contrib.sessions', 13 | 'django.contrib.messages', 14 | 'django.contrib.staticfiles', 15 | 'django.contrib.admindocs', 16 | 'rest_framework', 17 | 'testproject' 18 | ) 19 | 20 | ROOT_URLCONF = 'testproject.urls' 21 | 22 | SECRET_KEY = '9q7324#45RWtw843q$%&/' 23 | 24 | DATABASES = { 25 | 'default': { 26 | 'ENGINE': 'django.db.backends.sqlite3', 27 | 'NAME': os.path.join(os.path.dirname(os.path.dirname(__file__)), 'db.sqlite3'), 28 | } 29 | } 30 | 31 | MIDDLEWARE_CLASSES = ( 32 | 'django.middleware.common.CommonMiddleware', 33 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 34 | ) 35 | 36 | REST_FRAMEWORK = { 37 | 'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.AllowAny'], 38 | 'DEFAULT_RENDERER_CLASSES': ['rest_framework.renderers.JSONRenderer'], 39 | } 40 | 41 | STATIC_URL = '/static/' 42 | -------------------------------------------------------------------------------- /tests/testproject/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | from .models import TestResource 4 | 5 | 6 | class NestedFieldsTest(TestCase): 7 | def setUp(self): 8 | self.test_resource = TestResource.objects.create(name="Test-Resource", type=TestResource.Type.DEFAULT) 9 | 10 | def testGetResourceWithEnumField(self): 11 | resp = self.client.get("/test-resources/") 12 | self.assertEqual(200, resp.status_code, resp.content) 13 | self.assertEqual(1, len(resp.data)) 14 | self.assertEqual(self.test_resource.name, resp.data[0]["name"]) 15 | self.assertEqual(self.test_resource.type.value, resp.data[0]["type"]) 16 | 17 | -------------------------------------------------------------------------------- /tests/testproject/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import patterns, url, include 2 | from django.contrib import admin 3 | from rest_framework.routers import SimpleRouter 4 | from .views import TestResourceViewSet 5 | 6 | admin.autodiscover() 7 | 8 | router = SimpleRouter() 9 | router.register(r'test-resources', TestResourceViewSet) 10 | 11 | urlpatterns = patterns( 12 | '', 13 | url(r'', include(router.urls)), 14 | ) 15 | -------------------------------------------------------------------------------- /tests/testproject/views.py: -------------------------------------------------------------------------------- 1 | from rest_framework.viewsets import ModelViewSet 2 | 3 | from .models import TestResource 4 | from .serializers import TestResourceSerializer 5 | 6 | 7 | class TestResourceViewSet(ModelViewSet): 8 | serializer_class = TestResourceSerializer 9 | queryset = TestResource.objects.all() 10 | --------------------------------------------------------------------------------