From 4e10ff03e210c6e00407cd760b30f9344fbdd87e Mon Sep 17 00:00:00 2001 From: collerek Date: Wed, 2 Dec 2020 20:39:30 +0100 Subject: [PATCH] more optimizations --- ormar/models/metaclass.py | 102 +++++++++++++++++++++-------------- ormar/models/modelproxy.py | 66 +++++++++++------------ ormar/models/newbasemodel.py | 79 ++++++++++++++------------- 3 files changed, 138 insertions(+), 109 deletions(-) diff --git a/ormar/models/metaclass.py b/ormar/models/metaclass.py index 2280c11..5c569dd 100644 --- a/ormar/models/metaclass.py +++ b/ormar/models/metaclass.py @@ -47,7 +47,7 @@ def register_relation_on_build(table_name: str, field: Type[ForeignKeyField]) -> def register_many_to_many_relation_on_build( - table_name: str, field: Type[ManyToManyField] + table_name: str, field: Type[ManyToManyField] ) -> None: alias_manager.add_relation_type(field.through.Meta.tablename, table_name) alias_manager.add_relation_type( @@ -56,11 +56,11 @@ def register_many_to_many_relation_on_build( def reverse_field_not_already_registered( - child: Type["Model"], child_model_name: str, parent_model: Type["Model"] + child: Type["Model"], child_model_name: str, parent_model: Type["Model"] ) -> bool: return ( - child_model_name not in parent_model.__fields__ - and child.get_name() not in parent_model.__fields__ + child_model_name not in parent_model.__fields__ + and child.get_name() not in parent_model.__fields__ ) @@ -71,7 +71,7 @@ def expand_reverse_relationships(model: Type["Model"]) -> None: parent_model = model_field.to child = model if reverse_field_not_already_registered( - child, child_model_name, parent_model + child, child_model_name, parent_model ): register_reverse_model_fields( parent_model, child, child_model_name, model_field @@ -79,10 +79,10 @@ def expand_reverse_relationships(model: Type["Model"]) -> None: def register_reverse_model_fields( - model: Type["Model"], - child: Type["Model"], - child_model_name: str, - model_field: Type["ForeignKeyField"], + model: Type["Model"], + child: Type["Model"], + child_model_name: str, + model_field: Type["ForeignKeyField"], ) -> None: if issubclass(model_field, ManyToManyField): model.Meta.model_fields[child_model_name] = ManyToMany( @@ -97,7 +97,7 @@ def register_reverse_model_fields( def adjust_through_many_to_many_model( - model: Type["Model"], child: Type["Model"], model_field: Type[ManyToManyField] + model: Type["Model"], child: Type["Model"], model_field: Type[ManyToManyField] ) -> None: model_field.through.Meta.model_fields[model.get_name()] = ForeignKey( model, real_name=model.get_name(), ondelete="CASCADE" @@ -114,7 +114,7 @@ def adjust_through_many_to_many_model( def create_pydantic_field( - field_name: str, model: Type["Model"], model_field: Type[ManyToManyField] + field_name: str, model: Type["Model"], model_field: Type[ManyToManyField] ) -> None: model_field.through.__fields__[field_name] = ModelField( name=field_name, @@ -136,7 +136,7 @@ def get_pydantic_field(field_name: str, model: Type["Model"]) -> "ModelField": def create_and_append_m2m_fk( - model: Type["Model"], model_field: Type[ManyToManyField] + model: Type["Model"], model_field: Type[ManyToManyField] ) -> None: column = sqlalchemy.Column( model.get_name(), @@ -152,7 +152,7 @@ def create_and_append_m2m_fk( def check_pk_column_validity( - field_name: str, field: BaseField, pkname: Optional[str] + field_name: str, field: BaseField, pkname: Optional[str] ) -> Optional[str]: if pkname is not None: raise ModelDefinitionError("Only one primary key column is allowed.") @@ -162,7 +162,7 @@ def check_pk_column_validity( def sqlalchemy_columns_from_model_fields( - model_fields: Dict, table_name: str + model_fields: Dict, table_name: str ) -> Tuple[Optional[str], List[sqlalchemy.Column]]: columns = [] pkname = None @@ -176,9 +176,9 @@ def sqlalchemy_columns_from_model_fields( if field.primary_key: pkname = check_pk_column_validity(field_name, field, pkname) if ( - not field.pydantic_only - and not field.virtual - and not issubclass(field, ManyToManyField) + not field.pydantic_only + and not field.virtual + and not issubclass(field, ManyToManyField) ): columns.append(field.get_column(field.get_alias())) register_relation_in_alias_manager(table_name, field) @@ -186,7 +186,7 @@ def sqlalchemy_columns_from_model_fields( def register_relation_in_alias_manager( - table_name: str, field: Type[ForeignKeyField] + table_name: str, field: Type[ForeignKeyField] ) -> None: if issubclass(field, ManyToManyField): register_many_to_many_relation_on_build(table_name, field) @@ -195,7 +195,7 @@ def register_relation_in_alias_manager( def populate_default_pydantic_field_value( - ormar_field: Type[BaseField], field_name: str, attrs: dict + ormar_field: Type[BaseField], field_name: str, attrs: dict ) -> dict: curr_def_value = attrs.get(field_name, ormar.Undefined) if lenient_issubclass(curr_def_value, ormar.fields.BaseField): @@ -242,7 +242,7 @@ def extract_annotations_and_default_vals(attrs: dict) -> Tuple[Dict, Dict]: def populate_meta_tablename_columns_and_pk( - name: str, new_model: Type["Model"] + name: str, new_model: Type["Model"] ) -> Type["Model"]: tablename = name.lower() + "s" new_model.Meta.tablename = ( @@ -268,7 +268,7 @@ def populate_meta_tablename_columns_and_pk( def populate_meta_sqlalchemy_table_if_required( - new_model: Type["Model"], + new_model: Type["Model"], ) -> Type["Model"]: if not hasattr(new_model.Meta, "table"): new_model.Meta.table = sqlalchemy.Table( @@ -333,30 +333,54 @@ def populate_default_options_values(new_model: Type["Model"], model_fields: Dict def add_cached_properties(new_model): new_model._props = { - prop - for prop in vars(new_model) - if isinstance(getattr(new_model, prop), property) - and prop - not in ("__values__", "__fields__", "fields", "pk_column", "saved") - } + prop + for prop in vars(new_model) + if isinstance(getattr(new_model, prop), property) + and prop not in ("__values__", "__fields__", "fields", "pk_column", "saved") + } new_model._quick_access_fields = { - "_orm_id", - "_orm_saved", - "_orm", - "__fields__", - "_related_names", - "_props", - "__class__", - "extract_related_names", - } + "_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._pydantic_fields = {name for name in new_model.__fields__} def add_property_fields(new_model): if new_model.Meta.include_props_in_fields: for prop in new_model._props: - field_type = getattr(new_model, prop).fget.__annotations__.get('return') - new_model.Meta.model_fields[prop] = ModelFieldFactory(nullable=True, pydantic_only=True) + field_type = getattr(new_model, prop).fget.__annotations__.get("return") + new_model.Meta.model_fields[prop] = ModelFieldFactory( + nullable=True, pydantic_only=True + ) new_model.__fields__[prop] = ModelField( name=prop, type_=Optional[field_type] if field_type else Any, @@ -368,7 +392,7 @@ def add_property_fields(new_model): class ModelMetaclass(pydantic.main.ModelMetaclass): def __new__( # type: ignore - mcs: "ModelMetaclass", name: str, bases: Any, attrs: dict + mcs: "ModelMetaclass", name: str, bases: Any, attrs: dict ) -> "ModelMetaclass": attrs["Config"] = get_pydantic_base_orm_config() attrs["__name__"] = name diff --git a/ormar/models/modelproxy.py b/ormar/models/modelproxy.py index 1f637cc..ca12672 100644 --- a/ormar/models/modelproxy.py +++ b/ormar/models/modelproxy.py @@ -69,7 +69,7 @@ class ModelTableProxy: @staticmethod def get_clause_target_and_filter_column_name( - parent_model: Type["Model"], target_model: Type["Model"], reverse: bool + parent_model: Type["Model"], target_model: Type["Model"], reverse: bool ) -> Tuple[Type["Model"], str]: if reverse: field = target_model.resolve_relation_field(target_model, parent_model) @@ -84,10 +84,10 @@ class ModelTableProxy: @staticmethod def get_column_name_for_id_extraction( - parent_model: Type["Model"], - target_model: Type["Model"], - reverse: bool, - use_raw: bool, + parent_model: Type["Model"], + target_model: Type["Model"], + reverse: bool, + use_raw: bool, ) -> str: if reverse: column_name = parent_model.Meta.pkname @@ -110,7 +110,7 @@ class ModelTableProxy: def get_relation_model_id(self, target_field: Type["BaseField"]) -> Optional[int]: if target_field.virtual or issubclass( - target_field, ormar.fields.ManyToManyField + target_field, ormar.fields.ManyToManyField ): return self.pk related_name = self.resolve_relation_name(self, target_field.to) @@ -127,9 +127,9 @@ class ModelTableProxy: @classmethod def get_names_to_exclude( - cls, - fields: Optional[Union[Dict, Set]] = None, - exclude_fields: Optional[Union[Dict, Set]] = None, + cls, + fields: Optional[Union[Dict, Set]] = None, + exclude_fields: Optional[Union[Dict, Set]] = None, ) -> Set: fields_names = cls.extract_db_own_fields() if fields and fields is not Ellipsis: @@ -241,9 +241,9 @@ class ModelTableProxy: @classmethod def _update_excluded_with_related_not_required( - cls, - exclude: Union["AbstractSetIntStr", "MappingIntStrAny", None], - nested: bool = False, + cls, + exclude: Union["AbstractSetIntStr", "MappingIntStrAny", None], + nested: bool = False, ) -> Union[Set, Dict]: exclude = exclude or {} related_set = cls._exclude_related_names_not_required(nested=nested) @@ -269,18 +269,18 @@ class ModelTableProxy: @staticmethod def resolve_relation_name( # noqa CCR001 - item: Union[ - "NewBaseModel", - Type["NewBaseModel"], - "ModelTableProxy", - Type["ModelTableProxy"], - ], - related: Union[ - "NewBaseModel", - Type["NewBaseModel"], - "ModelTableProxy", - Type["ModelTableProxy"], - ], + item: Union[ + "NewBaseModel", + Type["NewBaseModel"], + "ModelTableProxy", + Type["ModelTableProxy"], + ], + related: Union[ + "NewBaseModel", + Type["NewBaseModel"], + "ModelTableProxy", + Type["ModelTableProxy"], + ], ) -> str: for name, field in item.Meta.model_fields.items(): if issubclass(field, ForeignKeyField): @@ -296,7 +296,7 @@ class ModelTableProxy: @staticmethod def resolve_relation_field( - item: Union["Model", Type["Model"]], related: Union["Model", Type["Model"]] + item: Union["Model", Type["Model"]], related: Union["Model", Type["Model"]] ) -> Type[BaseField]: name = ModelTableProxy.resolve_relation_name(item, related) to_field = item.Meta.model_fields.get(name) @@ -343,12 +343,12 @@ class ModelTableProxy: for field in one.Meta.model_fields.keys(): current_field = getattr(one, field) if isinstance(current_field, list) and not isinstance( - current_field, ormar.Model + current_field, ormar.Model ): setattr(other, field, current_field + getattr(other, field)) elif ( - isinstance(current_field, ormar.Model) - and current_field.pk == getattr(other, field).pk + isinstance(current_field, ormar.Model) + and current_field.pk == getattr(other, field).pk ): setattr( other, @@ -360,7 +360,7 @@ class ModelTableProxy: @staticmethod def _populate_pk_column( - model: Type["Model"], columns: List[str], use_alias: bool = False, + model: Type["Model"], columns: List[str], use_alias: bool = False, ) -> List[str]: pk_alias = ( model.get_column_alias(model.Meta.pkname) @@ -373,10 +373,10 @@ class ModelTableProxy: @staticmethod def own_table_columns( - model: Type["Model"], - fields: Optional[Union[Set, Dict]], - exclude_fields: Optional[Union[Set, Dict]], - use_alias: bool = False, + model: Type["Model"], + fields: Optional[Union[Set, Dict]], + exclude_fields: Optional[Union[Set, Dict]], + use_alias: bool = False, ) -> List[str]: columns = [ model.get_column_name_from_alias(col.name) if not use_alias else col.name diff --git a/ormar/models/newbasemodel.py b/ormar/models/newbasemodel.py index 77879c4..64f703f 100644 --- a/ormar/models/newbasemodel.py +++ b/ormar/models/newbasemodel.py @@ -1,5 +1,6 @@ import json import uuid +from collections import Counter from typing import ( AbstractSet, Any, @@ -155,24 +156,25 @@ class NewBaseModel( self.set_save_status(False) def __getattribute__(self, item: str) -> Any: - if item in object.__getattribute__(self, '_quick_access_fields'): + if item in object.__getattribute__(self, "_quick_access_fields"): return object.__getattribute__(self, item) if item == "pk": - return self.__dict__.get(self.Meta.pkname, None) - if item in self.extract_related_names(): - return self._extract_related_model_instead_of_field(item) - if item in self._props: + return object.__getattribute__(self, "__dict__").get(self.Meta.pkname, None) + if item in object.__getattribute__(self, "extract_related_names")(): + return object.__getattribute__( + self, "_extract_related_model_instead_of_field" + )(item) + if item in object.__getattribute__(self, "_props"): return object.__getattribute__(self, item) - if item in self.__fields__: - value = self.__dict__.get(item, None) - value = self._convert_json(item, value, "loads") + if item in object.__getattribute__(self, "_pydantic_fields"): + value = object.__getattribute__(self, "__dict__").get(item, None) + value = object.__getattribute__(self, "_convert_json")(item, value, "loads") return value - return super().__getattribute__(item) + return object.__getattribute__(self, item) def _extract_related_model_instead_of_field( - self, item: str + self, item: str ) -> Optional[Union["T", Sequence["T"]]]: - # alias = self.get_column_alias(item) if item in self._orm: return self._orm.get(item) return None # pragma no cover @@ -184,9 +186,9 @@ class NewBaseModel( def __same__(self, other: "NewBaseModel") -> bool: return ( - self._orm_id == other._orm_id - or self.dict() == other.dict() - or (self.pk == other.pk and self.pk is not None) + self._orm_id == other._orm_id + or (self.pk == other.pk and self.pk is not None) + or self.dict() == other.dict() ) @classmethod @@ -220,7 +222,7 @@ class NewBaseModel( @classmethod 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]: props = cls._props @@ -231,7 +233,7 @@ class NewBaseModel( return props def _get_related_not_excluded_fields( - self, include: Optional[Dict], exclude: Optional[Dict], + self, include: Optional[Dict], exclude: Optional[Dict], ) -> List: fields = [field for field in self.extract_related_names()] if include: @@ -246,15 +248,15 @@ class NewBaseModel( @staticmethod def _extract_nested_models_from_list( - models: MutableSequence, - include: Union[Set, Dict, None], - exclude: Union[Set, Dict, None], + models: MutableSequence, + include: Union[Set, Dict, None], + exclude: Union[Set, Dict, None], ) -> List: result = [] for model in models: try: result.append( - model.dict(nested=True, include=include, exclude=exclude, ) + model.dict(nested=True, include=include, exclude=exclude,) ) except ReferenceError: # pragma no cover continue @@ -262,17 +264,17 @@ class NewBaseModel( @staticmethod def _skip_ellipsis( - items: Union[Set, Dict, None], key: str + items: Union[Set, Dict, None], key: str ) -> Union[Set, Dict, None]: result = Excludable.get_child(items, key) return result if result is not Ellipsis else None def _extract_nested_models( # noqa: CCR001 - self, - nested: bool, - dict_instance: Dict, - include: Optional[Dict], - exclude: Optional[Dict], + self, + nested: bool, + dict_instance: Dict, + include: Optional[Dict], + exclude: Optional[Dict], ) -> Dict: fields = self._get_related_not_excluded_fields(include=include, exclude=exclude) @@ -298,16 +300,16 @@ class NewBaseModel( return dict_instance def dict( # type: ignore # noqa A003 - self, - *, - include: Union[Set, Dict] = None, - exclude: Union[Set, Dict] = None, - by_alias: bool = False, - skip_defaults: bool = None, - exclude_unset: bool = False, - exclude_defaults: bool = False, - exclude_none: bool = False, - nested: bool = False, + self, + *, + include: Union[Set, Dict] = None, + exclude: Union[Set, Dict] = None, + by_alias: bool = False, + skip_defaults: bool = None, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + nested: bool = False, ) -> "DictStrAny": # noqa: A003' dict_instance = super().dict( include=include, @@ -363,4 +365,7 @@ class NewBaseModel( return value def _is_conversion_to_json_needed(self, column_name: str) -> bool: - return column_name in self.Meta.model_fields and self.Meta.model_fields[column_name].__type__ == pydantic.Json + return ( + column_name in self.Meta.model_fields + and self.Meta.model_fields[column_name].__type__ == pydantic.Json + )