From 7562a1788211a710b3a6ee8125e941e252b390bf Mon Sep 17 00:00:00 2001 From: Jose Tomas Robles Hahn Date: Wed, 10 Sep 2025 17:24:43 -0300 Subject: [PATCH 1/6] chore(extras): Add more tests for Django form field for `Rut` --- src/tests/test_extras_dj_form_fields.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/tests/test_extras_dj_form_fields.py b/src/tests/test_extras_dj_form_fields.py index 5f217683..f8463430 100644 --- a/src/tests/test_extras_dj_form_fields.py +++ b/src/tests/test_extras_dj_form_fields.py @@ -19,6 +19,7 @@ def setUpClass(cls) -> None: cls.valid_rut_canonical_instance_with_invalid_dv = Rut( cls.valid_rut_canonical_with_invalid_dv ) + assert not cls.valid_rut_canonical_instance_with_invalid_dv.validate_dv() cls.valid_rut_verbose_leading_zero_lowercase = '060.803.000-k' def test_clean_value_of_invalid_canonical_str(self) -> None: @@ -57,6 +58,18 @@ def test_clean_value_of_rut_str_with_invalid_dv_if_not_validated(self) -> None: self.assertIsInstance(cleaned_value, Rut) self.assertEqual(cleaned_value.canonical, self.valid_rut_canonical_with_invalid_dv) + def test_clean_value_of_rut_instance_with_invalid_dv_if_validated(self) -> None: + rut_field = RutField(validate_dv=True) + with self.assertRaises(django.core.exceptions.ValidationError) as cm: + rut_field.clean(self.valid_rut_canonical_instance_with_invalid_dv) + self.assertEqual(cm.exception.code, 'invalid_dv') + + def test_clean_value_of_rut_instance_with_invalid_dv_if_not_validated(self) -> None: + rut_field = RutField(validate_dv=False) + cleaned_value = rut_field.clean(self.valid_rut_canonical_instance_with_invalid_dv) + self.assertIsInstance(cleaned_value, Rut) + self.assertEqual(cleaned_value, self.valid_rut_canonical_instance_with_invalid_dv) + def test_clean_of_empty_value_if_not_required(self) -> None: rut_field = RutField(required=False) for value in RutField.empty_values: From d8d8c5aef5230aeaf91a6c84f44e168d2824f052 Mon Sep 17 00:00:00 2001 From: Jose Tomas Robles Hahn Date: Wed, 10 Sep 2025 17:46:57 -0300 Subject: [PATCH 2/6] feat(extras): Improve Django filters `FILTER_FOR_DBFIELD_DEFAULTS` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, `cl_sii.extras.dj_filters.FILTER_FOR_DBFIELD_DEFAULTS` consisted of the `FILTER_FOR_DBFIELD_DEFAULTS` from `django_filters`, with the addition of `RutFilter`. From now on, `cl_sii.extras.dj_filters.FILTER_FOR_DBFIELD_DEFAULTS` will only include the filters provided by `cl_sii`, leaving the user to decide whether to merge it with the one from `django_filters`. ⚠️ Warning: Breaking change. --- src/cl_sii/extras/dj_filters.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cl_sii/extras/dj_filters.py b/src/cl_sii/extras/dj_filters.py index 9cde5986..8436fc1b 100644 --- a/src/cl_sii/extras/dj_filters.py +++ b/src/cl_sii/extras/dj_filters.py @@ -23,8 +23,7 @@ import cl_sii.extras.dj_model_fields -FILTER_FOR_DBFIELD_DEFAULTS: Mapping[Type[django.db.models.Field], Mapping[str, object]] -FILTER_FOR_DBFIELD_DEFAULTS = deepcopy(django_filters.filterset.FILTER_FOR_DBFIELD_DEFAULTS) +FILTER_FOR_DBFIELD_DEFAULTS: Mapping[Type[django.db.models.Field], Mapping[str, object]] = {} class RutFilter(django_filters.filters.CharFilter): @@ -90,4 +89,7 @@ class SiiFilterSet(django_filters.filterset.FilterSet): """ FILTER_DEFAULTS: ClassVar[Mapping[Type[django.db.models.Field], Mapping[str, object]]] - FILTER_DEFAULTS = FILTER_FOR_DBFIELD_DEFAULTS + FILTER_DEFAULTS = { + **deepcopy(django_filters.FilterSet.FILTER_DEFAULTS), + **FILTER_FOR_DBFIELD_DEFAULTS, + } From dd7561baa04a236b302346f72e0b1018078989fb Mon Sep 17 00:00:00 2001 From: Jose Tomas Robles Hahn Date: Wed, 10 Sep 2025 16:56:19 -0300 Subject: [PATCH 3/6] fix(extras): Django model field `RutField` does not validate DV of `Rut` In Django model field `RutField`, when field option `validate_dv` is enabled, the `to_python()` method does not validate the "digito verificador" if the input value is already an instance of `Rut`. This commit changes the implementation of `to_python()` so that the "digito verificador" is also validated for `Rut` instances when the `validate_dv` field option is enabled. --- src/cl_sii/extras/dj_model_fields.py | 44 ++++++++++++++---------- src/tests/test_extras_dj_model_fields.py | 37 ++++++++++++++++++++ 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/cl_sii/extras/dj_model_fields.py b/src/cl_sii/extras/dj_model_fields.py index 8013c73d..a03b9f12 100644 --- a/src/cl_sii/extras/dj_model_fields.py +++ b/src/cl_sii/extras/dj_model_fields.py @@ -153,29 +153,37 @@ def to_python(self, value: Optional[object]) -> Optional[Rut]: if the data can't be converted """ - if value is None or isinstance(value, Rut): + if value is None: converted_value = value - else: - try: - converted_value = Rut(value, validate_dv=self.validate_dv) # type: ignore - except (AttributeError, TypeError, ValueError) as exc: - if ( - isinstance(exc, ValueError) - and exc.args - and exc.args[0] == Rut.INVALID_DV_ERROR_MESSAGE - ): - raise django.core.exceptions.ValidationError( - self.error_messages['invalid_dv'], - code='invalid_dv', - params={'value': value}, - ) - + return converted_value + elif isinstance(value, Rut): + if not self.validate_dv: + converted_value = value + return converted_value + elif self.validate_dv and value.validate_dv(raise_exception=False): + converted_value = value + return converted_value + + try: + converted_value = Rut(value, validate_dv=self.validate_dv) # type: ignore + except (AttributeError, TypeError, ValueError) as exc: + if ( + isinstance(exc, ValueError) + and exc.args + and exc.args[0] == Rut.INVALID_DV_ERROR_MESSAGE + ): raise django.core.exceptions.ValidationError( - self.error_messages['invalid'], - code='invalid', + self.error_messages['invalid_dv'], + code='invalid_dv', params={'value': value}, ) + raise django.core.exceptions.ValidationError( + self.error_messages['invalid'], + code='invalid', + params={'value': value}, + ) + return converted_value def value_to_string(self, obj: django.db.models.Model) -> str: diff --git a/src/tests/test_extras_dj_model_fields.py b/src/tests/test_extras_dj_model_fields.py index a62e0a3a..5f7d8c11 100644 --- a/src/tests/test_extras_dj_model_fields.py +++ b/src/tests/test_extras_dj_model_fields.py @@ -11,6 +11,7 @@ class RutFieldTest(unittest.TestCase): valid_rut_canonical: str valid_rut_instance: Rut valid_rut_canonical_with_invalid_dv: str + valid_rut_canonical_instance_with_invalid_dv: Rut valid_rut_verbose_leading_zero_lowercase: str mock_model_instance: django.db.models.Model @@ -19,6 +20,10 @@ def setUpClass(cls) -> None: cls.valid_rut_canonical = '60803000-K' cls.valid_rut_instance = Rut(cls.valid_rut_canonical) cls.valid_rut_canonical_with_invalid_dv = '60803000-0' + cls.valid_rut_canonical_instance_with_invalid_dv = Rut( + cls.valid_rut_canonical_with_invalid_dv + ) + assert not cls.valid_rut_canonical_instance_with_invalid_dv.validate_dv() cls.valid_rut_verbose_leading_zero_lowercase = '060.803.000-k' cls.mock_model_instance = unittest.mock.create_autospec( django.db.models.Model, instance=True @@ -43,6 +48,14 @@ def test_get_prep_value_of_None(self) -> None: prepared_value = RutField().get_prep_value(None) self.assertIsNone(prepared_value) + def test_clean_value_with_invalid_type(self) -> None: + for value_with_invalid_type in [12345678, 12.34, [], {}, object()]: + with self.subTest(value=value_with_invalid_type): + rut_field = RutField() + with self.assertRaises(django.core.exceptions.ValidationError) as cm: + rut_field.clean(value_with_invalid_type, self.mock_model_instance) + self.assertEqual(cm.exception.code, 'invalid') + def test_clean_value_of_rut_str_with_invalid_dv_if_validated(self) -> None: rut_field = RutField(validate_dv=True) with self.assertRaises(django.core.exceptions.ValidationError) as cm: @@ -57,6 +70,30 @@ def test_clean_value_of_rut_str_with_invalid_dv_if_not_validated(self) -> None: self.assertIsInstance(cleaned_value, Rut) self.assertEqual(cleaned_value.canonical, self.valid_rut_canonical_with_invalid_dv) + def test_clean_value_of_rut_instance_with_valid_dv(self) -> None: + for validate_dv in [True, False]: + with self.subTest(validate_dv=validate_dv): + rut_field = RutField(validate_dv=validate_dv) + cleaned_value = rut_field.clean(self.valid_rut_instance, self.mock_model_instance) + self.assertIsInstance(cleaned_value, Rut) + self.assertEqual(cleaned_value, self.valid_rut_instance) + + def test_clean_value_of_rut_instance_with_invalid_dv_if_validated(self) -> None: + rut_field = RutField(validate_dv=True) + with self.assertRaises(django.core.exceptions.ValidationError) as cm: + rut_field.clean( + self.valid_rut_canonical_instance_with_invalid_dv, self.mock_model_instance + ) + self.assertEqual(cm.exception.code, 'invalid_dv') + + def test_clean_value_of_rut_instance_with_invalid_dv_if_not_validated(self) -> None: + rut_field = RutField(validate_dv=False) + cleaned_value = rut_field.clean( + self.valid_rut_canonical_instance_with_invalid_dv, self.mock_model_instance + ) + self.assertIsInstance(cleaned_value, Rut) + self.assertEqual(cleaned_value, self.valid_rut_canonical_instance_with_invalid_dv) + def test_deconstruct_without_options(self) -> None: name, path, args, kwargs = RutField().deconstruct() self.assertEqual(path, 'cl_sii.extras.dj_model_fields.RutField') From ae8d73b178e98f5ee6655cb33e0129ec37925745 Mon Sep 17 00:00:00 2001 From: Jose Tomas Robles Hahn Date: Wed, 10 Sep 2025 18:14:34 -0300 Subject: [PATCH 4/6] chore(deps): Update `cryptography` from 45.0.4 to 45.0.7 - [Software Repository](https://pypi.org/project/cryptography/45.0.7/) - ~~[Release Notes]()~~ - [Changelog](https://github.com/pyca/cryptography/blob/45.0.7/CHANGELOG.rst#4507---2025-09-01) - [Commits](https://github.com/pyca/cryptography/compare/45.0.4...45.0.7) --- requirements-dev.txt | 2 +- requirements.in | 2 +- requirements.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index b1b91aa6..d6e3f988 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -42,7 +42,7 @@ colorama==0.4.6 # via tox coverage==7.10.6 # via -r requirements-dev.in -cryptography==45.0.4 +cryptography==45.0.7 # via # -c requirements.txt # secretstorage diff --git a/requirements.in b/requirements.in index 9fa9d261..71fa70e0 100644 --- a/requirements.in +++ b/requirements.in @@ -6,7 +6,7 @@ # git+https://github.com/example/example.git@example-vcs-ref#egg=example-pkg[foo,bar]==1.42.3 backports-zoneinfo==0.2.1 ; python_version < "3.9" # Used by `djangorestframework`. -cryptography==45.0.4 +cryptography==45.0.7 defusedxml==0.7.1 django-filter>=24.2 Django>=4.2 diff --git a/requirements.txt b/requirements.txt index d02f8dcc..cbc045f0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,7 @@ certifi==2024.7.4 # via signxml cffi==1.16.0 # via cryptography -cryptography==45.0.4 +cryptography==45.0.7 # via # -r requirements.in # pyopenssl From f49220bb5536edd7a5c0584fa45d3df0ef266fc6 Mon Sep 17 00:00:00 2001 From: Jose Tomas Robles Hahn Date: Wed, 10 Sep 2025 18:24:17 -0300 Subject: [PATCH 5/6] chore: Update history for new version --- HISTORY.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 9fd382db..70521948 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,12 @@ # History +## 0.56.0 (2025-09-10) + +- (PR #883, 2025-09-10) extras: Add more tests for Django form field for `Rut` +- (PR #884, 2025-09-10) extras: Improve Django filters `FILTER_FOR_DBFIELD_DEFAULTS` +- (PR #882, 2025-09-10) extras: Django model field `RutField` does not validate DV of `Rut` +- (PR #885, 2025-09-10) deps: Update `cryptography` from 45.0.4 to 45.0.7 + ## 0.55.0 (2025-09-10) - (PR #878, 2025-09-10) extras: Improve Django filter for `Rut` From 88a6c5756b2bdc8beec69c995f7d422e31803527 Mon Sep 17 00:00:00 2001 From: Jose Tomas Robles Hahn Date: Wed, 10 Sep 2025 18:24:36 -0300 Subject: [PATCH 6/6] chore: Bump version from 0.55.0 to 0.56.0 --- .bumpversion.cfg | 2 +- src/cl_sii/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 89f6363c..5963bc5f 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.55.0 +current_version = 0.56.0 commit = True tag = False message = chore: Bump version from {current_version} to {new_version} diff --git a/src/cl_sii/__init__.py b/src/cl_sii/__init__.py index 9b305a08..c7410bcf 100644 --- a/src/cl_sii/__init__.py +++ b/src/cl_sii/__init__.py @@ -4,4 +4,4 @@ """ -__version__ = '0.55.0' +__version__ = '0.56.0'