I have the following structure of the models using Pydantic library. I created some of the classes, and one of them contains list of items of another, so the problem is that I can't parse json using this classes:
class FileTypeEnum(str, Enum):
file = 'FILE'
folder = 'FOLDER'
class ImportInModel(BaseModel):
id: constr(min_length=1)
url: constr(max_length=255) = None
parentId: str | None = None
size: PositiveInt | None = None
type: FileTypeEnum
@root_validator
def check_url(cls, values: dict):
file_type = values['type']
file_url = values['url']
if file_type == FileTypeEnum.folder and file_url is not None:
raise ValueError(
f'{file_type}\'s type file has {file_url} file url!'
)
@root_validator
def check_size(cls, values: dict):
file_type = values['type']
file_size = values['size']
if file_type == FileTypeEnum.file and file_size is None:
raise ValueError(
f'{file_type}\'s type file has {file_size} file size!'
)
class ImportsInModel(BaseModel):
items: list[ImportInModel]
updateDate: datetime
class Config:
json_encoders = {
datetime: isoformat
}
json_loads = ujson.loads
@validator('items')
def check_parent(cls, v):
for item_1 in v:
for item_2 in v:
print(item_1.type, item_1.parentId, item_2.id, item_2.type)
assert item_1.type == FileTypeEnum.file \
and item_1.parentId == item_2.id \
and item_2.type == FileTypeEnum.folder
But then I use it like
try:
json_raw = '''{
"items": [
{
"id": "элемент_1_4",
"url": "/file/url1",
"parentId": "элемент_1_1",
"size": 234,
"type": "FILE"
}
],
"updateDate": "2022-05-28T21:12:01.000Z"
}
'''
ImportsInModel.parse_raw(json_raw)
# print(json_)
except ValidationError as e:
print(e)
It gives me error:
1 validation error for ImportsInModel items -> 0 ->
__root__
'NoneType' object is not subscriptable (type=type_error)
And I have no idea what's wrong, because it literally copy of the documentation.
CodePudding user response:
You have to return parsed result after validation if success, i.e., you should add return values
in the end of the two root_validator
in ImportInModel
.
For instance, if I run below code, a corresponding error will throw up:
if __name__ == '__main__':
try:
x_raw = '''
{
"id": "элемент_1_4",
"url": "/file/url1",
"parentId": "элемент_1_1",
"size": 234,
"type": "FILE"
}
'''
y = ImportInModel.parse_raw(x_raw)
except ValidationError as e:
print(e)
Traceback (most recent call last):
File "pydantic/main.py", line 344, in pydantic.main.BaseModel.__init__
TypeError: __dict__ must be set to a dictionary, not a 'NoneType'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/will/PycharmProjects/pythonProject2/pydantic_1.py", line 90, in <module>
y = ImportInModel.parse_raw(x_raw)
File "pydantic/main.py", line 549, in pydantic.main.BaseModel.parse_raw
File "pydantic/main.py", line 526, in pydantic.main.BaseModel.parse_obj
File "pydantic/main.py", line 346, in pydantic.main.BaseModel.__init__
TypeError: Model values must be a dict; you may not have returned a dictionary from a root validator
As for the validator in ImportsInModel
, maybe you should check your logic of the code. You codes want to assert item_1.parentId == item_2.id
. However, in your testcase, item_1.parentId
will never equal to item_2.id
since you only have one item in your items
list, which means item_1 == item_2
.
So the final snippet is:
from datetime import datetime
from enum import Enum
import ujson
from pydantic.json import isoformat
from pydantic import BaseModel, constr, PositiveInt, root_validator, validator, ValidationError
class FileTypeEnum(Enum):
file = 'FILE'
folder = 'FOLDER'
class ImportInModel(BaseModel):
id: constr(min_length=1)
url: constr(max_length=255) = None
parentId: str | None = None
size: PositiveInt | None = None
type: FileTypeEnum
@root_validator
def check_url(cls, values: dict):
file_type = values['type']
file_url = values['url']
if file_type == FileTypeEnum.folder and file_url is not None:
raise ValueError(
f'{file_type}\'s type file has {file_url} file url!'
)
return values
@root_validator
def check_size(cls, values: dict):
file_type = values['type']
file_size = values['size']
if file_type == FileTypeEnum.file and file_size is None:
raise ValueError(
f'{file_type}\'s type file has {file_size} file size!'
)
return values
class ImportsInModel(BaseModel):
items: list[ImportInModel]
updateDate: datetime
class Config:
json_encoders = {
datetime: isoformat
}
json_loads = ujson.loads
@validator('items')
def check_parent(cls, v):
for item_1 in v:
for item_2 in v:
print(item_1.type, item_1.parentId, item_2.id, item_2.type)
assert item_1.type == FileTypeEnum.file \
and item_1.parentId == item_2.id \
and item_2.type == FileTypeEnum.folder
if __name__ == '__main__':
try:
json_raw = '''{
"items": [
{
"id": "элемент_1_4",
"url": "/file/url1",
"parentId": "элемент_1_1",
"size": 234,
"type": "FILE"
}
],
"updateDate": "2022-05-28T21:12:01.000Z"
}
'''
x_raw = '''
{
"id": "элемент_1_4",
"url": "/file/url1",
"parentId": "элемент_1_1",
"size": 234,
"type": "FILE"
}
'''
y = ImportInModel.parse_raw(x_raw)
z = ImportsInModel.parse_raw(json_raw)
print(y, z)
except ValidationError as e:
print(e)