Skip to content

Commit f17f63e

Browse files
anti-socialvmagamedov
authored andcommitted
Fixed define to work with relationship hybrid property
1 parent 2a6fe40 commit f17f63e

File tree

2 files changed

+74
-4
lines changed

2 files changed

+74
-4
lines changed

sqlconstruct.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
from sqlalchemy.orm import RelationshipProperty
4646
from sqlalchemy.util import immutabledict, ImmutableContainer, OrderedSet
4747
from sqlalchemy.orm.query import Query as _SAQuery, _QueryEntity
48-
from sqlalchemy.orm.attributes import QueryableAttribute, InstrumentedAttribute
48+
from sqlalchemy.orm.attributes import QueryableAttribute
4949

5050

5151
__version__ = '0.2.1'
@@ -347,7 +347,7 @@ def __init__(self, ext_expr, int_expr, scoped_session=None):
347347

348348
@classmethod
349349
def from_relation(cls, relation):
350-
if isinstance(relation, InstrumentedAttribute):
350+
if isinstance(relation, QueryableAttribute):
351351
relation_property = relation.property
352352
else:
353353
relation_property = relation
@@ -428,7 +428,7 @@ def __init__(self, ext_expr, int_expr, scoped_session=None):
428428

429429
@classmethod
430430
def from_relation(cls, relation):
431-
if isinstance(relation, InstrumentedAttribute):
431+
if isinstance(relation, QueryableAttribute):
432432
relation_property = relation.property
433433
else:
434434
relation_property = relation
@@ -734,7 +734,7 @@ def __getattr__(self, name):
734734
'attributes in the function definition')
735735

736736
def __argvalue__(self):
737-
if (isinstance(self.__value, InstrumentedAttribute) and
737+
if (isinstance(self.__value, QueryableAttribute) and
738738
isinstance(self.__value.property, RelationshipProperty)):
739739
relation_cls = self.__value.property.mapper.class_
740740
column = getattr(relation_cls, self.__attr_name)

tests.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from sqlalchemy.orm import subqueryload, scoped_session, sessionmaker
2929
from sqlalchemy.sql.operators import ColumnOperators
3030
from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta
31+
from sqlalchemy.ext.hybrid import hybrid_property
3132

3233

3334
PY3 = sys.version_info[0] == 3
@@ -102,6 +103,7 @@ def setUp(self):
102103
name=Column(String),
103104
a_id=Column(Integer, ForeignKey('a.id')),
104105
a=relationship('A'),
106+
just_a=hybrid_property(lambda self: self.a),
105107
))
106108

107109
base_cls.metadata.create_all(engine)
@@ -402,6 +404,33 @@ def test_defined_calls(self):
402404
(1, 2, 3, 'foo', 'bar', 'baz'),
403405
)
404406

407+
def test_define_with_relationship(self):
408+
res = defined_func.defn(
409+
self.b_cls.a, self.b_cls,
410+
)
411+
self.assertTrue(isinstance(res, apply_), type(res))
412+
self.assertEqual(
413+
columns_set(res),
414+
{
415+
self.b_cls.__table__.c.id,
416+
self.b_cls.__table__.c.name,
417+
self.b_cls.__table__.c.a_id,
418+
}
419+
)
420+
421+
res = defined_func.defn(
422+
self.b_cls.just_a, self.b_cls,
423+
)
424+
self.assertTrue(isinstance(res, apply_), type(res))
425+
self.assertEqual(
426+
columns_set(res),
427+
{
428+
self.b_cls.__table__.c.id,
429+
self.b_cls.__table__.c.name,
430+
self.b_cls.__table__.c.a_id,
431+
}
432+
)
433+
405434
def test_defined_meta(self):
406435
self.assertEqual(defined_func.__doc__, "doc")
407436
self.assertEqual(defined_func.__module__, "tests")
@@ -781,6 +810,10 @@ class A(self.base_cls):
781810
name = Column(String)
782811
b_list = relationship('B')
783812

813+
@hybrid_property
814+
def just_b_list(self):
815+
return self.b_list
816+
784817
class B(self.base_cls):
785818
name = Column(String)
786819
a_id = Column(Integer, ForeignKey('a.id'))
@@ -807,6 +840,20 @@ class B(self.base_cls):
807840
{'a_name': 'a3', 'b_names': ['B7', 'B8', 'B9']}),
808841
)
809842

843+
query = (
844+
ConstructQuery({
845+
'a_name': A.name,
846+
'b_names': map_(apply_(capitalize, [B.name]), A.just_b_list),
847+
})
848+
.with_session(session.registry())
849+
)
850+
self.assertEqual(
851+
tuple(dict(obj) for obj in query.all()),
852+
({'a_name': 'a1', 'b_names': ['B1', 'B2', 'B3']},
853+
{'a_name': 'a2', 'b_names': ['B4', 'B5', 'B6']},
854+
{'a_name': 'a3', 'b_names': ['B7', 'B8', 'B9']}),
855+
)
856+
810857
def test_many_to_many(self):
811858
ab_table = Table(
812859
'a_b',
@@ -1015,6 +1062,10 @@ class A(self.base_cls):
10151062
b_id = Column(Integer, ForeignKey('b.id'))
10161063
b = relationship('B')
10171064

1065+
@hybrid_property
1066+
def just_b(self):
1067+
return self.b
1068+
10181069
class B(self.base_cls):
10191070
name = Column(String)
10201071

@@ -1040,7 +1091,26 @@ def body(a_name, b_name):
10401091
.with_session(session.registry())
10411092
.order_by(A.name)
10421093
)
1094+
self.assertEqual(
1095+
tuple(dict(obj) for obj in query.all()),
1096+
({'full_name': 'A1 B1'},
1097+
{'full_name': 'A2 B1'},
1098+
{'full_name': 'A3 B1'},
1099+
{'full_name': 'A4 B2'},
1100+
{'full_name': 'A5 B2'},
1101+
{'full_name': 'A6 B2'},
1102+
{'full_name': 'A7 B3'},
1103+
{'full_name': 'A8 B3'},
1104+
{'full_name': 'A9 B3'}),
1105+
)
10431106

1107+
query = (
1108+
ConstructQuery({
1109+
'full_name': full_name.defn(A, A.just_b),
1110+
})
1111+
.with_session(session.registry())
1112+
.order_by(A.name)
1113+
)
10441114
self.assertEqual(
10451115
tuple(dict(obj) for obj in query.all()),
10461116
({'full_name': 'A1 B1'},

0 commit comments

Comments
 (0)