Skip to content

Commit

Permalink
Fixed missing validation errors (#42) πŸ‘¨πŸ½β€πŸ«
Browse files Browse the repository at this point in the history
* DictionaryField exception handling

* Fixed missing validation errors

* Added valid nested forms test

* Fixed flake8 codestyle

* Removed str option from Form.add_error() argument
  • Loading branch information
Serbel97 authored Sep 8, 2022
1 parent 2d1a9dc commit de65602
Show file tree
Hide file tree
Showing 10 changed files with 527 additions and 70 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 1.0.0-rc.4 : 06.09.2022

- **Fixed**: Fixed missing validation errors
- **Changed**: `Form.add_error()` now takes only `Tuple` as a `field` argument

## 1.0.0-rc.3 : 04.09.2022

- **Fixed**: Removed validation of non-required fields if they are not present in the request
Expand Down
10 changes: 7 additions & 3 deletions django_api_forms/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,19 @@ class UnsupportedMediaType(ApiFormException):

class DetailValidationError(ValidationError):
def __init__(self, error: ValidationError, path: Tuple):
super().__init__(error.message, error.code, error.params)
if not hasattr(error, 'message') and isinstance(error.error_list, list):
for item in error.error_list:
item.path = path

super().__init__(error)
self._path = path

@property
def path(self) -> Tuple:
return self._path

def prepend(self, key: str):
self._path = (key, ) + self._path
def prepend(self, key: Tuple):
self._path = key + self._path

def to_list(self) -> list:
return list(self.path)
Expand Down
2 changes: 1 addition & 1 deletion django_api_forms/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def to_python(self, value):
result.append(form.cleaned_data)
else:
for error in form.errors:
error.prepend(position)
error.prepend((position, ))
errors.append(error)

if errors:
Expand Down
21 changes: 14 additions & 7 deletions django_api_forms/forms.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import copy
from typing import Union, List, Tuple
from typing import List, Tuple

from django.core.exceptions import ValidationError
from django.forms import fields_for_model
Expand Down Expand Up @@ -95,18 +95,25 @@ def errors(self) -> dict:
def is_valid(self) -> bool:
return not self.errors

def add_error(self, field: Union[str, Tuple], errors: ValidationError):
def add_error(self, field: Tuple, errors: ValidationError):
if hasattr(errors, 'error_dict'):
for item in errors.error_dict.values():
for error in item:
for key, items in errors.error_dict.items():
for error in items:
if isinstance(error, DetailValidationError):
error.prepend(field)
self.add_error(error.path, error)
elif isinstance(error, ValidationError):
self.add_error(field + (key, ), error)
elif not hasattr(errors, 'message') and isinstance(errors.error_list, list):
for item in errors.error_list:
if isinstance(item, DetailValidationError):
item.prepend(field)
self.add_error(item.path, item)
elif isinstance(item, ValidationError):
path = field
if hasattr(item, 'path'):
path = field + item.path
self.add_error(path, item)
else:
self._errors.append(
DetailValidationError(errors, (field,) if isinstance(field, str) else field)
Expand All @@ -132,13 +139,13 @@ def full_clean(self):
if hasattr(self, f"clean_{key}"):
self.cleaned_data[key] = getattr(self, f"clean_{key}")()
except ValidationError as e:
self.add_error(key, e)
self.add_error((key, ), e)
except (AttributeError, TypeError, ValueError):
self.add_error(key, ValidationError(_("Invalid value")))
self.add_error((key, ), ValidationError(_("Invalid value")))
try:
cleaned_data = self.clean()
except ValidationError as e:
self.add_error('$body', e)
self.add_error(('$body', ), e)
else:
if cleaned_data is not None:
self.cleaned_data = cleaned_data
Expand Down
6 changes: 3 additions & 3 deletions docs/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ This process is much more simple than in classic Django form. It consists of:
1. Iterating over form attributes:
- calling `Field.clean(value)` method
- calling `Form.clean_<field_name>` method
- calling `Form.add_error(field_name, error)` in case of failures in clean methods
- calling `Form.add_error((field_name, ), error)` in case of failures in clean methods
- if field is marked as dirty, normalized attribute is saved to `Form.clean_data` property
2. Calling `Form.clean` method which returns final normalized values which will be presented in `Form.clean_data`
(feel free to override it, by default does nothing, useful for conditional validation, you can still add errors u
Expand All @@ -95,7 +95,7 @@ Validation errors are presented for each field in `Form.errors: List[ValidationE
As was mentioned above, you can extend property validation or normalisation by creating form method like
`clean_<property_name>`. You can add additional
[ValidationError](https://docs.djangoproject.com/en/3.1/ref/forms/validation/#raising-validationerror)
objects using `Form.add_error(field: Union[str, Tuple], error: ValidationError)` method. Result is final normalised
objects using `Form.add_error(field: Tuple, error: ValidationError)` method. Result is final normalised
value of the attribute.

```python
Expand All @@ -109,7 +109,7 @@ class BookForm(Form):

def clean_title(self):
if self.cleaned_data['title'] == "The Hitchhiker's Guide to the Galaxy":
self.add_error('title', ValidationError("Too cool!", code='too-cool'))
self.add_error(('title', ), ValidationError("Too cool!", code='too-cool'))
return self.cleaned_data['title'].upper()

def clean(self):
Expand Down
Loading

0 comments on commit de65602

Please sign in to comment.