Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- add orm package - implement rudimental nested query rendering - simple tests
- Loading branch information
Showing
10 changed files
with
178 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
from . import strategy | ||
from sqlalchemy.orm import strategies as _sqla_strat | ||
|
||
def nestedload(*keys, **kw): | ||
return _sqla_strat.EagerLazyOption(keys, lazy='akiban_nested') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
from sqlalchemy.orm.strategies import AbstractRelationshipLoader | ||
from sqlalchemy.orm.strategies import LazyLoader | ||
from sqlalchemy.orm.strategies import _factory | ||
from sqlalchemy.orm import interfaces | ||
from sqlalchemy.orm import attributes | ||
from sqlalchemy.orm import loading | ||
from sqlalchemy.orm import util as orm_util | ||
from sqlalchemy.sql import util as sql_util | ||
from sqlalchemy import util | ||
from sqlalchemy import log | ||
from sqlalchemy import select | ||
from sqlalchemy import exc as sa_exc | ||
|
||
class NestedLoader(AbstractRelationshipLoader): | ||
def __init__(self, parent): | ||
super(NestedLoader, self).__init__(parent) | ||
self.join_depth = self.parent_property.join_depth | ||
|
||
def init_class_attribute(self, mapper): | ||
self.parent_property.\ | ||
_get_strategy(LazyLoader).init_class_attribute(mapper) | ||
|
||
def setup_query(self, context, entity, path, adapter, \ | ||
column_collection=None, | ||
parentmapper=None, | ||
**kwargs): | ||
|
||
if not context.query._enable_eagerloads: | ||
return | ||
|
||
path = path[self.key] | ||
|
||
with_polymorphic = None | ||
|
||
# if not via query option, check for | ||
# a cycle | ||
if not path.contains(context, "loaderstrategy"): | ||
if self.join_depth: | ||
if path.length / 2 > self.join_depth: | ||
return | ||
elif path.contains_mapper(self.mapper): | ||
return | ||
|
||
#if parentmapper is None: | ||
# localparent = entity.mapper | ||
#else: | ||
# localparent = parentmapper | ||
|
||
source_selectable = entity.selectable | ||
|
||
with_poly_info = path.get( | ||
context, | ||
"path_with_polymorphic", | ||
None | ||
) | ||
if with_poly_info is not None: | ||
with_polymorphic = with_poly_info.with_polymorphic_mappers | ||
else: | ||
with_polymorphic = None | ||
|
||
pj, sj, source, dest, secondary, target_adapter = \ | ||
self.parent_property._create_joins(dest_polymorphic=True, | ||
source_selectable=source_selectable) | ||
|
||
add_to_collection = [] | ||
|
||
path = path[self.mapper] | ||
for value in self.mapper._iterate_polymorphic_properties( | ||
mappers=with_polymorphic): | ||
value.setup( | ||
context, | ||
entity, | ||
path, | ||
None, | ||
parentmapper=self.mapper, | ||
column_collection=add_to_collection) | ||
|
||
context.secondary_columns.append( | ||
select(add_to_collection).where(pj).as_scalar() | ||
) | ||
|
||
log.class_logger(NestedLoader) | ||
|
||
_factory["akiban_nested"] = NestedLoader |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
from sqlalchemy import Table, Column, Integer, String, Numeric, ForeignKey | ||
|
||
def cust_order_item(metadata): | ||
Table('customer', | ||
metadata, | ||
Column('id', Integer, primary_key=True), | ||
Column('name', String(20)), | ||
) | ||
|
||
Table('order', | ||
metadata, | ||
Column('id', Integer, primary_key=True), | ||
Column('customer_id', Integer, ForeignKey('customer.id')), | ||
Column('order_info', String(20)), | ||
) | ||
|
||
Table('item', | ||
metadata, | ||
Column('id', Integer, primary_key=True), | ||
Column('order_id', Integer, ForeignKey('order.id')), | ||
Column('price', Numeric(10, 2)), | ||
Column('quanity', Integer) | ||
) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
from sqlalchemy.testing import fixtures | ||
from sqlalchemy.testing.assertions import eq_, is_, AssertsCompiledSQL | ||
from sqlalchemy.orm import relationship, Session, mapper | ||
from .fixtures import cust_order_item | ||
|
||
from sqlalchemy_akiban import orm | ||
|
||
class RenderTest(fixtures.MappedTest, AssertsCompiledSQL): | ||
__dialect__ = 'akiban' | ||
|
||
@classmethod | ||
def define_tables(cls, metadata): | ||
cust_order_item(metadata) | ||
|
||
@classmethod | ||
def setup_classes(cls): | ||
class Customer(cls.Comparable): | ||
pass | ||
class Order(cls.Comparable): | ||
pass | ||
class Item(cls.Comparable): | ||
pass | ||
|
||
@classmethod | ||
def setup_mappers(cls): | ||
Customer, Order, Item = cls.classes.Customer, \ | ||
cls.classes.Order, \ | ||
cls.classes.Item | ||
customer, order, item = cls.tables.customer,\ | ||
cls.tables.order,\ | ||
cls.tables.item | ||
mapper(Customer, customer, properties={ | ||
'orders': relationship(Order, backref="customer") | ||
}) | ||
mapper(Order, order, properties={ | ||
'items': relationship(Item, backref="order") | ||
}) | ||
mapper(Item, item) | ||
|
||
def test_option_creation(self): | ||
from sqlalchemy.orm.strategies import EagerLazyOption | ||
Customer = self.classes.Customer | ||
opt = orm.nestedload(Customer.orders) | ||
assert isinstance(opt, EagerLazyOption) | ||
is_(opt.key[0], Customer.orders) | ||
|
||
def test_render_basic_nested(self): | ||
Customer = self.classes.Customer | ||
s = Session() | ||
q = s.query(Customer).options(orm.nestedload(Customer.orders)) | ||
self.assert_compile( | ||
q, | ||
'SELECT customer.id AS customer_id, ' | ||
'customer.name AS customer_name, (SELECT "order".id, ' | ||
'"order".customer_id, "order".order_info FROM "order" ' | ||
'WHERE customer.id = "order".customer_id) AS anon_1 ' | ||
'FROM customer' | ||
) | ||
|