From de581ea26e31890109da3ae7fa1be3bac0b486d2 Mon Sep 17 00:00:00 2001 From: Mike Fogel Date: Fri, 20 Nov 2020 12:24:18 -0300 Subject: [PATCH] feat: add Django REST Framework serializer field Closes #65 Squashed commit of the following: commit 1009c8864fac63f52c0a2703a5c3dc0a2b7c653b Author: Mike Fogel Date: Fri Nov 20 11:08:00 2020 -0300 feat: add docs commit 7086221648077530eeb2a8675063c3c3b4aa7e1d Author: Mike Fogel Date: Fri Nov 20 11:05:15 2020 -0300 chore: add test, allow for easier imports commit 9b9dc459ba529ca49f23497b94d33fd1a0b41ead Author: starryrbs <1322096624@qq.com> Date: Mon Nov 16 14:45:29 2020 +0800 fix the problem: line too long commit 35064760ffa5a8d4acda3c008d1c43cf2356963b Merge: b425511 58542a9 Author: starryrbs <1322096624@qq.com> Date: Mon Nov 16 14:36:48 2020 +0800 Merge remote-tracking branch 'origin/master' commit b4255119f289bcd3ea0cb82715a5b5b476ab7f42 Author: starryrbs <1322096624@qq.com> Date: Mon Nov 16 14:36:35 2020 +0800 add django_rest_framework TimeZoneField representation test commit 58542a9108c190e580e0f6b78784da81149f5535 Merge: 1f0de71 63b8828 Author: starryrbs <35923714+starryrbs@users.noreply.github.com> Date: Mon Nov 16 14:23:29 2020 +0800 Merge branch 'master' into master commit 1f0de71691370c946b08cbc610606bd35fc54b4b Author: starryrbs <1322096624@qq.com> Date: Mon Nov 16 14:18:44 2020 +0800 add django_rest_framework TimeZoneField test commit c87d38427f5c07b92d79d1bbc442c50aef8c2c2e Author: starryrbs <1322096624@qq.com> Date: Mon Nov 9 17:59:41 2020 +0800 Add django rest framework support --- README.rst | 26 ++++++++++++++++++++++++ setup.py | 3 +++ tests/settings.py | 1 + tests/tests.py | 35 +++++++++++++++++++++++++++++--- timezone_field/__init__.py | 3 ++- timezone_field/rest_framework.py | 19 +++++++++++++++++ tox.ini | 1 + 7 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 timezone_field/rest_framework.py diff --git a/README.rst b/README.rst index c1288ae..35ce743 100644 --- a/README.rst +++ b/README.rst @@ -62,6 +62,28 @@ Form Field repr(tz) # "" +REST Framework Serializer Field +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code:: python + + import pytz + from rest_framework import serializers + from timezone_field import TimeZoneSerializerField + + class MySerializer(serializers.Serializer): + tz1 = TimeZoneSerializerField() + tz2 = TimeZoneSerializerField() + + my_serializer = MySerializer(data={ + 'tz1': 'America/Argentina/Buenos_Aires', + 'tz2': pytz.timezone('America/Argentina/Buenos_Aires'), + }) + my_serializer.is_valid() # true + my_serializer.validated_data['tz1'] # "" + my_serializer.validated_data['tz2'] # "" + + Installation ------------ @@ -84,6 +106,10 @@ Installation Changelog ------------ +* master + + * Add Django REST Framework serializer field + * 4.0 (2019-12-03) * Add support for django 3.0, python 3.8 diff --git a/setup.py b/setup.py index 424302a..2ad0153 100644 --- a/setup.py +++ b/setup.py @@ -36,6 +36,9 @@ def find_version(*file_paths): 'timezone_field', ], install_requires=['django>=2.2', 'pytz'], + extras_require={ + 'rest_framework': ['djangorestframework>=3.0.0'] + }, python_requires='>=3.5', classifiers=[ 'Development Status :: 4 - Beta', diff --git a/tests/settings.py b/tests/settings.py index b54884d..5e3cf30 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -37,6 +37,7 @@ 'django.contrib.staticfiles', 'timezone_field', 'tests', + 'rest_framework' ) MIDDLEWARE_CLASSES = ( diff --git a/tests/tests.py b/tests/tests.py index 0ab2ded..8d67f55 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -7,8 +7,11 @@ from django.db import models from django.db.migrations.writer import MigrationWriter from django.test import TestCase +from rest_framework import serializers -from timezone_field import TimeZoneField, TimeZoneFormField +from timezone_field import ( + TimeZoneField, TimeZoneFormField, TimeZoneSerializerField, +) from timezone_field.utils import add_gmt_offset_to_choices from tests.models import TestModel @@ -81,8 +84,8 @@ def test_default_human_readable_choices_dont_have_underscores(self): class TestFormInvalidChoice(forms.Form): tz = TimeZoneFormField( choices=( - [(tz, tz) for tz in pytz.all_timezones] + - [(INVALID_TZ, pytz.UTC)] + [(tz, tz) for tz in pytz.all_timezones] + + [(INVALID_TZ, pytz.UTC)] ) ) @@ -453,3 +456,29 @@ def test_add_gmt_offset_to_choices(self): ] for i in range(len(expected)): self.assertEqual(expected[i], result[i][1]) + + +class TimeZoneSerializer(serializers.Serializer): + tz = TimeZoneSerializerField() + + +class TimeZoneSerializerFieldTestCase(TestCase): + def test_invalid_str(self): + serializer = TimeZoneSerializer(data={'tz': INVALID_TZ}) + self.assertFalse(serializer.is_valid()) + + def test_valid(self): + serializer = TimeZoneSerializer(data={'tz': PST}) + self.assertTrue(serializer.is_valid()) + self.assertEqual(serializer.validated_data['tz'], PST_tz) + + def test_valid_representation(self): + serializer = TimeZoneSerializer(data={'tz': PST}) + self.assertTrue(serializer.is_valid()) + self.assertEqual(serializer.data['tz'], PST) + + def test_valid_with_timezone_object(self): + serializer = TimeZoneSerializer(data={'tz': PST_tz}) + self.assertTrue(serializer.is_valid()) + self.assertEqual(serializer.data['tz'], PST) + self.assertEqual(serializer.validated_data['tz'], PST_tz) diff --git a/timezone_field/__init__.py b/timezone_field/__init__.py index 758bd38..09ad242 100644 --- a/timezone_field/__init__.py +++ b/timezone_field/__init__.py @@ -1,5 +1,6 @@ from timezone_field.fields import TimeZoneField from timezone_field.forms import TimeZoneFormField +from timezone_field.rest_framework import TimeZoneSerializerField __version__ = '4.0' -__all__ = ['TimeZoneField', 'TimeZoneFormField'] +__all__ = ['TimeZoneField', 'TimeZoneFormField', 'TimeZoneSerializerField'] diff --git a/timezone_field/rest_framework.py b/timezone_field/rest_framework.py new file mode 100644 index 0000000..d0fbd9a --- /dev/null +++ b/timezone_field/rest_framework.py @@ -0,0 +1,19 @@ +import pytz +from django.utils.translation import gettext_lazy as _ +from django.utils.encoding import force_str +from rest_framework.fields import Field + + +class TimeZoneSerializerField(Field): + default_error_messages = { + 'invalid': _('A valid timezone is required.'), + } + + def to_internal_value(self, data): + try: + return pytz.timezone(force_str(data)) + except pytz.UnknownTimeZoneError: + self.fail('invalid') + + def to_representation(self, value): + return str(value) diff --git a/tox.ini b/tox.ini index 44f419a..2937fe5 100644 --- a/tox.ini +++ b/tox.ini @@ -13,6 +13,7 @@ envlist = commands = coverage run --include='*/timezone_field/*' {envbindir}/django-admin.py test tests deps = coverage + djangorestframework>=3.0.0 django22: django>=2.2.17,<2.3 django30: django>=3.0.0,<3.1 django31: django>=3.1.0,<3.2