diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b538405761..6223966646 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,6 +23,7 @@ env: jobs: build: name: Build distribution 📦 + if: github.repository == 'encode/django-rest-framework' runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v6 diff --git a/docs/community/release-notes.md b/docs/community/release-notes.md index 458a4f23b5..eed8a65d81 100644 --- a/docs/community/release-notes.md +++ b/docs/community/release-notes.md @@ -38,6 +38,16 @@ You can determine your currently installed version using `pip show`: ## 3.17.x series +### 3.17.1 + +**Date**: 24th March 2026 + +#### Bug fixes + +* Fix `HTMLFormRenderer` with empty `datetime` values by [@p-r-a-v-i-n](https://github.com/p-r-a-v-i-n) in [#9928](https://github.com/encode/django-rest-framework/pull/9928) + +**Full Changelog**: [3.17.0...3.17.1](https://github.com/encode/django-rest-framework/compare/3.17.0...3.17.1) + ### 3.17.0 **Date**: 18th March 2026 @@ -49,13 +59,13 @@ You can determine your currently installed version using `pip show`: #### Features +* Add Django 6.0 support by [@MehrazRumman](https://github.com/MehrazRumman) in [#9819](https://github.com/encode/django-rest-framework/pull/9819) +* Add support for Python 3.14 by [@cclauss](https://github.com/cclauss) in [#9780](https://github.com/encode/django-rest-framework/pull/9780) * Add ability to specify output format for `DurationField` by [@sevdog](https://github.com/sevdog) in [#8532](https://github.com/encode/django-rest-framework/pull/8532) * Add missing decorators: `@versioning_class()`, `@content_negotiation_class()`, `@metadata_class()` for function-based views by [@qqii](https://github.com/qqii) in [#9719](https://github.com/encode/django-rest-framework/pull/9719) -* Add support for Python 3.14 by [@cclauss](https://github.com/cclauss) in [#9780](https://github.com/encode/django-rest-framework/pull/9780) * Support `violation_error_code` and `violation_error_message` from `UniqueConstraint` in `UniqueTogetherValidator` by [@s-aleshin](https://github.com/s-aleshin) in [#9766](https://github.com/encode/django-rest-framework/pull/9766) * Add support for `ipaddress` objects in `JSONEncoder` by [@corenting](https://github.com/corenting) in [#9087](https://github.com/encode/django-rest-framework/pull/9087) * Add optional support to serialize `BigInteger` to string by [@HoodyH](https://github.com/HoodyH) in [#9775](https://github.com/encode/django-rest-framework/pull/9775) -* Add Django 6.0 support by [@MehrazRumman](https://github.com/MehrazRumman) in [#9819](https://github.com/encode/django-rest-framework/pull/9819) #### Bug fixes diff --git a/docs/community/tutorials-and-resources.md b/docs/community/tutorials-and-resources.md index b7b799465a..8cccfe3587 100644 --- a/docs/community/tutorials-and-resources.md +++ b/docs/community/tutorials-and-resources.md @@ -6,16 +6,13 @@ There are a wide range of resources available for learning and using Django REST
- + - + - - - - +
@@ -43,7 +40,7 @@ There are a wide range of resources available for learning and using Django REST * [Django Polls Tutorial API][django-polls-api] * [Django REST Framework Tutorial: Todo API][django-rest-framework-todo-api] * [Tutorial: Django REST with React (Django 2.0)][django-rest-react-valentinog] - +* [Building APIs with Django and Django REST framework](https://books.agiliq.com/projects/django-api-polls-tutorial/en/latest/) ## Videos @@ -137,4 +134,4 @@ Want your Django REST Framework talk/tutorial/article to be added to our website [django-rest-react-valentinog]: https://www.valentinog.com/blog/tutorial-api-django-rest-react/ [doordash-implementing-rest-apis]: https://doordash.engineering/2013/10/07/implementing-rest-apis-with-embedded-privacy/ [developing-restful-apis-with-django-rest-framework]: https://testdriven.io/courses/django-rest-framework/ -[django-con-2018]: https://youtu.be/pY-oje5b5Qk?si=AOU6tLi0IL1_pVzq \ No newline at end of file +[django-con-2018]: https://youtu.be/pY-oje5b5Qk?si=AOU6tLi0IL1_pVzq diff --git a/rest_framework/__init__.py b/rest_framework/__init__.py index 37d69721fb..76e645bd3d 100644 --- a/rest_framework/__init__.py +++ b/rest_framework/__init__.py @@ -8,7 +8,7 @@ """ __title__ = 'Django REST framework' -__version__ = '3.17.0' +__version__ = '3.17.1' __author__ = 'Tom Christie' __license__ = 'BSD-3-Clause' __copyright__ = 'Copyright 2011-2023 Encode OSS Ltd' diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py index ce8291dae1..ec6ac3dd8f 100644 --- a/rest_framework/renderers.py +++ b/rest_framework/renderers.py @@ -345,7 +345,7 @@ def render_field(self, field, parent_style): except AttributeError: format_ = api_settings.DATETIME_FORMAT - if format_ is not None: + if format_ is not None and field.value not in (None, ''): # field.value is expected to be a string # https://www.django-rest-framework.org/api-guide/fields/#datetimefield field_value = field.value @@ -358,10 +358,11 @@ def render_field(self, field, parent_style): else datetime.datetime.strptime(field_value, format_) ) - # The format of an input type="datetime-local" is "yyyy-MM-ddThh:mm" - # followed by optional ":ss" or ":ss.SSS", so keep only the first three - # digits of milliseconds to avoid browser console error. - field.value = field.value.replace(tzinfo=None).isoformat(timespec="milliseconds") + if isinstance(field.value, datetime.datetime): + # The format of an input type="datetime-local" is "yyyy-MM-ddThh:mm" + # followed by optional ":ss" or ":ss.SSS", so keep only the first three + # digits of milliseconds to avoid browser console error. + field.value = field.value.replace(tzinfo=None).isoformat(timespec="milliseconds") if 'template' in style: template_name = style['template'] diff --git a/tests/test_renderers.py b/tests/test_renderers.py index ae38983276..3e3e7512f4 100644 --- a/tests/test_renderers.py +++ b/tests/test_renderers.py @@ -566,6 +566,22 @@ def test_datetime_field_rendering_timezone_aware_datetime(self): "2024-12-23T09:55:30.345" # Rendered in -06:00 ) + def test_datetime_field_rendering_empty_string_raises_no_error(self): + """ + Regression test for #9927 (issue): + Ensures that an empty string value doesn't cause a ValueError + when the HTMLFormRenderer tries to parse it via fromisoformat. + """ + self._assert_datetime_rendering("", "") + + def test_datetime_field_rendering_none_value_raises_no_error(self): + """ + Additional regression coverage for #9927: + Ensures that a None value, which is converted to an empty string + by as_form_field(), doesn't cause a ValueError when rendered. + """ + self._assert_datetime_rendering(None, "") + class TestHTMLFormRenderer(TestCase): def setUp(self):