add also basic examples for native pydantic fields including models to not skip them

This commit is contained in:
collerek
2021-04-30 18:36:25 +02:00
parent 12c002776b
commit 18706d884c
4 changed files with 106 additions and 20 deletions

View File

@ -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.

View File

@ -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,

View File

@ -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()

View File

@ -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"}}
],
}
],
} }