some cleanup of unused relations code, introduced caching of related_names and props on model, set profiling
This commit is contained in:
@ -12,11 +12,17 @@ from typing import (
|
||||
Union,
|
||||
)
|
||||
|
||||
import ormar
|
||||
from ormar.exceptions import RelationshipInstanceError
|
||||
|
||||
try:
|
||||
import orjson as json
|
||||
except ImportError: # pragma: nocover
|
||||
import json # type: ignore
|
||||
|
||||
import ormar
|
||||
from ormar.fields import BaseField, ManyToManyField
|
||||
from ormar.fields.foreign_key import ForeignKeyField
|
||||
from ormar.models.metaclass import ModelMeta, expand_reverse_relationships
|
||||
from ormar.models.metaclass import ModelMeta
|
||||
|
||||
if TYPE_CHECKING: # pragma no cover
|
||||
from ormar import Model
|
||||
@ -30,6 +36,8 @@ Field = TypeVar("Field", bound=BaseField)
|
||||
class ModelTableProxy:
|
||||
if TYPE_CHECKING: # pragma no cover
|
||||
Meta: ModelMeta
|
||||
_related_names: Set
|
||||
_related_names_hash: Union[str, bytes]
|
||||
|
||||
def dict(self): # noqa A003
|
||||
raise NotImplementedError # pragma no cover
|
||||
@ -88,10 +96,17 @@ class ModelTableProxy:
|
||||
|
||||
@classmethod
|
||||
def extract_related_names(cls) -> Set:
|
||||
|
||||
if isinstance(cls._related_names_hash, (str, bytes)):
|
||||
return cls._related_names
|
||||
|
||||
related_names = set()
|
||||
for name, field in cls.Meta.model_fields.items():
|
||||
if inspect.isclass(field) and issubclass(field, ForeignKeyField):
|
||||
related_names.add(name)
|
||||
cls._related_names_hash = json.dumps(list(cls.Meta.model_fields.keys()))
|
||||
cls._related_names = related_names
|
||||
|
||||
return related_names
|
||||
|
||||
@classmethod
|
||||
@ -99,10 +114,10 @@ class ModelTableProxy:
|
||||
related_names = set()
|
||||
for name, field in cls.Meta.model_fields.items():
|
||||
if (
|
||||
inspect.isclass(field)
|
||||
and issubclass(field, ForeignKeyField)
|
||||
and not issubclass(field, ManyToManyField)
|
||||
and not field.virtual
|
||||
inspect.isclass(field)
|
||||
and issubclass(field, ForeignKeyField)
|
||||
and not issubclass(field, ManyToManyField)
|
||||
and not field.virtual
|
||||
):
|
||||
related_names.add(name)
|
||||
return related_names
|
||||
@ -114,9 +129,9 @@ class ModelTableProxy:
|
||||
related_names = set()
|
||||
for name, field in cls.Meta.model_fields.items():
|
||||
if (
|
||||
inspect.isclass(field)
|
||||
and issubclass(field, ForeignKeyField)
|
||||
and field.nullable
|
||||
inspect.isclass(field)
|
||||
and issubclass(field, ForeignKeyField)
|
||||
and field.nullable
|
||||
):
|
||||
related_names.add(name)
|
||||
return related_names
|
||||
@ -136,9 +151,8 @@ class ModelTableProxy:
|
||||
|
||||
@staticmethod
|
||||
def resolve_relation_name( # noqa CCR001
|
||||
item: Union["NewBaseModel", Type["NewBaseModel"]],
|
||||
related: Union["NewBaseModel", Type["NewBaseModel"]],
|
||||
register_missing: bool = True,
|
||||
item: Union["NewBaseModel", Type["NewBaseModel"]],
|
||||
related: Union["NewBaseModel", Type["NewBaseModel"]]
|
||||
) -> str:
|
||||
for name, field in item.Meta.model_fields.items():
|
||||
if issubclass(field, ForeignKeyField):
|
||||
@ -147,12 +161,6 @@ class ModelTableProxy:
|
||||
# so we need to compare Meta too as this one is copied as is
|
||||
if field.to == related.__class__ or field.to.Meta == related.Meta:
|
||||
return name
|
||||
# fallback for not registered relation
|
||||
if register_missing: # pragma nocover
|
||||
expand_reverse_relationships(related.__class__) # type: ignore
|
||||
return ModelTableProxy.resolve_relation_name(
|
||||
item, related, register_missing=False
|
||||
)
|
||||
|
||||
raise ValueError(
|
||||
f"No relation between {item.get_name()} and {related.get_name()}"
|
||||
@ -160,8 +168,8 @@ class ModelTableProxy:
|
||||
|
||||
@staticmethod
|
||||
def resolve_relation_field(
|
||||
item: Union["Model", Type["Model"]], related: Union["Model", Type["Model"]]
|
||||
) -> Union[Type[BaseField], Type[ForeignKeyField]]:
|
||||
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)
|
||||
if not to_field: # pragma no cover
|
||||
@ -207,12 +215,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,
|
||||
@ -223,7 +231,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)
|
||||
@ -236,10 +244,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
|
||||
|
||||
@ -9,7 +9,7 @@ from typing import (
|
||||
Mapping,
|
||||
Optional,
|
||||
Sequence,
|
||||
TYPE_CHECKING,
|
||||
Set, TYPE_CHECKING,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
@ -43,7 +43,7 @@ if TYPE_CHECKING: # pragma no cover
|
||||
class NewBaseModel(
|
||||
pydantic.BaseModel, ModelTableProxy, Excludable, metaclass=ModelMetaclass
|
||||
):
|
||||
__slots__ = ("_orm_id", "_orm_saved", "_orm")
|
||||
__slots__ = ("_orm_id", "_orm_saved", "_orm", "_related_names", "_related_names_hash", "_props")
|
||||
|
||||
if TYPE_CHECKING: # pragma no cover
|
||||
__model_fields__: Dict[str, Type[BaseField]]
|
||||
@ -56,6 +56,10 @@ class NewBaseModel(
|
||||
__database__: databases.Database
|
||||
_orm_relationship_manager: AliasManager
|
||||
_orm: RelationsManager
|
||||
_orm_saved: bool
|
||||
_related_names: Set
|
||||
_related_names_hash: str
|
||||
_props: List[str]
|
||||
Meta: ModelMeta
|
||||
|
||||
# noinspection PyMissingConstructor
|
||||
@ -107,7 +111,7 @@ class NewBaseModel(
|
||||
)
|
||||
|
||||
def __setattr__(self, name: str, value: Any) -> None: # noqa CCR001
|
||||
if name in ("_orm_id", "_orm_saved", "_orm"):
|
||||
if name in ("_orm_id", "_orm_saved", "_orm", "_related_names", "_props"):
|
||||
object.__setattr__(self, name, value)
|
||||
elif name == "pk":
|
||||
object.__setattr__(self, self.Meta.pkname, value)
|
||||
@ -126,12 +130,12 @@ class NewBaseModel(
|
||||
super().__setattr__(name, value)
|
||||
|
||||
def __getattribute__(self, item: str) -> Any:
|
||||
if item in ("_orm_id", "_orm_saved", "_orm", "__fields__"):
|
||||
if item in ("_orm_id", "_orm_saved", "_orm", "__fields__", "_related_names", "_props"):
|
||||
return object.__getattribute__(self, item)
|
||||
if item != "extract_related_names" and item in self.extract_related_names():
|
||||
return self._extract_related_model_instead_of_field(item)
|
||||
if item == "pk":
|
||||
return self.__dict__.get(self.Meta.pkname, None)
|
||||
if item != "extract_related_names" and item in self.extract_related_names():
|
||||
return self._extract_related_model_instead_of_field(item)
|
||||
if item != "__fields__" and item in self.__fields__:
|
||||
value = self.__dict__.get(item, None)
|
||||
value = self._convert_json(item, value, "loads")
|
||||
@ -186,12 +190,16 @@ class NewBaseModel(
|
||||
include: Union["AbstractSetIntStr", "MappingIntStrAny"] = None,
|
||||
exclude: Union["AbstractSetIntStr", "MappingIntStrAny"] = None,
|
||||
) -> List[str]:
|
||||
props = [
|
||||
prop
|
||||
for prop in dir(cls)
|
||||
if isinstance(getattr(cls, prop), property)
|
||||
and prop not in ("__values__", "__fields__", "fields", "pk_column")
|
||||
]
|
||||
if isinstance(cls._props, list):
|
||||
props = cls._props
|
||||
else:
|
||||
props = [
|
||||
prop
|
||||
for prop in dir(cls)
|
||||
if isinstance(getattr(cls, prop), property)
|
||||
and prop not in ("__values__", "__fields__", "fields", "pk_column")
|
||||
]
|
||||
cls._props = props
|
||||
if include:
|
||||
props = [prop for prop in props if prop in include]
|
||||
if exclude:
|
||||
|
||||
@ -3,7 +3,6 @@ from ormar.relations.relation import Relation, RelationType
|
||||
from ormar.relations.relation_manager import RelationsManager
|
||||
from ormar.relations.utils import (
|
||||
get_relations_sides_and_names,
|
||||
register_missing_relation,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
@ -11,6 +10,5 @@ __all__ = [
|
||||
"Relation",
|
||||
"RelationsManager",
|
||||
"RelationType",
|
||||
"register_missing_relation",
|
||||
"get_relations_sides_and_names",
|
||||
]
|
||||
|
||||
@ -7,7 +7,6 @@ from ormar.fields.many_to_many import ManyToManyField
|
||||
from ormar.relations.relation import Relation, RelationType
|
||||
from ormar.relations.utils import (
|
||||
get_relations_sides_and_names,
|
||||
register_missing_relation,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING: # pragma no cover
|
||||
@ -42,8 +41,6 @@ class RelationsManager:
|
||||
to=field.to,
|
||||
through=getattr(field, "through", None),
|
||||
)
|
||||
if field.name not in self._related_names:
|
||||
self._related_names.append(field.name)
|
||||
|
||||
def __contains__(self, item: str) -> bool:
|
||||
return item in self._related_names
|
||||
@ -69,9 +66,10 @@ class RelationsManager:
|
||||
)
|
||||
|
||||
parent_relation = parent._orm._get(child_name)
|
||||
if not parent_relation:
|
||||
parent_relation = register_missing_relation(parent, child, child_name)
|
||||
parent_relation.add(child) # type: ignore
|
||||
if parent_relation:
|
||||
# print('missing', child_name)
|
||||
# parent_relation = register_missing_relation(parent, child, child_name)
|
||||
parent_relation.add(child) # type: ignore
|
||||
|
||||
child_relation = child._orm._get(to_name)
|
||||
if child_relation:
|
||||
|
||||
@ -72,6 +72,4 @@ class RelationProxy(list):
|
||||
if self.relation._type == ormar.RelationType.MULTIPLE:
|
||||
await self.queryset_proxy.create_through_instance(item)
|
||||
rel_name = item.resolve_relation_name(item, self._owner)
|
||||
if rel_name not in item._orm: # pragma nocover
|
||||
item._orm._add_relation(item.Meta.model_fields[rel_name])
|
||||
setattr(item, rel_name, self._owner)
|
||||
|
||||
@ -1,32 +1,19 @@
|
||||
from typing import Optional, TYPE_CHECKING, Tuple, Type
|
||||
from typing import TYPE_CHECKING, Tuple, Type
|
||||
from weakref import proxy
|
||||
|
||||
import ormar
|
||||
from ormar.fields import BaseField
|
||||
from ormar.fields.many_to_many import ManyToManyField
|
||||
from ormar.relations import Relation
|
||||
|
||||
if TYPE_CHECKING: # pragma no cover
|
||||
from ormar import Model
|
||||
|
||||
|
||||
def register_missing_relation(
|
||||
parent: "Model", child: "Model", child_name: str
|
||||
) -> Optional[Relation]:
|
||||
ormar.models.expand_reverse_relationships(child.__class__)
|
||||
name = parent.resolve_relation_name(parent, child)
|
||||
field = parent.Meta.model_fields[name]
|
||||
parent._orm._add_relation(field)
|
||||
parent_relation = parent._orm._get(child_name)
|
||||
return parent_relation
|
||||
|
||||
|
||||
def get_relations_sides_and_names(
|
||||
to_field: Type[BaseField],
|
||||
parent: "Model",
|
||||
child: "Model",
|
||||
child_name: str,
|
||||
virtual: bool,
|
||||
to_field: Type[BaseField],
|
||||
parent: "Model",
|
||||
child: "Model",
|
||||
child_name: str,
|
||||
virtual: bool,
|
||||
) -> Tuple["Model", "Model", str, str]:
|
||||
to_name = to_field.name
|
||||
if issubclass(to_field, ManyToManyField):
|
||||
|
||||
Reference in New Issue
Block a user