Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-django-timezone-field for 
openSUSE:Factory checked in at 2024-07-22 17:17:49
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-django-timezone-field (Old)
 and      /work/SRC/openSUSE:Factory/.python-django-timezone-field.new.17339 
(New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-django-timezone-field"

Mon Jul 22 17:17:49 2024 rev:6 rq:1188822 version:7.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-django-timezone-field/python-django-timezone-field.changes
        2023-12-06 23:48:25.971718622 +0100
+++ 
/work/SRC/openSUSE:Factory/.python-django-timezone-field.new.17339/python-django-timezone-field.changes
     2024-07-22 17:19:32.304964855 +0200
@@ -1,0 +2,11 @@
+Fri Jul 19 11:38:11 UTC 2024 - Markéta Machová <mmach...@suse.com>
+
+- Update to 7.0
+  * Better default sorting of choices
+  * Convert string value to timezone object immediately on creation/assignment
+  * Add support for django 5.1
+  * Drop support for django 3.2, 4.0, 4.1
+  * Change base class of TimeZoneSerializerField from DJRF's Field to CharField
+- Add missing test requirement and recommend pytz
+
+-------------------------------------------------------------------

Old:
----
  django-timezone-field-6.1.0.tar.gz

New:
----
  django-timezone-field-7.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-django-timezone-field.spec ++++++
--- /var/tmp/diff_new_pack.cvSHqt/_old  2024-07-22 17:19:32.836986245 +0200
+++ /var/tmp/diff_new_pack.cvSHqt/_new  2024-07-22 17:19:32.840986406 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package python-django-timezone-field
 #
-# Copyright (c) 2023 SUSE LLC
+# Copyright (c) 2024 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-django-timezone-field
-Version:        6.1.0
+Version:        7.0
 Release:        0
 Summary:        Django app providing database and form fields for pytz 
timezone objects
 License:        BSD-2-Clause
@@ -32,6 +32,7 @@
 BuildRequires:  fdupes
 BuildRequires:  python-rpm-macros
 Requires:       python-Django >= 2.3
+Recommends:     python-pytz
 BuildArch:      noarch
 # SECTION test requirements
 BuildRequires:  %{python_module Django >= 2.3}
@@ -39,6 +40,7 @@
 BuildRequires:  %{python_module pytest-django}
 BuildRequires:  %{python_module pytest-lazy-fixture}
 BuildRequires:  %{python_module pytest}
+BuildRequires:  %{python_module pytz}
 # /SECTION
 %python_subpackages
 

++++++ django-timezone-field-6.1.0.tar.gz -> django-timezone-field-7.0.tar.gz 
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-timezone-field-6.1.0/.github/workflows/ci.yml 
new/django-timezone-field-7.0/.github/workflows/ci.yml
--- old/django-timezone-field-6.1.0/.github/workflows/ci.yml    2023-11-26 
00:24:33.000000000 +0100
+++ new/django-timezone-field-7.0/.github/workflows/ci.yml      2024-07-07 
20:27:23.000000000 +0200
@@ -11,10 +11,10 @@
 
     steps:
       - name: Git checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - name: Set up Python
-        uses: actions/setup-python@v3
+        uses: actions/setup-python@v5
         with:
           python-version: "3.12"
 
@@ -26,7 +26,7 @@
 
       - name: Load cached venv
         id: cached-poetry-dependencies
-        uses: actions/cache@v3
+        uses: actions/cache@v4
         with:
           path: .venv
           key: venv-py3.12-${{ hashFiles('poetry.lock') }}
@@ -54,29 +54,23 @@
     strategy:
       fail-fast: false
       matrix:
-        # 
https://docs.djangoproject.com/en/4.1/faq/install/#what-python-version-can-i-use-with-django
+        # 
https://docs.djangoproject.com/en/5.1/faq/install/#what-python-version-can-i-use-with-django
         python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
-        django-version: ["3.2", "4.0", "4.1", "4.2", "5.0rc1"]
+        django-version: ["4.2", "5.0", "5.1b1"]
         db-engine: [sqlite, postgres]
         tz-engine: [pytz, zoneinfo]
         exclude:
-          - django-version: "3.2"
-            python-version: "3.11"
-          - django-version: "3.2"
-            python-version: "3.12"
-          - django-version: "3.2"
-            tz-engine: zoneinfo
-          - django-version: "4.0"
-            python-version: "3.11"
-          - django-version: "4.0"
-            python-version: "3.12"
-          - django-version: "4.1"
-            python-version: "3.12"
-          - django-version: "5.0rc1"
+          - django-version: "5.0"
             python-version: "3.8"
-          - django-version: "5.0rc1"
+          - django-version: "5.0"
             python-version: "3.9"
-          - django-version: "5.0rc1"
+          - django-version: "5.0"
+            tz-engine: pytz
+          - django-version: "5.1b1"
+            python-version: "3.8"
+          - django-version: "5.1b1"
+            python-version: "3.9"
+          - django-version: "5.1b1"
             tz-engine: pytz
 
     env:
@@ -100,10 +94,10 @@
 
     steps:
       - name: Git checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - name: Set up Python ${{ matrix.python-version }}
-        uses: actions/setup-python@v3
+        uses: actions/setup-python@v5
         with:
           python-version: ${{ matrix.python-version }}
 
@@ -115,7 +109,7 @@
 
       - name: Load cached venv
         id: cached-poetry-dependencies
-        uses: actions/cache@v3
+        uses: actions/cache@v4
         with:
           path: .venv
           key: venv-py${{ matrix.python-version}}-dj${{ matrix.django-version 
}}-${{ matrix.tz-engine }}-${{ hashFiles('poetry.lock') }}
@@ -149,24 +143,15 @@
           POSTGRES_DB: postgres
           POSTGRES_USER: postgres
           POSTGRES_PASSWORD: postgres
-        run: poetry run pytest --cov=timezone_field 
--ignore=tests/test_serializer_field.py
-
-      - name: Test serializer fiels with coverage
-        if: matrix.tz-engine == 'pytz'
-        env:
-          POSTGRES_HOST: localhost
-          POSTGRES_PORT: 5432
-          POSTGRES_DB: postgres
-          POSTGRES_USER: postgres
-          POSTGRES_PASSWORD: postgres
-        run: poetry run pytest --cov=timezone_field 
tests/test_serializer_field.py
+        run: poetry run pytest --cov=timezone_field
 
       - name: Generate coverage report
         run: poetry run coverage xml
 
       - name: Upload coverage report to codecov
-        uses: codecov/codecov-action@v3
+        uses: codecov/codecov-action@v4
         with:
+          token: ${{ secrets.CODECOV_TOKEN }}
           env_vars: PYTHON_VERSION,DJANGO_VERSION,DB_ENGINE
           fail_ci_if_error: true
 
@@ -176,10 +161,10 @@
 
     steps:
       - name: Git checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - name: Set up Python
-        uses: actions/setup-python@v3
+        uses: actions/setup-python@v5
         with:
           python-version: "3.12"
 
@@ -191,7 +176,7 @@
 
       - name: Load cached venv
         id: cached-poetry-dependencies
-        uses: actions/cache@v3
+        uses: actions/cache@v4
         with:
           path: .venv
           key: venv-py3.12-${{ hashFiles('poetry.lock') }}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-timezone-field-6.1.0/README.md 
new/django-timezone-field-7.0/README.md
--- old/django-timezone-field-6.1.0/README.md   2023-11-26 00:24:33.000000000 
+0100
+++ new/django-timezone-field-7.0/README.md     2024-07-07 20:27:23.000000000 
+0200
@@ -21,9 +21,9 @@
 - Django == 4.X: `use_pytz` defaults to the value of
   
[`django.conf.settings.USE_DEPRECATED_PYTZ`](https://docs.djangoproject.com/en/4.0/ref/settings/#use-deprecated-pytz),
   which itself defaults to `False`
-- Django >= 5.X: django plans to
-  [drop support for `pytz` 
altogether](https://docs.djangoproject.com/en/4.0/releases/4.0/#zoneinfo-default-timezone-implementation),
-  and this app will likely do the same.
+- Django >= 5.X: 
+  [drops support for `pytz` 
altogether](https://docs.djangoproject.com/en/5.0/releases/5.0/#features-removed-in-5-0),
+  and this app has done the same.
 
 Note that this app does _not_ declare `pytz` to be a dependency, so if you're 
using this app with `use_pytz=True`, you'll need
 to ensure `pytz` is included in the environment yourself.
@@ -71,6 +71,10 @@
 my_model.save()       # values stored in DB as strings
 my_model.tz3          # value returned as pytz timezone: <DstTzInfo 
'America/Vancouver' LMT-1 day, 15:48:00 STD>
 my_model.tz4          # value returned as zoneinfo: 
zoneinfo.ZoneInfo(key='America/Vancouver')
+
+my_model.tz1 = "UTC"  # assignment of a string, immediately converted to 
timezone object
+my_model.tz1          # zoneinfo.ZoneInfo(key='UTC') or pytz.utc, depending on 
use_pytz default
+my_model.tz2 = "Invalid/Not_A_Zone"  # immediately raises ValidationError
 ```
 
 ### Form Field
@@ -134,6 +138,18 @@
 
 ## Changelog
 
+#### 7.0 (2024-07-07)
+
+- Better default sorting of `choices` 
([#116](https://github.com/mfogel/django-timezone-field/issues/116)), 
([#123](https://github.com/mfogel/django-timezone-field/issues/123))
+- Convert string value to timezone object immediately on creation/assignment.
+  Accessing a TimeZoneField will _always_ return a timezone or None (never a 
string).
+  (Potentially BREAKING: Unknown timezone names now raise `ValidationError` at 
time of assignment.
+  Previously, conversion was delayed until model `full_clean` or `save`.) 
+  ([#57](https://github.com/mfogel/django-timezone-field/issues/57))
+- Add support for django 5.1
+- Drop support for django 3.2, 4.0, 4.1
+- Change base class of `TimeZoneSerializerField` from DJRF's `Field` to 
`CharField` ([#137](https://github.com/mfogel/django-timezone-field/issues/137))
+
 #### 6.1.0 (2023-11-25)
 
 - Add support for django 5.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-timezone-field-6.1.0/poetry.lock 
new/django-timezone-field-7.0/poetry.lock
--- old/django-timezone-field-6.1.0/poetry.lock 2023-11-26 00:24:33.000000000 
+0100
+++ new/django-timezone-field-7.0/poetry.lock   2024-07-07 20:27:23.000000000 
+0200
@@ -2,13 +2,13 @@
 
 [[package]]
 name = "asgiref"
-version = "3.7.2"
+version = "3.8.1"
 description = "ASGI specs, helper code, and adapters"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
 files = [
-    {file = "asgiref-3.7.2-py3-none-any.whl", hash = 
"sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"},
-    {file = "asgiref-3.7.2.tar.gz", hash = 
"sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"},
+    {file = "asgiref-3.8.1-py3-none-any.whl", hash = 
"sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"},
+    {file = "asgiref-3.8.1.tar.gz", hash = 
"sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"},
 ]
 
 [package.dependencies]
@@ -48,21 +48,22 @@
 
 [[package]]
 name = "attrs"
-version = "23.1.0"
+version = "23.2.0"
 description = "Classes Without Boilerplate"
 optional = false
 python-versions = ">=3.7"
 files = [
-    {file = "attrs-23.1.0-py3-none-any.whl", hash = 
"sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"},
-    {file = "attrs-23.1.0.tar.gz", hash = 
"sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"},
+    {file = "attrs-23.2.0-py3-none-any.whl", hash = 
"sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"},
+    {file = "attrs-23.2.0.tar.gz", hash = 
"sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"},
 ]
 
 [package.extras]
 cov = ["attrs[tests]", "coverage[toml] (>=5.3)"]
-dev = ["attrs[docs,tests]", "pre-commit"]
+dev = ["attrs[tests]", "pre-commit"]
 docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", 
"sphinxcontrib-towncrier", "towncrier", "zope-interface"]
 tests = ["attrs[tests-no-zope]", "zope-interface"]
-tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", 
"pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"]
+tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", 
"pytest (>=4.3.0)", "pytest-xdist[psutil]"]
 
 [[package]]
 name = "backports-zoneinfo"
@@ -94,29 +95,33 @@
 
 [[package]]
 name = "black"
-version = "23.11.0"
+version = "24.4.2"
 description = "The uncompromising code formatter."
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "black-23.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = 
"sha256:dbea0bb8575c6b6303cc65017b46351dc5953eea5c0a59d7b7e3a2d2f433a911"},
-    {file = "black-23.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = 
"sha256:412f56bab20ac85927f3a959230331de5614aecda1ede14b373083f62ec24e6f"},
-    {file = 
"black-23.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 
hash = 
"sha256:d136ef5b418c81660ad847efe0e55c58c8208b77a57a28a503a5f345ccf01394"},
-    {file = "black-23.11.0-cp310-cp310-win_amd64.whl", hash = 
"sha256:6c1cac07e64433f646a9a838cdc00c9768b3c362805afc3fce341af0e6a9ae9f"},
-    {file = "black-23.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = 
"sha256:cf57719e581cfd48c4efe28543fea3d139c6b6f1238b3f0102a9c73992cbb479"},
-    {file = "black-23.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = 
"sha256:698c1e0d5c43354ec5d6f4d914d0d553a9ada56c85415700b81dc90125aac244"},
-    {file = 
"black-23.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", 
hash = 
"sha256:760415ccc20f9e8747084169110ef75d545f3b0932ee21368f63ac0fee86b221"},
-    {file = "black-23.11.0-cp311-cp311-win_amd64.whl", hash = 
"sha256:58e5f4d08a205b11800332920e285bd25e1a75c54953e05502052738fe16b3b5"},
-    {file = "black-23.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = 
"sha256:45aa1d4675964946e53ab81aeec7a37613c1cb71647b5394779e6efb79d6d187"},
-    {file = "black-23.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = 
"sha256:4c44b7211a3a0570cc097e81135faa5f261264f4dfaa22bd5ee2875a4e773bd6"},
-    {file = 
"black-23.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash 
= "sha256:2a9acad1451632021ee0d146c8765782a0c3846e0e0ea46659d7c4f89d9b212b"},
-    {file = "black-23.11.0-cp38-cp38-win_amd64.whl", hash = 
"sha256:fc7f6a44d52747e65a02558e1d807c82df1d66ffa80a601862040a43ec2e3142"},
-    {file = "black-23.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = 
"sha256:7f622b6822f02bfaf2a5cd31fdb7cd86fcf33dab6ced5185c35f5db98260b055"},
-    {file = "black-23.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = 
"sha256:250d7e60f323fcfc8ea6c800d5eba12f7967400eb6c2d21ae85ad31c204fb1f4"},
-    {file = 
"black-23.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash 
= "sha256:5133f5507007ba08d8b7b263c7aa0f931af5ba88a29beacc4b2dc23fcefe9c06"},
-    {file = "black-23.11.0-cp39-cp39-win_amd64.whl", hash = 
"sha256:421f3e44aa67138ab1b9bfbc22ee3780b22fa5b291e4db8ab7eee95200726b07"},
-    {file = "black-23.11.0-py3-none-any.whl", hash = 
"sha256:54caaa703227c6e0c87b76326d0862184729a69b73d3b7305b6288e1d830067e"},
-    {file = "black-23.11.0.tar.gz", hash = 
"sha256:4c68855825ff432d197229846f971bc4d6666ce90492e5b02013bcaca4d9ab05"},
+    {file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = 
"sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"},
+    {file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = 
"sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"},
+    {file = 
"black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash 
= "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"},
+    {file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = 
"sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"},
+    {file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = 
"sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"},
+    {file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = 
"sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"},
+    {file = 
"black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash 
= "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"},
+    {file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = 
"sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"},
+    {file = "black-24.4.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = 
"sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d"},
+    {file = "black-24.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = 
"sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04"},
+    {file = 
"black-24.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash 
= "sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc"},
+    {file = "black-24.4.2-cp312-cp312-win_amd64.whl", hash = 
"sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0"},
+    {file = "black-24.4.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = 
"sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7"},
+    {file = "black-24.4.2-cp38-cp38-macosx_11_0_arm64.whl", hash = 
"sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94"},
+    {file = 
"black-24.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = 
"sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8"},
+    {file = "black-24.4.2-cp38-cp38-win_amd64.whl", hash = 
"sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c"},
+    {file = "black-24.4.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = 
"sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1"},
+    {file = "black-24.4.2-cp39-cp39-macosx_11_0_arm64.whl", hash = 
"sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741"},
+    {file = 
"black-24.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = 
"sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e"},
+    {file = "black-24.4.2-cp39-cp39-win_amd64.whl", hash = 
"sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7"},
+    {file = "black-24.4.2-py3-none-any.whl", hash = 
"sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"},
+    {file = "black-24.4.2.tar.gz", hash = 
"sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"},
 ]
 
 [package.dependencies]
@@ -130,7 +135,7 @@
 
 [package.extras]
 colorama = ["colorama (>=0.4.3)"]
-d = ["aiohttp (>=3.7.4)"]
+d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"]
 jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
 uvloop = ["uvloop (>=0.15.2)"]
 
@@ -226,27 +231,28 @@
 
 [[package]]
 name = "dill"
-version = "0.3.7"
+version = "0.3.8"
 description = "serialize all of Python"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
 files = [
-    {file = "dill-0.3.7-py3-none-any.whl", hash = 
"sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e"},
-    {file = "dill-0.3.7.tar.gz", hash = 
"sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03"},
+    {file = "dill-0.3.8-py3-none-any.whl", hash = 
"sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"},
+    {file = "dill-0.3.8.tar.gz", hash = 
"sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"},
 ]
 
 [package.extras]
 graph = ["objgraph (>=1.7.2)"]
+profile = ["gprof2dot (>=2022.7.29)"]
 
 [[package]]
 name = "django"
-version = "4.2.7"
+version = "4.2.13"
 description = "A high-level Python web framework that encourages rapid 
development and clean, pragmatic design."
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "Django-4.2.7-py3-none-any.whl", hash = 
"sha256:e1d37c51ad26186de355cbcec16613ebdabfa9689bbade9c538835205a8abbe9"},
-    {file = "Django-4.2.7.tar.gz", hash = 
"sha256:8e0f1c2c2786b5c0e39fe1afce24c926040fad47c8ea8ad30aaf1188df29fc41"},
+    {file = "Django-4.2.13-py3-none-any.whl", hash = 
"sha256:a17fcba2aad3fc7d46fdb23215095dbbd64e6174bf4589171e732b18b07e426a"},
+    {file = "Django-4.2.13.tar.gz", hash = 
"sha256:837e3cf1f6c31347a1396a3f6b65688f2b4bb4a11c580dcb628b5afe527b68a5"},
 ]
 
 [package.dependencies]
@@ -261,18 +267,18 @@
 
 [[package]]
 name = "djangorestframework"
-version = "3.14.0"
+version = "3.15.2"
 description = "Web APIs for Django, made easy."
 optional = false
-python-versions = ">=3.6"
+python-versions = ">=3.8"
 files = [
-    {file = "djangorestframework-3.14.0-py3-none-any.whl", hash = 
"sha256:eb63f58c9f218e1a7d064d17a70751f528ed4e1d35547fdade9aaf4cd103fd08"},
-    {file = "djangorestframework-3.14.0.tar.gz", hash = 
"sha256:579a333e6256b09489cbe0a067e66abe55c6595d8926be6b99423786334350c8"},
+    {file = "djangorestframework-3.15.2-py3-none-any.whl", hash = 
"sha256:2b8871b062ba1aefc2de01f773875441a961fefbf79f5eed1e32b2f096944b20"},
+    {file = "djangorestframework-3.15.2.tar.gz", hash = 
"sha256:36fe88cd2d6c6bec23dca9804bab2ba5517a8bb9d8f47ebc68981b56840107ad"},
 ]
 
 [package.dependencies]
-django = ">=3.0"
-pytz = "*"
+"backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""}
+django = ">=4.2"
 
 [[package]]
 name = "flake8"
@@ -303,64 +309,62 @@
 
 [[package]]
 name = "isort"
-version = "5.12.0"
+version = "5.13.2"
 description = "A Python utility / library to sort Python imports."
 optional = false
 python-versions = ">=3.8.0"
 files = [
-    {file = "isort-5.12.0-py3-none-any.whl", hash = 
"sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"},
-    {file = "isort-5.12.0.tar.gz", hash = 
"sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"},
+    {file = "isort-5.13.2-py3-none-any.whl", hash = 
"sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
+    {file = "isort-5.13.2.tar.gz", hash = 
"sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
 ]
 
 [package.extras]
-colors = ["colorama (>=0.4.3)"]
-pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", 
"requirementslib"]
-plugins = ["setuptools"]
-requirements-deprecated-finder = ["pip-api", "pipreqs"]
+colors = ["colorama (>=0.4.6)"]
 
 [[package]]
 name = "lazy-object-proxy"
-version = "1.9.0"
+version = "1.10.0"
 description = "A fast and thorough lazy object proxy."
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
 files = [
-    {file = "lazy-object-proxy-1.9.0.tar.gz", hash = 
"sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"},
-    {file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash 
= "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"},
-    {file = 
"lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
 hash = 
"sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"},
-    {file = 
"lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
 hash = 
"sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd"},
-    {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", 
hash = 
"sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"},
-    {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", 
hash = 
"sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"},
-    {file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = 
"sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"},
-    {file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = 
"sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"},
-    {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash 
= "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"},
-    {file = 
"lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
 hash = 
"sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"},
-    {file = 
"lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
 hash = 
"sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"},
-    {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", 
hash = 
"sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"},
-    {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", 
hash = 
"sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"},
-    {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = 
"sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"},
-    {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = 
"sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"},
-    {file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash 
= "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"},
-    {file = 
"lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
 hash = 
"sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"},
-    {file = 
"lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
 hash = 
"sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2"},
-    {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", 
hash = 
"sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"},
-    {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", 
hash = 
"sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"},
-    {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = 
"sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"},
-    {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = 
"sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"},
-    {file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = 
"sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"},
-    {file = 
"lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
 hash = 
"sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"},
-    {file = 
"lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
 hash = 
"sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be"},
-    {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", 
hash = 
"sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"},
-    {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash 
= "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"},
-    {file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = 
"sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"},
-    {file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = 
"sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"},
-    {file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = 
"sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"},
-    {file = 
"lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
 hash = 
"sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"},
-    {file = 
"lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
 hash = 
"sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006"},
-    {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", 
hash = 
"sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"},
-    {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash 
= "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"},
-    {file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = 
"sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"},
-    {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = 
"sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"},
+    {file = "lazy-object-proxy-1.10.0.tar.gz", hash = 
"sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"},
+    {file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", 
hash = 
"sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"},
+    {file = 
"lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
 hash = 
"sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3"},
+    {file = 
"lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
 hash = 
"sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05"},
+    {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", 
hash = 
"sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895"},
+    {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", 
hash = 
"sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83"},
+    {file = "lazy_object_proxy-1.10.0-cp310-cp310-win32.whl", hash = 
"sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9"},
+    {file = "lazy_object_proxy-1.10.0-cp310-cp310-win_amd64.whl", hash = 
"sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4"},
+    {file = "lazy_object_proxy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", 
hash = 
"sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c"},
+    {file = 
"lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
 hash = 
"sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4"},
+    {file = 
"lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
 hash = 
"sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56"},
+    {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", 
hash = 
"sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9"},
+    {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", 
hash = 
"sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f"},
+    {file = "lazy_object_proxy-1.10.0-cp311-cp311-win32.whl", hash = 
"sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03"},
+    {file = "lazy_object_proxy-1.10.0-cp311-cp311-win_amd64.whl", hash = 
"sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6"},
+    {file = "lazy_object_proxy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", 
hash = 
"sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba"},
+    {file = 
"lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
 hash = 
"sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43"},
+    {file = 
"lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
 hash = 
"sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9"},
+    {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", 
hash = 
"sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3"},
+    {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", 
hash = 
"sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b"},
+    {file = "lazy_object_proxy-1.10.0-cp312-cp312-win32.whl", hash = 
"sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074"},
+    {file = "lazy_object_proxy-1.10.0-cp312-cp312-win_amd64.whl", hash = 
"sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282"},
+    {file = "lazy_object_proxy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash 
= "sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a"},
+    {file = 
"lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
 hash = 
"sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c"},
+    {file = 
"lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
 hash = 
"sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255"},
+    {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_aarch64.whl", 
hash = 
"sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8"},
+    {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", 
hash = 
"sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee"},
+    {file = "lazy_object_proxy-1.10.0-cp38-cp38-win32.whl", hash = 
"sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4"},
+    {file = "lazy_object_proxy-1.10.0-cp38-cp38-win_amd64.whl", hash = 
"sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696"},
+    {file = "lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash 
= "sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94"},
+    {file = 
"lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl",
 hash = 
"sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b"},
+    {file = 
"lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl",
 hash = 
"sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757"},
+    {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", 
hash = 
"sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424"},
+    {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", 
hash = 
"sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658"},
+    {file = "lazy_object_proxy-1.10.0-cp39-cp39-win32.whl", hash = 
"sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70"},
+    {file = "lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl", hash = 
"sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd"},
+    {file = 
"lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = 
"sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d"},
 ]
 
 [[package]]
@@ -387,50 +391,51 @@
 
 [[package]]
 name = "packaging"
-version = "23.2"
+version = "24.1"
 description = "Core utilities for Python packages"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
 files = [
-    {file = "packaging-23.2-py3-none-any.whl", hash = 
"sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
-    {file = "packaging-23.2.tar.gz", hash = 
"sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
+    {file = "packaging-24.1-py3-none-any.whl", hash = 
"sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
+    {file = "packaging-24.1.tar.gz", hash = 
"sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
 ]
 
 [[package]]
 name = "pathspec"
-version = "0.11.2"
+version = "0.12.1"
 description = "Utility library for gitignore style pattern matching of file 
paths."
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
 files = [
-    {file = "pathspec-0.11.2-py3-none-any.whl", hash = 
"sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"},
-    {file = "pathspec-0.11.2.tar.gz", hash = 
"sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"},
+    {file = "pathspec-0.12.1-py3-none-any.whl", hash = 
"sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
+    {file = "pathspec-0.12.1.tar.gz", hash = 
"sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
 ]
 
 [[package]]
 name = "platformdirs"
-version = "4.0.0"
-description = "A small Python package for determining appropriate 
platform-specific dirs, e.g. a \"user data dir\"."
+version = "4.2.2"
+description = "A small Python package for determining appropriate 
platform-specific dirs, e.g. a `user data dir`."
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
 files = [
-    {file = "platformdirs-4.0.0-py3-none-any.whl", hash = 
"sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"},
-    {file = "platformdirs-4.0.0.tar.gz", hash = 
"sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"},
+    {file = "platformdirs-4.2.2-py3-none-any.whl", hash = 
"sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"},
+    {file = "platformdirs-4.2.2.tar.gz", hash = 
"sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"},
 ]
 
 [package.extras]
-docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", 
"sphinx-autodoc-typehints (>=1.24)"]
-test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", 
"pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"]
+docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", 
"sphinx-autodoc-typehints (>=1.25.2)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", 
"pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
+type = ["mypy (>=1.8)"]
 
 [[package]]
 name = "pluggy"
-version = "1.3.0"
+version = "1.5.0"
 description = "plugin and hook calling mechanisms for python"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "pluggy-1.3.0-py3-none-any.whl", hash = 
"sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"},
-    {file = "pluggy-1.3.0.tar.gz", hash = 
"sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"},
+    {file = "pluggy-1.5.0-py3-none-any.whl", hash = 
"sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
+    {file = "pluggy-1.5.0.tar.gz", hash = 
"sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
 ]
 
 [package.extras]
@@ -670,30 +675,29 @@
 
 [[package]]
 name = "pytz"
-version = "2023.3.post1"
+version = "2024.1"
 description = "World timezone definitions, modern and historical"
 optional = false
 python-versions = "*"
 files = [
-    {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = 
"sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"},
-    {file = "pytz-2023.3.post1.tar.gz", hash = 
"sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"},
+    {file = "pytz-2024.1-py2.py3-none-any.whl", hash = 
"sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"},
+    {file = "pytz-2024.1.tar.gz", hash = 
"sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"},
 ]
 
 [[package]]
 name = "sqlparse"
-version = "0.4.4"
+version = "0.5.0"
 description = "A non-validating SQL parser."
 optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.8"
 files = [
-    {file = "sqlparse-0.4.4-py3-none-any.whl", hash = 
"sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"},
-    {file = "sqlparse-0.4.4.tar.gz", hash = 
"sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"},
+    {file = "sqlparse-0.5.0-py3-none-any.whl", hash = 
"sha256:c204494cd97479d0e39f28c93d46c0b2d5959c7b9ab904762ea6c7af211c8663"},
+    {file = "sqlparse-0.5.0.tar.gz", hash = 
"sha256:714d0a4932c059d16189f58ef5411ec2287a4360f17cdd0edd2d09d4c5087c93"},
 ]
 
 [package.extras]
-dev = ["build", "flake8"]
+dev = ["build", "hatch"]
 doc = ["sphinx"]
-test = ["pytest", "pytest-cov"]
 
 [[package]]
 name = "toml"
@@ -719,35 +723,35 @@
 
 [[package]]
 name = "tomlkit"
-version = "0.12.3"
+version = "0.12.5"
 description = "Style preserving TOML library"
 optional = false
 python-versions = ">=3.7"
 files = [
-    {file = "tomlkit-0.12.3-py3-none-any.whl", hash = 
"sha256:b0a645a9156dc7cb5d3a1f0d4bab66db287fcb8e0430bdd4664a095ea16414ba"},
-    {file = "tomlkit-0.12.3.tar.gz", hash = 
"sha256:75baf5012d06501f07bee5bf8e801b9f343e7aac5a92581f20f80ce632e6b5a4"},
+    {file = "tomlkit-0.12.5-py3-none-any.whl", hash = 
"sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"},
+    {file = "tomlkit-0.12.5.tar.gz", hash = 
"sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"},
 ]
 
 [[package]]
 name = "typing-extensions"
-version = "4.8.0"
+version = "4.12.2"
 description = "Backported and Experimental Type Hints for Python 3.8+"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = 
"sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"},
-    {file = "typing_extensions-4.8.0.tar.gz", hash = 
"sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"},
+    {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = 
"sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
+    {file = "typing_extensions-4.12.2.tar.gz", hash = 
"sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
 ]
 
 [[package]]
 name = "tzdata"
-version = "2023.3"
+version = "2023.4"
 description = "Provider of IANA time zone data"
 optional = false
 python-versions = ">=2"
 files = [
-    {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = 
"sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"},
-    {file = "tzdata-2023.3.tar.gz", hash = 
"sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"},
+    {file = "tzdata-2023.4-py2.py3-none-any.whl", hash = 
"sha256:aa3ace4329eeacda5b7beb7ea08ece826c28d761cda36e747cfbf97996d39bf3"},
+    {file = "tzdata-2023.4.tar.gz", hash = 
"sha256:dd54c94f294765522c77399649b4fefd95522479a664a0cec87f41bebc6148c9"},
 ]
 
 [[package]]
@@ -832,4 +836,4 @@
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.8"
-content-hash = 
"30436b1c225fd7570ea535458ea6f4c02328a0592b543c6751a34f2598035649"
+content-hash = 
"e7c575a30807c6f6c2f3e79f6315a193af37420091df6c06de7c5e0c0d1778f0"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-timezone-field-6.1.0/pyproject.toml 
new/django-timezone-field-7.0/pyproject.toml
--- old/django-timezone-field-6.1.0/pyproject.toml      2023-11-26 
00:24:33.000000000 +0100
+++ new/django-timezone-field-7.0/pyproject.toml        2024-07-07 
20:27:23.000000000 +0200
@@ -14,7 +14,7 @@
 
 [tool.poetry]
 name = "django-timezone-field"
-version = "6.1.0"
+version = "7.0"
 description = "A Django app providing DB, form, and REST framework fields for 
zoneinfo and pytz timezone objects."
 license = "BSD-2-Clause"
 authors = ["Mike Fogel <m...@fogel.ca>"]
@@ -24,11 +24,9 @@
     "Development Status :: 4 - Beta",
     "Environment :: Web Environment",
     "Framework :: Django",
-    "Framework :: Django :: 3.2",
-    "Framework :: Django :: 4.0",
-    "Framework :: Django :: 4.1",
     "Framework :: Django :: 4.2",
     "Framework :: Django :: 5.0",
+    "Framework :: Django :: 5.1",
     "Intended Audience :: Developers",
     "Operating System :: OS Independent",
     "Topic :: Utilities",
@@ -44,7 +42,7 @@
 
 [tool.poetry.dev-dependencies]
 coverage = {extras = ["toml"], version = "^6.2"}
-djangorestframework = "^3.13.1"
+djangorestframework = "^3.15.2"
 flake8 = "^5.0.4"
 psycopg2-binary = "^2.9.3"
 pytest = "^6.2.5"
@@ -52,7 +50,8 @@
 pytest-pythonpath = "^0.7.3"
 pytest-lazy-fixture = "^0.6.3"
 pytest-cov = "^3.0.0"
-black = "^23.3.0"
+pytz = "^2024.1"
+black = "^24.4.2"
 isort = "^5.11.5"
 pylint = "^2.13.9"
 tzdata = "^2023.3"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-timezone-field-6.1.0/tests/test_choices.py 
new/django-timezone-field-7.0/tests/test_choices.py
--- old/django-timezone-field-6.1.0/tests/test_choices.py       2023-11-26 
00:24:33.000000000 +0100
+++ new/django-timezone-field-7.0/tests/test_choices.py 2024-07-07 
20:27:23.000000000 +0200
@@ -38,11 +38,20 @@
 
 
 @pytest.fixture
+def tzs3_names_sorted():
+    yield [
+        "America/Argentina/Buenos_Aires",
+        "America/Los_Angeles",
+        "Europe/London",
+    ]
+
+
+@pytest.fixture
 def tzs3_standard_displays():
     yield [
+        "America/Argentina/Buenos Aires",
         "America/Los Angeles",
         "Europe/London",
-        "America/Argentina/Buenos Aires",
     ]
 
 
@@ -104,10 +113,11 @@
     assert with_gmt_offset(tz_names, now=after, use_pytz=use_pytz) == 
[("Europe/London", "GMT+00:00 Europe/London")]
 
 
-def test_standard_using_timezone_names(tzs3_names, tzs3_standard_displays):
-    assert standard(tzs3_names) == list(zip(tzs3_names, 
tzs3_standard_displays))
+def test_standard_using_timezone_names(tzs3_names, tzs3_names_sorted, 
tzs3_standard_displays):
+    assert standard(tzs3_names) == list(zip(tzs3_names_sorted, 
tzs3_standard_displays))
 
 
-def test_standard_using_timezone_objects(tzs3_names, tzs3_standard_displays, 
to_tzobj):
+def test_standard_using_timezone_objects(tzs3_names, tzs3_names_sorted, 
tzs3_standard_displays, to_tzobj):
     tzs3_objects = [to_tzobj(tz) for tz in tzs3_names]
-    assert standard(tzs3_objects) == list(zip(tzs3_objects, 
tzs3_standard_displays))
+    tzs3_objects_sorted = [to_tzobj(tz) for tz in tzs3_names_sorted]
+    assert standard(tzs3_objects) == list(zip(tzs3_objects_sorted, 
tzs3_standard_displays))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-timezone-field-6.1.0/tests/test_choices_display_option.py 
new/django-timezone-field-7.0/tests/test_choices_display_option.py
--- old/django-timezone-field-6.1.0/tests/test_choices_display_option.py        
2023-11-26 00:24:33.000000000 +0100
+++ new/django-timezone-field-7.0/tests/test_choices_display_option.py  
2024-07-07 20:27:23.000000000 +0200
@@ -1,3 +1,5 @@
+from collections import Counter
+
 import pytest
 from django import forms
 from django.db import models
@@ -104,7 +106,7 @@
 def test_form_field_none(ChoicesDisplayForm, base_tzstrs):
     form = ChoicesDisplayForm()
     values, displays = zip(*form.fields["tz_none"].choices)
-    assert values == tuple(base_tzstrs)
+    assert sorted(values) == sorted(base_tzstrs)
     assert displays[values.index("America/Los_Angeles")] == "America/Los 
Angeles"
     assert displays[values.index("Asia/Kolkata")] == "Asia/Kolkata"
 
@@ -136,10 +138,10 @@
 def test_form_field_limited_standard(ChoicesDisplayForm):
     form = ChoicesDisplayForm()
     assert form.fields["tz_limited_standard"].choices == [
-        ("Asia/Tokyo", "Asia/Tokyo"),
-        ("Asia/Dubai", "Asia/Dubai"),
-        ("America/Argentina/Buenos_Aires", "America/Argentina/Buenos Aires"),
         ("Africa/Nairobi", "Africa/Nairobi"),
+        ("America/Argentina/Buenos_Aires", "America/Argentina/Buenos Aires"),
+        ("Asia/Dubai", "Asia/Dubai"),
+        ("Asia/Tokyo", "Asia/Tokyo"),
     ]
 
 
@@ -156,7 +158,7 @@
 def test_model_form_field_none(ChoicesDisplayModelForm, to_tzobj, base_tzobjs):
     form = ChoicesDisplayModelForm()
     values, displays = zip(*form.fields["tz_none"].choices)
-    assert values == ("",) + tuple(base_tzobjs)
+    assert Counter(values) == Counter(("",) + tuple(base_tzobjs))
     assert displays[values.index(to_tzobj("America/Los_Angeles"))] == 
"America/Los Angeles"
     assert displays[values.index(to_tzobj("Asia/Kolkata"))] == "Asia/Kolkata"
 
@@ -192,10 +194,10 @@
     form = ChoicesDisplayModelForm()
     assert form.fields["tz_limited_standard"].choices == [
         ("", "---------"),
-        (to_tzobj("Asia/Tokyo"), "Asia/Tokyo"),
-        (to_tzobj("Asia/Dubai"), "Asia/Dubai"),
-        (to_tzobj("America/Argentina/Buenos_Aires"), "America/Argentina/Buenos 
Aires"),
         (to_tzobj("Africa/Nairobi"), "Africa/Nairobi"),
+        (to_tzobj("America/Argentina/Buenos_Aires"), "America/Argentina/Buenos 
Aires"),
+        (to_tzobj("Asia/Dubai"), "Asia/Dubai"),
+        (to_tzobj("Asia/Tokyo"), "Asia/Tokyo"),
     ]
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-timezone-field-6.1.0/tests/test_deconstruct.py 
new/django-timezone-field-7.0/tests/test_deconstruct.py
--- old/django-timezone-field-6.1.0/tests/test_deconstruct.py   2023-11-26 
00:24:33.000000000 +0100
+++ new/django-timezone-field-7.0/tests/test_deconstruct.py     2024-07-07 
20:27:23.000000000 +0200
@@ -2,6 +2,7 @@
 from django.db.migrations.writer import MigrationWriter
 
 from timezone_field import TimeZoneField
+from timezone_field.choices import standard
 
 
 @pytest.fixture(
@@ -16,13 +17,21 @@
     ]
 )
 def field(request, to_tzobj, use_pytz):
-    yield TimeZoneField(use_pytz=use_pytz) if request.param == "use_pytz_1" 
else TimeZoneField(
-        choices=[
-            (to_tzobj("US/Pacific"), "US/Pacific"),
-            (to_tzobj("US/Eastern"), "US/Eastern"),
-        ],
-        use_pytz=use_pytz,
-    ) if request.param == "use_pytz_2" else request.param
+    yield (
+        TimeZoneField(use_pytz=use_pytz)
+        if request.param == "use_pytz_1"
+        else (
+            TimeZoneField(
+                choices=[
+                    (to_tzobj("US/Pacific"), "US/Pacific"),
+                    (to_tzobj("US/Eastern"), "US/Eastern"),
+                ],
+                use_pytz=use_pytz,
+            )
+            if request.param == "use_pytz_2"
+            else request.param
+        )
+    )
 
 
 def test_deconstruct(field):
@@ -62,7 +71,7 @@
     assert "max_length" not in kwargs
 
 
-def test_specifying_defaults_not_frozen(use_pytz, to_tzobj, base_tzstrs):
+def test_specifying_defaults_not_frozen(use_pytz, base_tzstrs):
     """
     If someone's matched the default values with their kwarg args, we
     shouldn't bothering freezing those
@@ -72,7 +81,7 @@
     _name, _path, _args, kwargs = field.deconstruct()
     assert "max_length" not in kwargs
 
-    choices = [(to_tzobj(tz), tz.replace("_", " ")) for tz in base_tzstrs]
+    choices = standard(base_tzstrs)
     field = TimeZoneField(choices=choices, use_pytz=use_pytz)
     _name, _path, _args, kwargs = field.deconstruct()
     assert "choices" not in kwargs
@@ -85,10 +94,14 @@
     ]
 )
 def choices(request, to_tzobj):
-    yield request.param if request.param is not None else [
-        (to_tzobj("US/Pacific"), "US/Pacific"),
-        (to_tzobj("US/Eastern"), "US/Eastern"),
-    ]
+    yield (
+        request.param
+        if request.param is not None
+        else [
+            (to_tzobj("US/Pacific"), "US/Pacific"),
+            (to_tzobj("US/Eastern"), "US/Eastern"),
+        ]
+    )
 
 
 def test_deconstruct_when_using_choices(choices, use_pytz):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-timezone-field-6.1.0/tests/test_field.py 
new/django-timezone-field-7.0/tests/test_field.py
--- old/django-timezone-field-6.1.0/tests/test_field.py 2023-11-26 
00:24:33.000000000 +0100
+++ new/django-timezone-field-7.0/tests/test_field.py   2024-07-07 
20:27:23.000000000 +0200
@@ -61,11 +61,30 @@
     assert qs.count() == 1
 
 
+@pytest.mark.parametrize(
+    "input_tz, output_tz",
+    [
+        [lazy_fixture("pst"), lazy_fixture("pst_tz")],
+        [lazy_fixture("pst_tz"), lazy_fixture("pst_tz")],
+        ["", None],
+        [None, None],
+    ],
+)
+def test_string_value_assignment_without_save(Model, utc, input_tz, output_tz):
+    m = Model(tz=utc, tz_opt=utc)
+    m.tz_opt = input_tz
+    assert m.tz_opt == output_tz
+
+
 @pytest.mark.parametrize("tz", [None, "", "not-a-tz", 4, object()])
 def test_invalid_input(Model, tz):
-    m = Model(tz=tz)
     with pytest.raises(ValidationError):
-        m.full_clean()
+        # Most invalid values are detected at creation/assignment.
+        # Invalid blank values aren't detected until clean/save.
+        m = Model(tz=tz)
+        if tz is None or tz == "":
+            assert m.tz is None
+            m.full_clean()
 
 
 def test_three_positional_args_does_not_throw():
@@ -92,8 +111,8 @@
 
 @pytest.mark.parametrize("kwargs", [{"tz_superset": "not a tz"}, {"tz_subset": 
"Europe/Brussels"}])
 def test_with_limited_choices_invalid_choice(ModelChoice, kwargs):
-    m = ModelChoice(**kwargs)
     with pytest.raises(ValidationError):
+        m = ModelChoice(**kwargs)
         m.full_clean()
 
 
@@ -107,6 +126,6 @@
 
 @pytest.mark.parametrize("kwargs", [{"tz_superset": "not a tz"}, {"tz_subset": 
"Europe/Brussels"}])
 def test_with_limited_choices_old_format_invalid_choice(ModelOldChoiceFormat, 
kwargs):
-    m = ModelOldChoiceFormat(**kwargs)
     with pytest.raises(ValidationError):
+        m = ModelOldChoiceFormat(**kwargs)
         m.full_clean()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-timezone-field-6.1.0/tests/test_serializer_field.py 
new/django-timezone-field-7.0/tests/test_serializer_field.py
--- old/django-timezone-field-6.1.0/tests/test_serializer_field.py      
2023-11-26 00:24:33.000000000 +0100
+++ new/django-timezone-field-7.0/tests/test_serializer_field.py        
2024-07-07 20:27:23.000000000 +0200
@@ -13,6 +13,17 @@
     yield _TimeZoneSerializer
 
 
+@pytest.fixture
+def TimeZoneSerializerEmpties(use_pytz):
+    class _TimeZoneSerializer(serializers.Serializer):
+        # pylint: disable=abstract-method
+        tz_allow_null = TimeZoneSerializerField(use_pytz=use_pytz, 
allow_null=True)
+        tz_allow_blank = TimeZoneSerializerField(use_pytz=use_pytz, 
allow_blank=True)
+        tz_not_required = TimeZoneSerializerField(use_pytz=use_pytz, 
required=False)
+
+    yield _TimeZoneSerializer
+
+
 def test_invalid_str(TimeZoneSerializer, invalid_tz):
     serializer = TimeZoneSerializer(data={"tz": invalid_tz})
     assert not serializer.is_valid()
@@ -22,6 +33,15 @@
 def test_empty_str(TimeZoneSerializer):
     serializer = TimeZoneSerializer(data={"tz": ""})
     assert not serializer.is_valid()
+    assert serializer.data == {"tz": ""}
+    assert serializer.validated_data == {}
+
+
+def test_none(TimeZoneSerializer):
+    serializer = TimeZoneSerializer(data={"tz": None})
+    assert not serializer.is_valid()
+    assert serializer.data == {"tz": None}
+    assert serializer.validated_data == {}
 
 
 def test_valid(TimeZoneSerializer, pst, pst_tz):
@@ -41,3 +61,23 @@
     assert serializer.is_valid()
     assert serializer.data["tz"] == pst
     assert serializer.validated_data["tz"] == pst_tz
+
+
+def test_valid_empties(TimeZoneSerializerEmpties):
+    serializer = TimeZoneSerializerEmpties(data={"tz_allow_null": None, 
"tz_allow_blank": ""})
+    assert serializer.is_valid()
+    assert serializer.data == {"tz_allow_null": None, "tz_allow_blank": ""}
+    assert serializer.validated_data == {"tz_allow_null": None, 
"tz_allow_blank": ""}
+
+
+def test_invalid_empties(TimeZoneSerializerEmpties):
+    serializer = TimeZoneSerializerEmpties(
+        data={
+            "tz_allow_null": "",
+            "tz_allow_blank": None,
+            "tz_not_required": None,
+        }
+    )
+    assert not serializer.is_valid()
+    assert serializer.data == {"tz_allow_null": "", "tz_allow_blank": None, 
"tz_not_required": None}
+    assert serializer.validated_data == {}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-timezone-field-6.1.0/timezone_field/__init__.py 
new/django-timezone-field-7.0/timezone_field/__init__.py
--- old/django-timezone-field-6.1.0/timezone_field/__init__.py  2023-11-26 
00:24:33.000000000 +0100
+++ new/django-timezone-field-7.0/timezone_field/__init__.py    2024-07-07 
20:27:23.000000000 +0200
@@ -1,5 +1,5 @@
 from timezone_field.fields import TimeZoneField
 from timezone_field.forms import TimeZoneFormField
 
-__version__ = "6.1.0"
+__version__ = "7.0"
 __all__ = ["TimeZoneField", "TimeZoneFormField"]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-timezone-field-6.1.0/timezone_field/choices.py 
new/django-timezone-field-7.0/timezone_field/choices.py
--- old/django-timezone-field-6.1.0/timezone_field/choices.py   2023-11-26 
00:24:33.000000000 +0100
+++ new/django-timezone-field-7.0/timezone_field/choices.py     2024-07-07 
20:27:23.000000000 +0200
@@ -3,6 +3,28 @@
 from timezone_field.backends import get_tz_backend
 
 
+def normalize_standard(tztuple):
+    """Normalize timezone names by replacing special characters with space.
+
+    For proper sorting, using spaces makes comparisons more consistent.
+
+    :param str tztuple: tuple of timezone and representation
+    """
+    return tztuple[1].translate(str.maketrans({"-": " ", "_": " "}))
+
+
+def normalize_gmt(tztuple):
+    """Normalize timezone GMT names for sorting.
+
+    For proper sorting, using GMT values as a positive or negative number.
+
+    :param str tztuple: tuple of timezone and representation
+    """
+    gmt = tztuple[1].split()[0]
+    cmp = gmt.replace("GMT", "").replace(":", "")
+    return int(cmp)
+
+
 def standard(timezones):
     """
     Given a list of timezones (either strings of timezone objects),
@@ -14,7 +36,7 @@
     for tz in timezones:
         tz_str = str(tz)
         choices.append((tz, tz_str.replace("_", " ")))
-    return choices
+    return sorted(choices, key=normalize_standard)
 
 
 def with_gmt_offset(timezones, now=None, use_pytz=None):
@@ -41,4 +63,4 @@
         _choices.append((delta, tz, display))
     _choices.sort(key=lambda x: x[0])
     choices = [(one, two) for zero, one, two in _choices]
-    return choices
+    return sorted(choices, key=normalize_gmt)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-timezone-field-6.1.0/timezone_field/fields.py 
new/django-timezone-field-7.0/timezone_field/fields.py
--- old/django-timezone-field-6.1.0/timezone_field/fields.py    2023-11-26 
00:24:33.000000000 +0100
+++ new/django-timezone-field-7.0/timezone_field/fields.py      2024-07-07 
20:27:23.000000000 +0200
@@ -4,6 +4,7 @@
 
 from timezone_field.backends import TimeZoneNotFoundError, get_tz_backend
 from timezone_field.choices import standard, with_gmt_offset
+from timezone_field.utils import AutoDeserializedAttribute
 
 
 class TimeZoneField(models.Field):
@@ -35,6 +36,8 @@
     stored as [<timezone object>, <str>].
     """
 
+    descriptor_class = AutoDeserializedAttribute
+
     description = "A timezone object"
 
     # NOTE: these defaults are excluded from migrations. If these are changed,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/django-timezone-field-6.1.0/timezone_field/rest_framework.py 
new/django-timezone-field-7.0/timezone_field/rest_framework.py
--- old/django-timezone-field-6.1.0/timezone_field/rest_framework.py    
2023-11-26 00:24:33.000000000 +0100
+++ new/django-timezone-field-7.0/timezone_field/rest_framework.py      
2024-07-07 20:27:23.000000000 +0200
@@ -1,11 +1,11 @@
 from django.utils.encoding import force_str
 from django.utils.translation import gettext_lazy as _
-from rest_framework.fields import Field
+from rest_framework.fields import CharField
 
 from timezone_field.backends import TimeZoneNotFoundError, get_tz_backend
 
 
-class TimeZoneSerializerField(Field):
+class TimeZoneSerializerField(CharField):
     default_error_messages = {
         "invalid": _("A valid timezone is required."),
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/django-timezone-field-6.1.0/timezone_field/utils.py 
new/django-timezone-field-7.0/timezone_field/utils.py
--- old/django-timezone-field-6.1.0/timezone_field/utils.py     1970-01-01 
01:00:00.000000000 +0100
+++ new/django-timezone-field-7.0/timezone_field/utils.py       2024-07-07 
20:27:23.000000000 +0200
@@ -0,0 +1,19 @@
+from django.db.models.query_utils import DeferredAttribute
+
+
+class AutoDeserializedAttribute(DeferredAttribute):
+    """
+    Use as the descriptor_class for a Django custom field.
+    Allows setting the field to a serialized (typically string) value,
+    and immediately reflecting that as the deserialized `to_python` value.
+
+    (This requires that the field's `to_python` returns the same thing
+    whether called with a serialized or deserialized value.)
+    """
+
+    # (Adapted from django.db.models.fields.subclassing.Creator,
+    # which was included in Django 1.8 and earlier.)
+
+    def __set__(self, instance, value):
+        value = self.field.to_python(value)
+        instance.__dict__[self.field.attname] = value

Reply via email to