add also basic examples for native pydantic fields including models to not skip them
This commit is contained in:
@ -1,8 +1,9 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import decimal
|
import decimal
|
||||||
|
import numbers
|
||||||
import uuid
|
import uuid
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any, Dict, List, TYPE_CHECKING, Tuple, Type
|
from typing import Any, Dict, List, Set, TYPE_CHECKING, Tuple, Type, Union
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import orjson as json
|
import orjson as json
|
||||||
@ -138,15 +139,79 @@ def generate_model_example(model: Type["Model"], relation_map: Dict = None) -> D
|
|||||||
if not field.is_relation:
|
if not field.is_relation:
|
||||||
example[name] = field.__sample__
|
example[name] = field.__sample__
|
||||||
elif isinstance(relation_map, dict) and name in relation_map:
|
elif isinstance(relation_map, dict) and name in relation_map:
|
||||||
value = generate_model_example(
|
example[name] = get_nested_model_example(
|
||||||
field.to, relation_map=relation_map.get(name, {})
|
name=name, field=field, relation_map=relation_map
|
||||||
)
|
)
|
||||||
new_value = [value] if field.is_multi or field.virtual else value
|
to_exclude = {name for name in model.Meta.model_fields}
|
||||||
example[name] = new_value
|
pydantic_repr = generate_pydantic_example(pydantic_model=model, exclude=to_exclude)
|
||||||
|
example.update(pydantic_repr)
|
||||||
|
|
||||||
return example
|
return example
|
||||||
|
|
||||||
|
|
||||||
|
def get_nested_model_example(
|
||||||
|
name: str, field: "BaseField", relation_map: Dict
|
||||||
|
) -> Union[List, Dict]:
|
||||||
|
"""
|
||||||
|
Gets representation of nested model.
|
||||||
|
|
||||||
|
:param name: name of the field to follow
|
||||||
|
:type name: str
|
||||||
|
:param field: ormar field
|
||||||
|
:type field: BaseField
|
||||||
|
:param relation_map: dict with relation map
|
||||||
|
:type relation_map: Dict
|
||||||
|
:return: nested model or list of nested model repr
|
||||||
|
:rtype: Union[List, Dict]
|
||||||
|
"""
|
||||||
|
value = generate_model_example(field.to, relation_map=relation_map.get(name, {}))
|
||||||
|
new_value: Union[List, Dict] = [value] if field.is_multi or field.virtual else value
|
||||||
|
return new_value
|
||||||
|
|
||||||
|
|
||||||
|
def generate_pydantic_example(
|
||||||
|
pydantic_model: Type[pydantic.BaseModel], exclude: Set = None
|
||||||
|
) -> Dict:
|
||||||
|
"""
|
||||||
|
Generates dict with example.
|
||||||
|
|
||||||
|
:param pydantic_model: model to parse
|
||||||
|
:type pydantic_model: Type[pydantic.BaseModel]
|
||||||
|
:param exclude: list of fields to exclude
|
||||||
|
:type exclude: Optional[Set]
|
||||||
|
:return: dict with fields and sample values
|
||||||
|
:rtype: Dict
|
||||||
|
"""
|
||||||
|
example: Dict[str, Any] = dict()
|
||||||
|
exclude = exclude or set()
|
||||||
|
for name in pydantic_model.__fields__:
|
||||||
|
if name not in exclude:
|
||||||
|
field = pydantic_model.__fields__[name]
|
||||||
|
type_ = field.type_
|
||||||
|
if getattr(field.outer_type_, "_name", None) == "List":
|
||||||
|
example[name] = [get_pydantic_example_repr(type_)]
|
||||||
|
else:
|
||||||
|
example[name] = get_pydantic_example_repr(type_)
|
||||||
|
return example
|
||||||
|
|
||||||
|
|
||||||
|
def get_pydantic_example_repr(type_: Any) -> Any:
|
||||||
|
"""
|
||||||
|
Gets sample representation of pydantic field for example dict.
|
||||||
|
|
||||||
|
:param type_: type of pydantic field
|
||||||
|
:type type_: Any
|
||||||
|
:return: representation to include in example
|
||||||
|
:rtype: Any
|
||||||
|
"""
|
||||||
|
if issubclass(type_, (numbers.Number, decimal.Decimal)):
|
||||||
|
return 0
|
||||||
|
elif issubclass(type_, pydantic.BaseModel):
|
||||||
|
return generate_pydantic_example(pydantic_model=type_)
|
||||||
|
else:
|
||||||
|
return "string"
|
||||||
|
|
||||||
|
|
||||||
def construct_modify_schema_function(fields_with_choices: List) -> SchemaExtraCallable:
|
def construct_modify_schema_function(fields_with_choices: List) -> SchemaExtraCallable:
|
||||||
"""
|
"""
|
||||||
Modifies the schema to include fields with choices validator.
|
Modifies the schema to include fields with choices validator.
|
||||||
|
|||||||
@ -275,12 +275,12 @@ class SavePrepareMixin(RelationMixin, AliasMixin):
|
|||||||
:rtype: int
|
:rtype: int
|
||||||
"""
|
"""
|
||||||
for field in fields_list:
|
for field in fields_list:
|
||||||
value = getattr(self, field.name) or []
|
values = getattr(self, field.name) or []
|
||||||
if not isinstance(value, list):
|
if not isinstance(values, list):
|
||||||
value = [value]
|
values = [values]
|
||||||
for val in value:
|
for value in values:
|
||||||
if follow:
|
if follow:
|
||||||
update_count = await val.save_related(
|
update_count = await value.save_related(
|
||||||
follow=follow,
|
follow=follow,
|
||||||
save_all=save_all,
|
save_all=save_all,
|
||||||
relation_map=self._skip_ellipsis( # type: ignore
|
relation_map=self._skip_ellipsis( # type: ignore
|
||||||
@ -291,8 +291,8 @@ class SavePrepareMixin(RelationMixin, AliasMixin):
|
|||||||
relation_field=field,
|
relation_field=field,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
update_count = await val._upsert_model(
|
update_count = await value._upsert_model(
|
||||||
instance=val,
|
instance=value,
|
||||||
save_all=save_all,
|
save_all=save_all,
|
||||||
previous_model=self,
|
previous_model=self,
|
||||||
relation_field=field,
|
relation_field=field,
|
||||||
|
|||||||
@ -316,11 +316,7 @@ class NewBaseModel(pydantic.BaseModel, ModelTableProxy, metaclass=ModelMetaclass
|
|||||||
k,
|
k,
|
||||||
model_fields[k].expand_relationship(v, self, to_register=False,)
|
model_fields[k].expand_relationship(v, self, to_register=False,)
|
||||||
if k in model_fields
|
if k in model_fields
|
||||||
else (
|
else (v if k in pydantic_fields else model_fields[k]),
|
||||||
v
|
|
||||||
if k in pydantic_fields
|
|
||||||
else model_fields["HAP&*YA^)*GW^&QT6567q56gGG%$%"]
|
|
||||||
),
|
|
||||||
"dumps",
|
"dumps",
|
||||||
)
|
)
|
||||||
for k, v in kwargs.items()
|
for k, v in kwargs.items()
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
from typing import List
|
import datetime
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
import databases
|
import databases
|
||||||
import pydantic
|
import pydantic
|
||||||
@ -35,6 +36,17 @@ class LocalMeta:
|
|||||||
database = database
|
database = database
|
||||||
|
|
||||||
|
|
||||||
|
class PTestA(pydantic.BaseModel):
|
||||||
|
c: str
|
||||||
|
d: bytes
|
||||||
|
e: datetime.datetime
|
||||||
|
|
||||||
|
|
||||||
|
class PTestP(pydantic.BaseModel):
|
||||||
|
a: int
|
||||||
|
b: Optional[PTestA]
|
||||||
|
|
||||||
|
|
||||||
class Category(ormar.Model):
|
class Category(ormar.Model):
|
||||||
class Meta(LocalMeta):
|
class Meta(LocalMeta):
|
||||||
tablename = "categories"
|
tablename = "categories"
|
||||||
@ -49,6 +61,8 @@ class Item(ormar.Model):
|
|||||||
|
|
||||||
id: int = ormar.Integer(primary_key=True)
|
id: int = ormar.Integer(primary_key=True)
|
||||||
name: str = ormar.String(max_length=100)
|
name: str = ormar.String(max_length=100)
|
||||||
|
pydantic_int: Optional[int]
|
||||||
|
test_P: Optional[List[PTestP]]
|
||||||
categories = ormar.ManyToMany(Category)
|
categories = ormar.ManyToMany(Category)
|
||||||
|
|
||||||
|
|
||||||
@ -126,16 +140,27 @@ def test_schema_modification():
|
|||||||
)
|
)
|
||||||
assert schema["properties"]["categories"]["title"] == "Categories"
|
assert schema["properties"]["categories"]["title"] == "Categories"
|
||||||
assert schema["example"] == {
|
assert schema["example"] == {
|
||||||
|
"categories": [{"id": 0, "name": "string"}],
|
||||||
"id": 0,
|
"id": 0,
|
||||||
"name": "string",
|
"name": "string",
|
||||||
"categories": [{"id": 0, "name": "string"}],
|
"pydantic_int": 0,
|
||||||
|
"test_P": [{"a": 0, "b": {"c": "string", "d": "string", "e": "string"}}],
|
||||||
}
|
}
|
||||||
|
|
||||||
schema = Category.schema()
|
schema = Category.schema()
|
||||||
assert schema["example"] == {
|
assert schema["example"] == {
|
||||||
"id": 0,
|
"id": 0,
|
||||||
"name": "string",
|
"name": "string",
|
||||||
"items": [{"id": 0, "name": "string"}],
|
"items": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "string",
|
||||||
|
"pydantic_int": 0,
|
||||||
|
"test_P": [
|
||||||
|
{"a": 0, "b": {"c": "string", "d": "string", "e": "string"}}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user