Thank you for the reply. I like the idea of having the duplicated rows 
first (for simplicity) before it gets deduplicated.
Before that, I was returning None-s from the query, and then I was fetching 
the result from the bundle itself (from its internal collection being 
collected). And it was so ugly.
Yes, basically I care about performance. I haven't found any benchmarks of 
SQLAlchemy fetching in different ways (only inserts/bulks etc.), but from 
what I got from my benchmarks, manual mapping comparing to entities 
relationships load is ~1,5-2 times faster for two middle-sized 1:N entities.
Another point is that we're trying to stick with the CQRS principles, so we 
fetch entities only when we want business rules to be applied and checked 
when modifying the database. When it's readonly, we don't have any complex 
business logic (setters, services and such), we just have to check some 
permissions (even based on objects ids), fetch the data quickly, serialize 
it and return. So basically we don't want to maintain the unit of work when 
fetching data.

среда, 26 декабря 2018 г., 15:39:29 UTC+7 пользователь Mike Bayer написал:
>
> On Tue, Dec 25, 2018 at 10:18 PM Pavel Pristupa <pris...@gmail.com 
> <javascript:>> wrote: 
> > 
> > Hello everybody! 
> > 
> > What I want sometimes is to query some columns but to map the resulting 
> rows into custom data classes rather than tuples, like with 
> values(*columns), or SQLA entities. 
> > I found that Bundles (
> https://docs.sqlalchemy.org/en/latest/orm/loading_columns.html#bundles) 
> could potentially help, but what I see there is that I can process rows 
> with create_row_processor and give the immediate result of one-to-one 
> converting every single row. 
> > Let's say I have User and Address with 1:N relation: 
> > 
> > 
> > class User(Base): 
> >     __tablename__ = 'users' 
> > 
> >     id = Column(Integer, primary_key=True) 
> >     name = Column(String) 
> > 
> >     addresses = relationship('Address') 
> > 
> > 
> > class Address(Base): 
> >     __tablename__ = 'addresses' 
> > 
> >     id = Column(Integer, primary_key=True) 
> >     user_id = Column(ForeignKey(User.id)) 
> >     city = Column(String) 
> > 
> > 
> > 
> > I'd like to query all users into a custom structure like this: 
> > 
> > 
> > [{ 
> >   'id': 1, 
> >   'name': 'User 1', 
> >   'addresses': [{ 
> >     'id': 1, 
> >     'city': 'City 1', 
> >   }, { 
> >     'id': 2, 
> >     'city': 'City 2', 
> >   }], 
> > }, { 
> >   'id': 2, 
> >   'name': 'User 2', 
> >   'addresses': [{ 
> >     'id': 3, 
> >     'city': 'City 3', 
> >   }], 
> > }] 
> > 
> > 
> > 
> > It's similar to what ORM does for me when using joinedload for 
> relationships, but how to nest related items without ORM identity-mapped 
> classes? 
> > Your help would be much appreciated! 
>
> by far the simplest way would be to just query User/Address normally 
> then have them serialize into that structure with some method like 
> User.to_json().   Is the reason you don't want to use ORM entities due 
> to performance? 
>
> If you'd like the Bundle.create_row_processor to do it, that's 
> possible also but you need to re-implement a miniature identity map as 
> well as receive the nested rows and also deduplicate on the result 
> side.    Basically re-implementing what the ORM already does for you: 
>
> from sqlalchemy import * 
> from sqlalchemy.orm import * 
> from sqlalchemy.ext.declarative import declarative_base 
> from sqlalchemy.ext.declarative import declared_attr 
>
> Base = declarative_base() 
>
>
> class A(Base): 
>     __tablename__ = 'a' 
>
>     id = Column(Integer, primary_key=True) 
>     data = Column(String) 
>     bs = relationship("B") 
>
>
> class B(Base): 
>     __tablename__ = 'b' 
>     id = Column(Integer, primary_key=True) 
>     a_id = Column(ForeignKey("a.id")) 
>     data = Column(String) 
>
> e = create_engine("sqlite://", echo=True) 
> Base.metadata.create_all(e) 
>
> s = Session(e) 
>
> s.add_all([ 
>     A(data='a1', bs=[B(data='b1'), B(data='b2')]), 
>     A(data='a2', bs=[B(data='b3'), B(data='b4')]) 
> ]) 
> s.commit() 
>
>
> class ABundle(Bundle): 
>     def create_row_processor(self, query, procs, labels): 
>         """Override create_row_processor to return values as 
> dictionaries""" 
>
>         a_map = {} 
>
>         def proc(row): 
>             pk = row['a_id'] 
>             if pk in a_map: 
>                 rec = a_map[pk] 
>             else: 
>                 rec = a_map[pk] = { 
>                     "pk": row['a_id'], "data": row['a_data'], "bs": []} 
>
>             if row['b_id']: 
>                 rec['bs'].append({'pk': row['b_id'], 'data': 
> row['b_data']}) 
>
>             return rec 
>         return proc 
>
>
> def _unique_dict_rows(iterator): 
>     # uniquify dupe rows 
>     map_by_pk = {} 
>     for rec, in iterator: 
>         pk = rec['pk'] 
>         if pk not in map_by_pk: 
>             yield rec 
>             map_by_pk[rec['pk']] = rec 
>
>
> q = s.query( 
>     ABundle('mybundle', A.id, A.data, B.id, 
> B.data)).select_from(A).join(B) 
>
> print(list(_unique_dict_rows(q))) 
>
>
>
>
>
> > 
> > -- 
> > SQLAlchemy - 
> > The Python SQL Toolkit and Object Relational Mapper 
> > 
> > http://www.sqlalchemy.org/ 
> > 
> > To post example code, please provide an MCVE: Minimal, Complete, and 
> Verifiable Example. See http://stackoverflow.com/help/mcve for a full 
> description. 
> > --- 
> > You received this message because you are subscribed to the Google 
> Groups "sqlalchemy" group. 
> > To unsubscribe from this group and stop receiving emails from it, send 
> an email to sqlalchemy+...@googlegroups.com <javascript:>. 
> > To post to this group, send email to sqlal...@googlegroups.com 
> <javascript:>. 
> > Visit this group at https://groups.google.com/group/sqlalchemy. 
> > For more options, visit https://groups.google.com/d/optout. 
>

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to