Skip to content

Commit 8e0bb6d

Browse files
committed
48 | Updated IsDataclassValidator to also check against their types, including nested dataclasses, lists, and dictionaries
1 parent 15ef8b7 commit 8e0bb6d

17 files changed

+359
-133
lines changed

docs/source/changelog.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@ Changelog
33

44
All notable changes to this project will be documented in this file.
55

6+
[0.5.4] - 2025-05-24
7+
--------------------
8+
9+
Added
10+
^^^^^
11+
- Added ``ArrayElementFilter`` to filter elements in an array against specific filter.
12+
13+
Changed
14+
^^^^^^^
15+
- Updated ``ArrayElementValidator`` to support validators directly.
16+
- Updated ``IsDataclassValidator`` to also check against their types, including nested dataclasses, lists, and dictionaries.
17+
18+
619
[0.5.3] - 2025-04-28
720
--------------------
821

docs/source/options/filter.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Example
3636
Available Filters
3737
-----------------
3838

39+
- `ArrayElementFilter <#flask_inputfilter.filters.ArrayElementFilter>`_
3940
- `ArrayExplodeFilter <#flask_inputfilter.filters.ArrayExplodeFilter>`_
4041
- `Base64ImageDownscaleFilter <#flask_inputfilter.filters.Base64ImageDownscaleFilter>`_
4142
- `Base64ImageResizeFilter <#flask_inputfilter.filters.Base64ImageResizeFilter>`_

examples/example 1/README.md

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,14 @@ This example demonstrates various use cases of the Flask-InputFilter package, sh
66

77
```
88
example 1/
9-
├── app.py # Main Flask application
10-
├── filters/ # InputFilter classes
11-
│ ├── __init__.py # Package exports
12-
│ ├── user_filter.py # User input validation
13-
│ ├── address_filter.py # Address input validation
14-
│ ├── profile_filter.py # Profile input validation
15-
│ └── product_filter.py # Product input validation
16-
├── test.http # HTTP test requests
17-
└── README.md # This file
9+
├── app.py # Main Flask application
10+
├── filters/ # InputFilter classes
11+
│ ├── __init__.py # Package exports
12+
│ ├── product_inputfilter.py # Product input validation
13+
│ ├── profile_inputfilter.py # Profile input validation
14+
│ └── user_inputfilter.py # User input validation
15+
├── test.http # HTTP test requests
16+
└── README.md # This file
1817
```
1918

2019
## Features Demonstrated
@@ -29,12 +28,12 @@ example 1/
2928

3029
1. Make sure you have Flask and Flask-InputFilter installed:
3130
```bash
32-
pip install flask flask-inputfilter
31+
pip install flask flask-inputfilter
3332
```
3433

3534
2. Run the example application:
3635
```bash
37-
python app.py
36+
python app.py
3837
```
3938

4039
The server will start on `http://localhost:5000`.
@@ -55,18 +54,19 @@ You can use the provided `test.http` file to test the endpoints. This file conta
5554
- Demonstrates nested filtering with multiple InputFilter classes
5655
- Optional phone number field
5756

58-
3. `POST /api/products`
57+
3. `POST /api/product`
5958
- Creates a new product
60-
- Demonstrates basic type conversion with ToFloatFilter
61-
- Optional tags field
59+
- Demonstrates list validation for tags
60+
- Required fields: name, price
61+
- Optional tags field as a list of strings
6262

6363
## Example Requests
6464

6565
### Successful User Creation
6666
```json
6767
{
6868
"name": "John Doe",
69-
"age": "30",
69+
"age": 30,
7070
"email": "[email protected]"
7171
}
7272
```
@@ -76,13 +76,13 @@ You can use the provided `test.http` file to test the endpoints. This file conta
7676
{
7777
"user": {
7878
"name": "John Doe",
79-
"age": "30",
79+
"age": 30,
8080
"email": "[email protected]"
8181
},
8282
"address": {
8383
"street": "123 Main St",
8484
"city": "New York",
85-
"zip_code": "10001"
85+
"zip_code": 10001
8686
},
8787
"phone": "+1234567890"
8888
}
@@ -92,8 +92,11 @@ You can use the provided `test.http` file to test the endpoints. This file conta
9292
```json
9393
{
9494
"name": "Laptop",
95-
"price": "999.99",
96-
"tags": "electronics,computers,gadgets"
95+
"price": 999,
96+
"tags": [
97+
"electronics",
98+
"sports"
99+
]
97100
}
98101
```
99102

@@ -138,9 +141,9 @@ You can use the provided `test.http` file to test the endpoints. This file conta
138141
The application demonstrates various validation errors:
139142
- Missing required fields
140143
- Invalid email format
141-
- Invalid zip code format
142-
- Invalid phone number format
143-
- Negative price values
144-
- Invalid data types
144+
- Invalid age format (must be integer)
145+
- Invalid price format
146+
- Invalid tags format (must be a list of strings)
147+
- Invalid nested data structures
145148

146149
Each error will return a 400 status code with a descriptive error message.

examples/example 1/app.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,42 @@
1-
from flask import Flask, jsonify, g
2-
from .filters import UserInputFilter, ProfileInputFilter, ProductInputFilter
1+
from flask import Flask, Response, g
2+
3+
from .filters import ProductInputFilter, ProfileInputFilter, UserInputFilter
34

45
app = Flask(__name__)
56

6-
@app.route('/api/user', methods=['POST'], endpoint='create-user')
7+
8+
@app.route("/api/user", methods=["POST"], endpoint="create-user")
79
@UserInputFilter.validate()
810
def create_user():
9-
return jsonify({"message": "User created successfully", "data": g.validated_data}), 201
11+
return Response(
12+
{"message": "User created successfully", "data": g.validated_data},
13+
201,
14+
)
15+
1016

11-
@app.route('/api/profile', methods=['POST'], endpoint='create-profile')
17+
@app.route("/api/profile", methods=["POST"], endpoint="create-profile")
1218
@ProfileInputFilter.validate()
1319
def create_profile():
14-
return jsonify({"message": "Profile created successfully", "data": g.validated_data}), 201
20+
return Response(
21+
{
22+
"message": "Profile created successfully",
23+
"data": g.validated_data,
24+
},
25+
201,
26+
)
1527

16-
@app.route('/api/product', methods=['POST'], endpoint='create-product')
28+
29+
@app.route("/api/product", methods=["POST"], endpoint="create-product")
1730
@ProductInputFilter.validate()
1831
def create_products():
19-
return jsonify({"message": "Products created successfully", "data": g.validated_data}), 201
32+
return Response(
33+
{
34+
"message": "Products created successfully",
35+
"data": g.validated_data,
36+
},
37+
201,
38+
)
39+
2040

21-
if __name__ == '__main__':
41+
if __name__ == "__main__":
2242
app.run(debug=True)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from .product_inputfilter import ProductInputFilter
22
from .profile_inputfilter import ProfileInputFilter
3-
from .user_inputfilter import UserInputFilter
3+
from .user_inputfilter import UserInputFilter

examples/example 1/filters/product_inputfilter.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,50 +2,50 @@
22

33
from flask_inputfilter import InputFilter
44
from flask_inputfilter.filters import ToFloatFilter
5-
from flask_inputfilter.validators import IsStringValidator, IsIntegerValidator, InEnumValidator, IsArrayValidator, \
6-
ArrayElementValidator, IsFloatValidator
5+
from flask_inputfilter.validators import (
6+
ArrayElementValidator,
7+
InEnumValidator,
8+
IsArrayValidator,
9+
IsFloatValidator,
10+
IsStringValidator,
11+
)
712

813

914
class Tags(Enum):
10-
ELECTRONICS = 'electronics'
11-
FASHION = 'fashion'
12-
HOME = 'home'
13-
BEAUTY = 'beauty'
14-
SPORTS = 'sports'
15-
TOYS = 'toys'
15+
ELECTRONICS = "electronics"
16+
FASHION = "fashion"
17+
HOME = "home"
18+
BEAUTY = "beauty"
19+
SPORTS = "sports"
20+
TOYS = "toys"
21+
1622

1723
class ProductInputFilter(InputFilter):
1824
def __init__(self):
1925
super().__init__()
2026

2127
self.add(
22-
'name',
28+
"name",
2329
required=True,
2430
validators=[
2531
IsStringValidator(),
26-
]
32+
],
2733
)
2834

2935
self.add(
30-
'price',
36+
"price",
3137
required=True,
32-
filters=[
33-
ToFloatFilter()
34-
],
38+
filters=[ToFloatFilter()],
3539
validators=[
3640
IsFloatValidator(),
37-
]
41+
],
3842
)
3943

4044
self.add(
41-
'tags',
45+
"tags",
4246
required=False,
4347
validators=[
4448
IsArrayValidator(),
45-
ArrayElementValidator(
46-
InEnumValidator(
47-
Tags
48-
)
49-
)
50-
]
49+
ArrayElementValidator(InEnumValidator(Tags)),
50+
],
5151
)
Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
from dataclasses import dataclass
22

33
from flask_inputfilter import InputFilter
4-
from flask_inputfilter.validators import IsStringValidator, IsDataclassValidator
4+
from flask_inputfilter.validators import (
5+
IsDataclassValidator,
6+
IsStringValidator,
7+
)
58

69

710
@dataclass
811
class User:
9-
name: int
12+
name: str
1013
age: int
1114
email: str
1215

@@ -23,29 +26,15 @@ def __init__(self):
2326
super().__init__()
2427

2528
self.add(
26-
'user',
27-
required=True,
28-
validators=[
29-
IsDataclassValidator(
30-
dataclass_type=User
31-
)
32-
]
33-
)
29+
"user",
30+
required=True,
31+
validators=[IsDataclassValidator(dataclass_type=User)],
32+
)
3433

3534
self.add(
36-
'address',
37-
required=True,
38-
validators=[
39-
IsDataclassValidator(
40-
dataclass_type=Address
41-
)
42-
]
43-
)
35+
"address",
36+
required=True,
37+
validators=[IsDataclassValidator(dataclass_type=Address)],
38+
)
4439

45-
self.add(
46-
'phone',
47-
required=False,
48-
validators=[
49-
IsStringValidator()
50-
]
51-
)
40+
self.add("phone", required=False, validators=[IsStringValidator()])

examples/example 1/filters/user_inputfilter.py

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,8 @@ class UserInputFilter(InputFilter):
66
def __init__(self):
77
super().__init__()
88

9-
self.add(
10-
'name',
11-
required=True,
12-
validators=[
13-
IsStringValidator()
14-
]
15-
)
9+
self.add("name", required=True, validators=[IsStringValidator()])
1610

17-
self.add(
18-
'age',
19-
required=True,
20-
validators=[
21-
IsIntegerValidator()
22-
]
23-
)
11+
self.add("age", required=True, validators=[IsIntegerValidator()])
2412

25-
self.add(
26-
'email',
27-
required=True,
28-
validators=[
29-
IsStringValidator()
30-
]
31-
)
13+
self.add("email", required=True, validators=[IsStringValidator()])

flask_inputfilter/filters/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from flask_inputfilter.filters.base_filter import BaseFilter
22

3+
from .array_element_filter import ArrayElementFilter
34
from .array_explode_filter import ArrayExplodeFilter
45
from .base_64_image_downscale_filter import Base64ImageDownscaleFilter
56
from .base_64_image_resize_filter import Base64ImageResizeFilter
@@ -31,7 +32,7 @@
3132
from .whitespace_collapse_filter import WhitespaceCollapseFilter
3233

3334
__all__ = [
34-
"ArrayExplodeFilter",
35+
"ArrayElementFilter" "ArrayExplodeFilter",
3536
"Base64ImageDownscaleFilter",
3637
"Base64ImageResizeFilter",
3738
"BaseFilter",

0 commit comments

Comments
 (0)