some cleanup and optimization

This commit is contained in:
collerek
2020-12-03 09:15:19 +01:00
parent 4e10ff03e2
commit b838fa1edf
7 changed files with 106 additions and 72 deletions

View File

@ -18,11 +18,15 @@ def is_field_nullable(
pydantic_only: Optional[bool], pydantic_only: Optional[bool],
) -> bool: ) -> bool:
if nullable is None: if nullable is None:
return default is not None or server_default is not None or pydantic_only return (
default is not None
or server_default is not None
or (pydantic_only is not None and pydantic_only)
)
return nullable return nullable
def is_auto_primary_key(primary_key: bool, autoincrement: bool): def is_auto_primary_key(primary_key: bool, autoincrement: bool) -> bool:
return primary_key and autoincrement return primary_key and autoincrement

View File

@ -1,7 +1,6 @@
import inspect
import logging import logging
import warnings import warnings
from typing import Any, Dict, List, Optional, Set, TYPE_CHECKING, Tuple, Type, Union from typing import Any, Dict, List, Optional, TYPE_CHECKING, Tuple, Type, Union
import databases import databases
import pydantic import pydantic
@ -12,11 +11,12 @@ from pydantic.utils import lenient_issubclass
from sqlalchemy.sql.schema import ColumnCollectionConstraint from sqlalchemy.sql.schema import ColumnCollectionConstraint
import ormar # noqa I100 import ormar # noqa I100
from ormar import ForeignKey, ModelDefinitionError, Integer # noqa I100 from ormar import ForeignKey, Integer, ModelDefinitionError # noqa I100
from ormar.fields import BaseField from ormar.fields import BaseField
from ormar.fields.foreign_key import ForeignKeyField from ormar.fields.foreign_key import ForeignKeyField
from ormar.fields.many_to_many import ManyToMany, ManyToManyField from ormar.fields.many_to_many import ManyToMany, ManyToManyField
from ormar.fields.model_fields import ModelFieldFactory from ormar.fields.model_fields import ModelFieldFactory
from ormar.models.quick_access_views import quick_access_set
from ormar.queryset import QuerySet from ormar.queryset import QuerySet
from ormar.relations.alias_manager import AliasManager from ormar.relations.alias_manager import AliasManager
@ -128,7 +128,7 @@ def create_pydantic_field(
def get_pydantic_field(field_name: str, model: Type["Model"]) -> "ModelField": def get_pydantic_field(field_name: str, model: Type["Model"]) -> "ModelField":
return ModelField( return ModelField(
name=field_name, name=field_name,
type_=model.Meta.model_fields[field_name].__type__, type_=model.Meta.model_fields[field_name].__type__, # type: ignore
model_config=model.__config__, model_config=model.__config__,
required=not model.Meta.model_fields[field_name].nullable, required=not model.Meta.model_fields[field_name].nullable,
class_validators={}, class_validators={},
@ -316,10 +316,12 @@ def populate_choices_validators(model: Type["Model"]) -> None: # noqa CCR001
validators = getattr(model, "__pre_root_validators__", []) validators = getattr(model, "__pre_root_validators__", [])
if choices_validator not in validators: if choices_validator not in validators:
validators.append(choices_validator) validators.append(choices_validator)
setattr(model, "__pre_root_validators__", validators) model.__pre_root_validators__ = validators
def populate_default_options_values(new_model: Type["Model"], model_fields: Dict): def populate_default_options_values(
new_model: Type["Model"], model_fields: Dict
) -> None:
if not hasattr(new_model.Meta, "constraints"): if not hasattr(new_model.Meta, "constraints"):
new_model.Meta.constraints = [] new_model.Meta.constraints = []
if not hasattr(new_model.Meta, "model_fields"): if not hasattr(new_model.Meta, "model_fields"):
@ -331,59 +333,28 @@ def populate_default_options_values(new_model: Type["Model"], model_fields: Dict
new_model.Meta.include_props_in_fields = False new_model.Meta.include_props_in_fields = False
def add_cached_properties(new_model): def add_cached_properties(new_model: Type["Model"]) -> None:
new_model._props = { new_model._props = {
prop prop
for prop in vars(new_model) for prop in vars(new_model)
if isinstance(getattr(new_model, prop), property) if isinstance(getattr(new_model, prop), property)
and prop not in ("__values__", "__fields__", "fields", "pk_column", "saved") and prop not in ("__values__", "__fields__", "fields", "pk_column", "saved")
} }
new_model._quick_access_fields = { new_model._quick_access_fields = quick_access_set
"_orm_id",
"_orm_saved",
"_orm",
"_convert_json",
"__fields__",
"_related_names",
"_props",
"__class__",
"__dict__",
"__config__",
"_iter",
"_get_value",
"_is_conversion_to_json_needed",
"__fields_set__",
"_skip_ellipsis",
"_calculate_keys",
"dict",
"_update_excluded_with_related_not_required",
"_extract_nested_models",
"_get_related_not_excluded_fields",
"get_properties",
"resolve_relation_name",
"resolve_relation_field",
"set_save_status",
"__pre_root_validators__",
"__post_root_validators__",
"_extract_nested_models_from_list",
"get_name",
"extract_related_names",
"Meta",
}
new_model._related_names = None new_model._related_names = None
new_model._pydantic_fields = {name for name in new_model.__fields__} new_model._pydantic_fields = {name for name in new_model.__fields__}
def add_property_fields(new_model): def add_property_fields(new_model: Type["Model"]) -> None:
if new_model.Meta.include_props_in_fields: if new_model.Meta.include_props_in_fields:
for prop in new_model._props: for prop in new_model._props:
field_type = getattr(new_model, prop).fget.__annotations__.get("return") field_type = getattr(new_model, prop).fget.__annotations__.get("return")
new_model.Meta.model_fields[prop] = ModelFieldFactory( new_model.Meta.model_fields[prop] = ModelFieldFactory( # type: ignore
nullable=True, pydantic_only=True nullable=True, pydantic_only=True
) )
new_model.__fields__[prop] = ModelField( new_model.__fields__[prop] = ModelField(
name=prop, name=prop,
type_=Optional[field_type] if field_type else Any, type_=Optional[field_type] if field_type is not None else Any, # type: ignore
model_config=new_model.__config__, model_config=new_model.__config__,
required=False, required=False,
class_validators={}, class_validators={},

View File

@ -20,10 +20,6 @@ from typing import (
from ormar.exceptions import ModelPersistenceError, RelationshipInstanceError from ormar.exceptions import ModelPersistenceError, RelationshipInstanceError
from ormar.queryset.utils import translate_list_to_dict, update from ormar.queryset.utils import translate_list_to_dict, update
try:
import orjson as json
except ImportError: # pragma: nocover
import json # type: ignore
import ormar # noqa: I100 import ormar # noqa: I100
from ormar.fields import BaseField from ormar.fields import BaseField
@ -45,7 +41,7 @@ Field = TypeVar("Field", bound=BaseField)
class ModelTableProxy: class ModelTableProxy:
if TYPE_CHECKING: # pragma no cover if TYPE_CHECKING: # pragma no cover
Meta: ModelMeta Meta: ModelMeta
_related_names: Set _related_names: Optional[Set]
_related_names_hash: Union[str, bytes] _related_names_hash: Union[str, bytes]
pk: Any pk: Any
get_name: Callable get_name: Callable

View File

@ -1,6 +1,5 @@
import json import json
import uuid import uuid
from collections import Counter
from typing import ( from typing import (
AbstractSet, AbstractSet,
Any, Any,
@ -66,9 +65,11 @@ class NewBaseModel(
_orm_relationship_manager: AliasManager _orm_relationship_manager: AliasManager
_orm: RelationsManager _orm: RelationsManager
_orm_saved: bool _orm_saved: bool
_related_names: Set _related_names: Optional[Set]
_related_names_hash: str _related_names_hash: str
_props: Set _props: Set
_pydantic_fields: Set
_quick_access_fields: Set
Meta: ModelMeta Meta: ModelMeta
# noinspection PyMissingConstructor # noinspection PyMissingConstructor
@ -170,7 +171,7 @@ class NewBaseModel(
value = object.__getattribute__(self, "__dict__").get(item, None) value = object.__getattribute__(self, "__dict__").get(item, None)
value = object.__getattribute__(self, "_convert_json")(item, value, "loads") value = object.__getattribute__(self, "_convert_json")(item, value, "loads")
return value return value
return object.__getattribute__(self, item) return object.__getattribute__(self, item) # pragma: no cover
def _extract_related_model_instead_of_field( def _extract_related_model_instead_of_field(
self, item: str self, item: str
@ -223,13 +224,13 @@ class NewBaseModel(
@classmethod @classmethod
def get_properties( def get_properties(
cls, include: Union[Set, Dict, None], exclude: Union[Set, Dict, None] cls, include: Union[Set, Dict, None], exclude: Union[Set, Dict, None]
) -> List[str]: ) -> Set[str]:
props = cls._props props = cls._props
if include: if include:
props = [prop for prop in props if prop in include] props = {prop for prop in props if prop in include}
if exclude: if exclude:
props = [prop for prop in props if prop not in exclude] props = {prop for prop in props if prop not in exclude}
return props return props
def _get_related_not_excluded_fields( def _get_related_not_excluded_fields(

View File

@ -0,0 +1,62 @@
quick_access_set = {
"Config",
"Meta",
"__class__",
"__config__",
"__custom_root_type__",
"__dict__",
"__fields__",
"__fields_set__",
"__json_encoder__",
"__post_root_validators__",
"__pre_root_validators__",
"__same__",
"_calculate_keys",
"_convert_json",
"_extract_db_related_names",
"_extract_model_db_fields",
"_extract_nested_models",
"_extract_nested_models_from_list",
"_extract_own_model_fields",
"_get_related_not_excluded_fields",
"_get_value",
"_is_conversion_to_json_needed",
"_iter",
"_orm",
"_orm_id",
"_orm_saved",
"_props",
"_related_names",
"_skip_ellipsis",
"_update_and_follow",
"_update_excluded_with_related_not_required",
"copy",
"delete",
"dict",
"extract_related_names",
"from_dict",
"get_column_alias",
"get_column_name_from_alias",
"get_filtered_names_to_extract",
"get_name",
"get_properties",
"get_related_field_name",
"get_relation_model_id",
"json",
"keys",
"load",
"pk_column",
"pk_type",
"populate_default_values",
"remove",
"resolve_relation_field",
"resolve_relation_name",
"save",
"save_related",
"saved",
"set_save_status",
"translate_aliases_to_columns",
"translate_columns_to_aliases",
"update",
"upsert",
}

View File

@ -76,7 +76,7 @@ class RandomModel(ormar.Model):
@property @property
def full_name(self): def full_name(self):
return ' '.join([self.first_name, self.last_name]) return " ".join([self.first_name, self.last_name])
class User(ormar.Model): class User(ormar.Model):
@ -199,7 +199,7 @@ def test_all_endpoints():
"first_name", "first_name",
"last_name", "last_name",
"created_date", "created_date",
"full_name" "full_name",
] ]
assert response.json().get("full_name") == "John Test" assert response.json().get("full_name") == "John Test"
@ -212,7 +212,7 @@ def test_all_endpoints():
"first_name", "first_name",
"last_name", "last_name",
"created_date", "created_date",
"full_name" "full_name",
] ]
RandomModel.Meta.include_props_in_dict = True RandomModel.Meta.include_props_in_dict = True
@ -224,5 +224,5 @@ def test_all_endpoints():
"first_name", "first_name",
"last_name", "last_name",
"created_date", "created_date",
"full_name" "full_name",
] ]

View File

@ -26,9 +26,9 @@ class Album(ormar.Model):
@property @property
def name10(self) -> str: def name10(self) -> str:
return self.name + '_10' return self.name + "_10"
@validator('name') @validator("name")
def test(cls, v): def test(cls, v):
return v return v
@ -46,30 +46,30 @@ def create_test_database():
async def test_pydantic_only_fields(): async def test_pydantic_only_fields():
async with database: async with database:
async with database.transaction(force_rollback=True): async with database.transaction(force_rollback=True):
album = await Album.objects.create(name='Hitchcock') album = await Album.objects.create(name="Hitchcock")
assert album.pk is not None assert album.pk is not None
assert album.saved assert album.saved
assert album.timestamp is None assert album.timestamp is None
album = await Album.objects.exclude_fields('timestamp').get() album = await Album.objects.exclude_fields("timestamp").get()
assert album.timestamp is None assert album.timestamp is None
album = await Album.objects.fields({'name', 'timestamp'}).get() album = await Album.objects.fields({"name", "timestamp"}).get()
assert album.timestamp is None assert album.timestamp is None
test_dict = album.dict() test_dict = album.dict()
assert 'timestamp' in test_dict assert "timestamp" in test_dict
assert test_dict['timestamp'] is None assert test_dict["timestamp"] is None
album.timestamp = datetime.datetime.now() album.timestamp = datetime.datetime.now()
test_dict = album.dict() test_dict = album.dict()
assert 'timestamp' in test_dict assert "timestamp" in test_dict
assert test_dict['timestamp'] is not None assert test_dict["timestamp"] is not None
assert test_dict.get('name10') == 'Hitchcock_10' assert test_dict.get("name10") == "Hitchcock_10"
Album.Meta.include_props_in_dict = False Album.Meta.include_props_in_dict = False
test_dict = album.dict() test_dict = album.dict()
assert 'timestamp' in test_dict assert "timestamp" in test_dict
assert test_dict['timestamp'] is not None assert test_dict["timestamp"] is not None
# key is still there as now it's a field # key is still there as now it's a field
assert test_dict['name10'] is None assert test_dict["name10"] is None