Hi @samuelcolvin , Thank you for this amazing library. I am using it with fastapi for defining input models.
"preview": {
"9:16": {
"thumbnail": "",
"video": ""
}, "16:9": {
"thumbnail": "",
"video": ""
}, "1:1": {
"thumbnail": "",
"video": ""
}
}
I have the above data coming in the request for which I want to create a model. I tried implementing wildcard fields but didn't get any success. In the above case, the aspect ratio is dynamic and can be anything but will always be in : format. In case that is coming, thumbnail and video are in nested model.
I have implemented this in JSON schema like the following:
template_schema = {
"type": "object",
"properties": {
"preview": {
"minProperties": 1,
"type": "object",
"patternProperties": {
"^[0-9]{1,2}:\d{1,2}$": {
"type": "object",
"properties": {
"thumbnail": {"type": "string"},
"video": {"type": "string"}
},
"required": ["thumbnail", "video"],
}
},
},
}
}
Please guide me on how to get it to work?
Any leads would be appreciated. Thanks in advance!
If you want change the input data to confirm to a more conventional shape, you'd be best of with something like:
class ChildModel(BaseModel):
thumbnail: str
video: str
class MainModel(BaseModel):
__root__: Dict[str, ChildModel]
Then you'll need a validator to check the format of the keys.
You'll then need to manually customise the schema
Hi @samuelcolvin, thank you for reverting back so quick. I tried to follow the link and the example shared you above. but wasn't able to achieve the use case as mentioned above. Can you please help me with this? A code example will be a great help.
I get the following error using your code snippet
TypeError: custom root type cannot allow mapping
Thanks in advance!
Bump!
Sorry, I forgot dictionaries can't be used as the root type.
You need to use a key for the dictionaries of video formats, in your above example that's fine since you have {"preview: {...}}.
With that you might want something like:
from typing import Dict
from pydantic import BaseModel, validator
from devtools import debug
class ChildModel(BaseModel):
thumbnail: str
video: str
class MainModel(BaseModel):
preview: Dict[str, ChildModel]
@validator('preview')
def check_keys(cls, v):
for i, k in enumerate(v):
if ':' not in k:
raise ValueError(f'{i} key {k!r} does not contain a colon (:)')
return v
data = {
'preview': {
'9:16': {'thumbnail': '', 'video': ''},
'16:9': {'thumbnail': '', 'video': ''},
'1:1': {'thumbnail': '', 'video': ''},
'bad': {'thumbnail': '', 'video': ''},
}
}
m = MainModel(**data)
debug(m)
Will raise an error as one of the keys doesn't contain a colon.
If you're data is just a dictionary of the form {"9:16": {}, "16:9": {}, ...}, you would need to initialise with something like MainModel(preview=data).
@samuelcolvin Is there a fundamental reason we don't support root mappings, or is it just because this way was easier to implement for now? I recognize it may require adding an unacceptable amount of added complexity to support a mapping type in the root; I'm just trying to understand whether this restriction is more purposeful or accidental.
I think the problem is parse_obj:
Might be possible to fix, but that was why we did it.
Yeah, I think it could make sense to replace that with a root model check; it feels kind of arbitrarily limiting to not support mappings as the root model.
On the other hand, I don't love adding all of these runtime checks when in most cases they aren't relevant; maybe there is a way to essentially decorate/replace this method inside the metaclass to get different behavior (if the model is a custom root model with a mapping type)?
(I guess maybe this is starting to get outside the scope of this issue though.)
Hello there,
i'm quite interested in this feature too. I'm getting a payload I don't control the schema. The payload is an object where the keys are all unknown (and of a various format). I wanted to use a mapping as a root type but not sure how to go about it until this has a chance to be reviewed/merged/released.
Cheers
Thanks for your interest, see the discussion on #958 as it happens I'm reviewing the last comment right now and working out what to do.