WIP skip relation registration on m2m through instance, simplify registering relations part 2

This commit is contained in:
collerek
2021-01-10 12:06:49 +01:00
parent 055c99ba02
commit 4071ff7d11
13 changed files with 114 additions and 120 deletions

View File

@ -79,11 +79,7 @@ class AliasManager:
return text(f"{name} {alias}_{name}")
def add_relation_type(
self,
source_model: Type["Model"],
relation_name: str,
reverse_name: str = None,
is_multi: bool = False,
self, source_model: Type["Model"], relation_name: str, reverse_name: str = None,
) -> None:
"""
Registers the relations defined in ormar models.
@ -104,21 +100,16 @@ class AliasManager:
:type relation_name: str
:param reverse_name: name of related_name fo given relation for m2m relations
:type reverse_name: Optional[str]
:param is_multi: flag if relation being registered is a through m2m model
:type is_multi: bool
:return: none
:rtype: None
"""
parent_key = f"{source_model.get_name()}_{relation_name}"
if parent_key not in self._aliases_new:
self._aliases_new[parent_key] = get_table_alias()
to_field = source_model.Meta.model_fields[relation_name]
child_model = to_field.to
related_name = to_field.related_name
if not related_name:
related_name = reverse_name if is_multi else source_model.get_name() + "s"
child_key = f"{child_model.get_name()}_{related_name}"
child_key = f"{child_model.get_name()}_{reverse_name}"
if child_key not in self._aliases_new:
self._aliases_new[child_key] = get_table_alias()

View File

@ -12,6 +12,7 @@ from typing import (
)
import ormar
from ormar.exceptions import ModelPersistenceError
if TYPE_CHECKING: # pragma no cover
from ormar.relations import Relation
@ -106,11 +107,19 @@ class QuerysetProxy(ormar.QuerySetProtocol):
:param child: child model instance
:type child: Model
"""
queryset = ormar.QuerySet(model_cls=self.relation.through)
model_cls = self.relation.through
owner_column = self._owner.get_name()
child_column = child.get_name()
kwargs = {owner_column: self._owner, child_column: child}
await queryset.create(**kwargs)
kwargs = {owner_column: self._owner.pk, child_column: child.pk}
if child.pk is None:
raise ModelPersistenceError(
f"You cannot save {child.get_name()} "
f"model without primary key set! \n"
f"Save the child model first."
)
expr = model_cls.Meta.table.insert()
expr = expr.values(**kwargs)
await model_cls.Meta.database.execute(expr)
async def delete_through_instance(self, child: "T") -> None:
"""

View File

@ -63,7 +63,7 @@ class Relation:
self._type: RelationType = type_
self._to_remove: Set = set()
self.to: Type["T"] = to
self.through: Optional[Type["T"]] = through
self._through: Optional[Type["T"]] = through
self.field_name = field_name
self.related_models: Optional[Union[RelationProxy, "T"]] = (
RelationProxy(relation=self, type_=type_, field_name=field_name)
@ -71,6 +71,12 @@ class Relation:
else None
)
@property
def through(self) -> Type["T"]:
if not self._through: # pragma: no cover
raise RelationshipInstanceError("Relation does not have through model!")
return self._through
def _clean_related(self) -> None:
"""
Removes dead weakrefs from RelationProxy.

View File

@ -101,13 +101,7 @@ class RelationsManager:
return None
@staticmethod
def add(
parent: "Model",
child: "Model",
child_name: str,
virtual: bool,
relation_name: str,
) -> None:
def add(parent: "Model", child: "Model", field: Type["ForeignKeyField"],) -> None:
"""
Adds relation on both sides -> meaning on both child and parent models.
One side of the relation is always weakref proxy to avoid circular refs.
@ -120,25 +114,19 @@ class RelationsManager:
:type parent: Model
:param child: child model to register
:type child: Model
:param child_name: potential child name used if related name is not set
:type child_name: str
:param virtual:
:type virtual: bool
:param relation_name: name of the relation
:type relation_name: str
:param field: field with relation definition
:type field: ForeignKeyField
"""
to_field: Type[BaseField] = child.Meta.model_fields[relation_name]
# print('comming', child_name, relation_name)
(parent, child, child_name, to_name,) = get_relations_sides_and_names(
to_field, parent, child, child_name, virtual, relation_name
field, parent, child
)
# print('adding', parent.get_name(), child.get_name(), child_name)
# print('adding parent', parent.get_name(), child.get_name(), child_name)
parent_relation = parent._orm._get(child_name)
if parent_relation:
parent_relation.add(child) # type: ignore
# print('adding', child.get_name(), parent.get_name(), child_name)
# print('adding child', child.get_name(), parent.get_name(), to_name)
child_relation = child._orm._get(to_name)
if child_relation:
child_relation.add(parent)

View File

@ -1,48 +1,33 @@
from typing import TYPE_CHECKING, Tuple, Type
from weakref import proxy
from ormar.fields import BaseField
from ormar.fields.many_to_many import ManyToManyField
from ormar.fields.foreign_key import ForeignKeyField
if TYPE_CHECKING: # pragma no cover
from ormar import Model
def get_relations_sides_and_names(
to_field: Type[BaseField],
parent: "Model",
child: "Model",
child_name: str,
virtual: bool,
relation_name: str,
to_field: Type[ForeignKeyField], parent: "Model", child: "Model",
) -> Tuple["Model", "Model", str, str]:
"""
Determines the names of child and parent relations names, as well as
changes one of the sides of the relation into weakref.proxy to model.
:param to_field: field with relation definition
:type to_field: BaseField
:type to_field: ForeignKeyField
:param parent: parent model
:type parent: Model
:param child: child model
:type child: Model
:param child_name: name of the child
:type child_name: str
:param virtual: flag if relation is virtual
:type virtual: bool
:param relation_name:
:type relation_name:
:return: parent, child, child_name, to_name
:rtype: Tuple["Model", "Model", str, str]
"""
to_name = to_field.name
if issubclass(to_field, ManyToManyField):
child_name = to_field.related_name or child.get_name() + "s"
child = proxy(child)
elif virtual:
child_name, to_name = to_name, child_name or child.get_name()
child_name = to_field.get_related_name()
if to_field.virtual:
child_name, to_name = to_name, child_name
child, parent = parent, proxy(child)
else:
child_name = child_name or child.get_name() + "s"
child = proxy(child)
return parent, child, child_name, to_name