From 0bc971e464b57898087c4ef807bd86d43af7d75b Mon Sep 17 00:00:00 2001 From: Derek Brandao Date: Tue, 12 Mar 2019 17:51:47 +0000 Subject: [PATCH] Remove circular dependency between conditions and relationships I've changed the return type of the constraints property so that relationships no longer depend on conditions, breaking the circular dependency. --- spanner_orm/condition.py | 11 ++++++++++- spanner_orm/relationship.py | 32 ++++++++++++++++++++------------ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/spanner_orm/condition.py b/spanner_orm/condition.py index 98b7aa2..b410420 100644 --- a/spanner_orm/condition.py +++ b/spanner_orm/condition.py @@ -178,10 +178,19 @@ def bind(self, model): @property def conditions(self): + """Generate the child conditions based on the relationship constraints.""" if not self.relation: raise error.SpannerError( 'Condition must be bound before conditions is called') - return self.relation.conditions + self._conditions + relation_conditions = [] + for constraint in self.relation.constraints: + # This is backward from what you might imagine because the condition will + # be processed from the context of the destination model + relation_conditions.append( + ColumnsEqualCondition(constraint.destination_column, + constraint.origin_class, + constraint.origin_column)) + return relation_conditions + self._conditions @property def destination(self): diff --git a/spanner_orm/relationship.py b/spanner_orm/relationship.py index f44d413..38a6248 100644 --- a/spanner_orm/relationship.py +++ b/spanner_orm/relationship.py @@ -16,13 +16,21 @@ from __future__ import annotations -from typing import Dict, List, Type, Union +from typing import Any, Dict, List, Type, Union -from spanner_orm import condition +import dataclasses from spanner_orm import error from spanner_orm import model +@dataclasses.dataclass +class RelationshipConstraint: + destination_class: Type[Any] + destination_column: str + origin_class: Type[Any] + origin_column: str + + class Relationship(object): """Helps define a foreign key relationship between two models.""" @@ -52,8 +60,8 @@ def __init__(self, self.origin = None @property - def conditions(self) -> List[condition.Condition]: - assert self.origin, 'Origin must be set before conditions is called' + def constraints(self) -> List[RelationshipConstraint]: + assert self.origin, 'Origin must be set before constraints is called' return self._parse_constraints() @property @@ -66,9 +74,9 @@ def destination(self) -> Type[model.Model]: def single(self) -> bool: return self._single - def _parse_constraints(self) -> List[condition.Condition]: + def _parse_constraints(self) -> List[RelationshipConstraint]: """Validates the dictionary of constraints and turns it into Conditions.""" - conditions = [] + constraints = [] for origin_column, destination_column in self._constraints.items(): if origin_column not in self.origin.schema: raise error.SpannerError( @@ -77,10 +85,10 @@ def _parse_constraints(self) -> List[condition.Condition]: if destination_column not in self.destination.schema: raise error.SpannerError( 'Destination column must be present in destination model') - # This is backward from what you might imagine because the condition will - # be processed from the context of the destination model - conditions.append( - condition.ColumnsEqualCondition(destination_column, self.origin, - origin_column)) - return conditions + # TODO(dbrandao): remove when pytype #234 is fixed + constraints.append( + RelationshipConstraint(self.destination, destination_column, + self.origin, origin_column)) # type: ignore + + return constraints