Actually ignore that last message.  I see why that case is working for us 
now... we are introducing the 'Tire' join explicitly for another reason in 
the case I was thinking of, so that .expression accidentally (on our part) 
just happens to work.  If I just query for unicycle.weight_of_tires by 
itself, it has the same issues.

Think I'm getting it... thanks so much for the explanation.

On Thursday, December 17, 2020 at 11:01:17 AM UTC-5 Gmoney wrote:

> I think I follow you... definitely about how it would be done in SQL and 
> if constructing a query manually.  I think I wasn't fully clear about what 
> level of dynamic query building an .expression could drive.
> If I'm understanding correctly, it sounds like in our existing simpler 
> case, we may just be getting lucky that it works...  assume the same 
> example but for a unicycle ;)  just 'tire' (now only a single relationship 
> attribute of that type)
>
> @weight_of_tires.expression
> def weight_of_tires(cls):
>         return Tire.weight
>
> This actually works for us now in a couple situations.  But I'm guessing 
> it's because we are already referencing the unicycle.tire relationship 
> attribute which triggers the join.  The .expression itself would not 
> trigger the join on it's own and if used in a query filter alone, it 
> wouldn't work - maybe end up with a cartesian join without an ON clause.
>
> Am I on the right track there?
> On Thursday, December 17, 2020 at 10:42:28 AM UTC-5 Mike Bayer wrote:
>
>> this kind of issue should be approached by thinking in SQL.   the reason 
>> one wants to use a hybrid property at the class level is so that one could 
>> say:
>>
>> session.query(Motorcycle).filter(Motorcycle.weight_of_tires > 30)
>>
>>
>> OK.  So what SQL would be needed for that to work?    it's actually not 
>> very straightforward, as the SQL we would most likely use without using a 
>> hybrid expression would be a JOIN to the Tire table twice using aliases, 
>> then the WHERE criteria would need to refer to those aliases:
>>
>> ft = aliased(Tire)
>> rt = aliased(Tire)
>> session.query(Motorcycle).join(Motorcycle.front_tire.of_type(ft)).\
>>     join(Motorcycle.rear_tire.of_type(rt)).filter((ft.weight + rt.weight) 
>> > 30)
>>
>> Because our hybrid expression is not able to imply a JOIN, for the above 
>> to work we would have no choice but to make use of correlated subqueries, 
>> so for hybrid expression to work, one way would be to construct two 
>> select() constructs that correlate back to Motorcycle and then use them as 
>> expressions, or slightly better a single correlated subquery that does 
>> both.  this is not too unlike the EXISTS query we get when we do an 
>> expression like Cls.relationship.has().
>>
>> a rough sketch of the latter concept would look like:
>>
>> @weight_of_tires.expression
>> def weight_of_tires(cls):
>>     ft = aliased(Tire)
>>     rt = aliased(Tire)
>>     return select([ft.weight + rt.weight]).where(cls.front_tire_id == 
>> ft.id).where(cls.rear_tire_id == rt.id).as_scalar()
>>
>> note that the above query will generally be a poor performer compared to 
>> our pure SQL version that would use JOINs instead of correlated subqueries, 
>> however, I would suggest running a query plan on your target database to 
>> see if it is in fact an issue.
>>
>>
>>
>>     
>>
>>
>>
>> On Thu, Dec 17, 2020, at 10:21 AM, Gmoney wrote:
>>
>> Trying to understand the right way to define a hybridproperty.expression 
>> that references an 'sub-attribute' of a relationship attribute.  I have 
>> done it in another case by using the class name of the relationship 
>> attribute (Tire.weight vs. cls.tire.weight) and that's worked OK.  But it 
>> begs the question how to do it if that class (Tire) is used in multiple 
>> relationship attributes.  How does the .expression discern between the two.
>>
>> Hopefully the example below explains better than my words probably have.  
>> See the final .expression "weight_of_tires"... how to make it reference the 
>> front_tire attribute and rear_tire attribute - both of the same model class 
>> - distinctly?  This sample is using flask-alchemy - hopefully that doesn't 
>> confuse things.
>>
>> class Tire(db.Model):
>>     __tablename__ = 'tire'
>>     id = Column(Integer, primary_key=True)
>>     weight = Column(Integer)
>>     size = Column(VARCHAR(50))
>>
>> class Motorcycle(db.Model):
>>     __tablename__ = 'motorcycle'
>>     id = Column(Integer, primary_key=True)
>>     front_tire_id = Column(ForeignKey(Tire.id))
>>     rear_tire_id = Column(ForeignKey(Tire.id))
>>
>>     front_tire = relationship(Tire, foreign_keys=[front_tire_id], 
>> lazy='joined')
>>     rear_tire = relationship(Tire, foreign_keys=[rear_tire_id], 
>> lazy='joined')
>>
>>     @hybrid_property
>>     def weight_of_tires(self):
>>         return self.front_tire.weight + self.rear_tire.weight
>>
>>     @weight_of_tires.expression
>>     def weight_of_tires(cls):
>>         # return Tire.weight + Tire.weight
>>         # return cls.front_tire.weight + cls.rear_tire.weight
>>
>>
>> -- 
>> 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.
>> To view this discussion on the web visit 
>> https://groups.google.com/d/msgid/sqlalchemy/3e4cd06e-8d7f-4471-81f9-6ecbf8f236ban%40googlegroups.com
>>  
>> <https://groups.google.com/d/msgid/sqlalchemy/3e4cd06e-8d7f-4471-81f9-6ecbf8f236ban%40googlegroups.com?utm_medium=email&utm_source=footer>
>> .
>>
>>
>>

-- 
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 view this discussion on the web visit 
https://groups.google.com/d/msgid/sqlalchemy/10679cc3-fb1e-4817-bc49-79f6f096dacan%40googlegroups.com.

Reply via email to