it should work in 0.8 as well (and can be done even in 0.7 with some 
adjustments), just not the more optimized nested JOIN part.


On Dec 5, 2013, at 2:41 PM, Adrian Schreyer <adrian.schre...@gmail.com> wrote:

> I will try this out then, thanks for your help! I assume this works in 0.9 
> only?
> 
> 
> On Thu, Dec 5, 2013 at 7:18 PM, Michael Bayer <mike...@zzzcomputing.com> 
> wrote:
> With the example I gave, when accessing .partitioned on a First instance, the 
> lazy loader will convert all columns from “First” into a bound parameter, it 
> emits this:
> 
> 
>  SELECT partitioned.other_id AS partitioned_other_id, second.other_id AS 
> second_other_id, partitioned.partition_key AS partitioned_partition_key, 
> second.first_id AS second_first_id 
> FROM partitioned JOIN second ON partitioned.other_id = second.other_id 
> WHERE ? = partitioned.partition_key AND ? = second.first_id
> 2013-12-05 14:14:42,689 INFO sqlalchemy.engine.base.Engine (u'p1', 2)
> 
> 
> “first.partition_key” is not in the query, it’s replaced by ‘p1’ in this 
> case, the value that was assigned to that First instance.    There is no 
> “secondary” table per se in the example I gave.
> 
> 
> 
> 
> On Dec 5, 2013, at 1:55 PM, Adrian Schreyer <adrian.schre...@gmail.com> wrote:
> 
>> The partitioned relationship actually referred to the tertiary table in both 
>> the primary and secondary join - the problem for me was that in the 
>> primaryjoin
>> 
>> primaryjoin="and_(First.first_id==Second.first_id, 
>> First.partition_key==Partitioned.partition_key)"
>> only First.first_id will be interpolated with the actual value first_id of 
>> the instance in question whereas First.partition_key on the other hand will 
>> be interpolated as column object. The problem is that in this case 
>> First.partition_key has to be interpolated with the actual value to get the 
>> constraint-exclusion to work. In a normal many-to-many relationship this 
>> would not be necessary and maybe that is why it only interpolates the values 
>> for the join on the secondary table.
>> 
>> The partitioned relationship emits a query like this if the attribute is 
>> accessed:
>> 
>> 
>> SELECT partitioned.*
>>   FROM partitioned, second, first
>>  WHERE %(param_1)s = second.first_id
>>        AND first.partition_key = partitioned.partition_key
>>        AND second.other_id = partitioned.other_id
>> But I would need first.partitioned_key to be %(param_2)s.
>> 
>> So far I used a @property around a query function to add the partition_key 
>> to query.filter() manually.
>> 
>> 
>> 
>> On Thu, Dec 5, 2013 at 4:37 PM, Michael Bayer <mike...@zzzcomputing.com> 
>> wrote:
>> oh, you want to refer to the tertiary table in both the primary and 
>> secondary join.    so right this pattern does not correspond to the 
>> A->secondary->B pattern and isn’t really a classic many-to-many.
>> 
>> a quick way to map these are to use non primary mappers (was going to just 
>> paraphrase, but let me just try it out b.c. these are fun anyway, and I want 
>> to see the new joining behavior we have in 0.9…):
>> 
>> from sqlalchemy import *
>> from sqlalchemy.orm import *
>> from sqlalchemy.ext.declarative import declarative_base
>> 
>> Base = declarative_base()
>> 
>> class First(Base):
>>     __tablename__ = 'first'
>> 
>>     first_id = Column(Integer, primary_key=True)
>>     partition_key = Column(String)
>> 
>>     def __repr__(self):
>>         return ("First(%s, %s)" % (self.first_id, self.partition_key))
>> 
>> class Second(Base):
>>     __tablename__ = 'second'
>> 
>>     id = Column(Integer, primary_key=True)
>>     first_id = Column(Integer)
>>     other_id = Column(Integer)
>> 
>> class Partitioned(Base):
>>     __tablename__ = 'partitioned'
>> 
>>     id = Column(Integer, primary_key=True)
>>     partition_key = Column(String)
>>     other_id = Column(Integer)
>> 
>>     def __repr__(self):
>>         return ("Partitioned(%s, %s)" % (self.partition_key, self.other_id))
>> 
>> 
>> j = join(Partitioned, Second, Partitioned.other_id == Second.other_id)
>> 
>> partitioned_second = mapper(Partitioned, j, non_primary=True, properties={
>>         # note we need to disambiguate columns here - the join()
>>         # will provide them as j.c.<tablename>_<colname> for access,
>>         # but they retain their real names in the mapping
>>         "id": j.c.partitioned_id,
>>         "other_id": [j.c.partitioned_other_id, j.c.second_other_id],
>>         "secondary_id": j.c.second_id
>>     })
>> 
>> First.partitioned = relationship(
>>                             partitioned_second,
>>                             primaryjoin=and_(
>>                                 First.partition_key == 
>> partitioned_second.c.partition_key,
>>                                 First.first_id == 
>> foreign(partitioned_second.c.first_id)
>>                             ), innerjoin=True)
>> 
>> e = create_engine("postgresql://scott:tiger@localhost/test", echo=True)
>> Base.metadata.drop_all(e)
>> Base.metadata.create_all(e)
>> s = Session(e)
>> s.add_all([
>>     First(first_id=1, partition_key='p1'),
>>     First(first_id=2, partition_key='p1'),
>>     First(first_id=3, partition_key='p2'),
>>     Second(first_id=1, other_id=1),
>>     Second(first_id=2, other_id=1),
>>     Second(first_id=3, other_id=2),
>>     Partitioned(partition_key='p1', other_id=1),
>>     Partitioned(partition_key='p1', other_id=2),
>>     Partitioned(partition_key='p2', other_id=2),
>> ])
>> s.commit()
>> 
>> for row in s.query(First, Partitioned).join(First.partitioned):
>>     print(row)
>> 
>> for f in s.query(First):
>>     for p in f.partitioned:
>>         print(f.partition_key, p.partition_key)
>> 
>> 
>> I mapped to a join directly, and not a select, so as long as we aren’t using 
>> SQLite (and are using 0.9) we get nested join behavior like this:
>> 
>> SELECT first.first_id AS first_first_id, first.partition_key AS 
>> first_partition_key, partitioned.id AS partitioned_id, 
>> partitioned.partition_key AS partitioned_partition_key, partitioned.other_id 
>> AS partitioned_other_id 
>> FROM first JOIN (partitioned JOIN second ON partitioned.other_id = 
>> second.other_id) ON first.partition_key = partitioned.partition_key AND 
>> first.first_id = second.first_id
>> 2013-12-05 11:27:18,347 INFO sqlalchemy.engine.base.Engine {}
>> (First(1, p1), Partitioned(p1, 1))
>> (First(2, p1), Partitioned(p1, 1))
>> (First(3, p2), Partitioned(p2, 2))
>> 
>> 
>> the load of f.partitioned will load the Partitioned objects in terms of the 
>> “partitioned_second” mapper, so those objects will have those extra cols 
>> from “second” on them.  You can screw around with this using 
>> exclude_properties for those cols you don’t need to refer to on the mapping, 
>> and perhaps primary_key if the mapper complains, such as:
>> 
>> partitioned_second = mapper(Partitioned, j, non_primary=True, properties={
>>         "id": j.c.partitioned_id,
>>         "other_id": [j.c.partitioned_other_id, j.c.second_other_id],
>>     }, exclude_properties=[j.c.second_id], primary_key=[j.c.partitioned_id, 
>> j.c.second_other_id])
>> 
>> or you can just ignore those extra attributes on some of your Partitioned 
>> objects.
>> 
>> 
>> 
>> 
>> 
>> 
>> 
>> 
>> On Dec 5, 2013, at 11:03 AM, Adrian Schreyer <adrian.schre...@gmail.com> 
>> wrote:
>> 
>>> Given the three mappings First, Second and Partitioned, I want to declare a 
>>> relationship between First and Partitioned. The problem is that Partitioned 
>>> is partitioned by partition_key which is a column in First but not in 
>>> Second. Second however contains the identifier that actually links First to 
>>> specific rows in the partitioned table.
>>> 
>>> So far the mapping looks like this mock example:
>>> 
>>> 
>>> partitioned = relationship("Partitioned",
>>>     secondary=Base.metadata.tables['schema.seconds'],
>>>     primaryjoin="and_(First.first_id==Second.first_id, 
>>> First.partition_key==Partitioned.partition_key)",
>>>     secondaryjoin="Second.other_id==Partitioned.other_id",
>>>     foreign_keys="[Second.first_id, Partitioned.partition_key, 
>>> Partitioned.other_id]",
>>>     uselist=True, innerjoin=True, lazy='dynamic')
>>> It works, but it only interpolates the First.first_id with the actual value 
>>> which normally makes sense but to make the PostgreSQL constraint-exclusion 
>>> work the First.partition_key would need to be interpolated with the proper 
>>> value as well. Right now it is only given as 
>>> First.partition_key==Partitioned.partition_key.
>>> 
>>> Does that make sense? I am not sure if my relationship configuration is 
>>> wrong or if this kind of mapping is simply not supported.
>>> 
>>> 
>>> 
>>> On Thu, Dec 5, 2013 at 3:31 PM, Michael Bayer <mike...@zzzcomputing.com> 
>>> wrote:
>>> 
>>> On Dec 5, 2013, at 6:57 AM, Adrian Schreyer <adrian.schre...@gmail.com> 
>>> wrote:
>>> 
>>>> Actually that was a bit too early but I tracked the problem down to the 
>>>> many-to-many relationship. Parameters are only interpolated (e.g. 
>>>> %(param_1)s) for the primaryjoin to the secondary table. Is there a 
>>>> technique to force relationship() to interpolate a parameter between the 
>>>> 1st and 3rd table instead of using only table.column=table.column?
>>> 
>>> there’s no reason why that would be the case can you provide more specifics?
>>> 
>>> 
>>> 
>>> 
>>>> 
>>>> 
>>>> On Thu, Dec 5, 2013 at 10:58 AM, Adrian Schreyer 
>>>> <adrian.schre...@gmail.com> wrote:
>>>> Never mind,
>>>> 
>>>> the problem was that I specified the clause in a secondaryjoin and not in 
>>>> the primaryjoin of the relationship().
>>>> 
>>>> 
>>>> On Thu, Dec 5, 2013 at 10:44 AM, Adrian <adrian.schre...@gmail.com> wrote:
>>>> Hi All,
>>>> 
>>>> I have a few partitioned tables in my PostgreSQL database but I do not 
>>>> know yet how to make the ORM relationship() with partition 
>>>> constraint-exclusion on the instance level. Constraint-exclusion does not 
>>>> work with joins and requires scalar values - the problem is that I would 
>>>> need to add an additional WHERE clause to the primaryjoin (which adds the 
>>>> partition key) if the relationship is accessed from the instance level, 
>>>> e.g. user.addresses. Is there a mechanism in relationship() to distinguish 
>>>> between class-based joins (User.addresses) and instance-level access?
>>>> 
>>>> -- 
>>>> You received this message because you are subscribed to a topic in the 
>>>> Google Groups "sqlalchemy" group.
>>>> To unsubscribe from this topic, visit 
>>>> https://groups.google.com/d/topic/sqlalchemy/ov-mYWA7XAM/unsubscribe.
>>>> To unsubscribe from this group and all its topics, send an email to 
>>>> sqlalchemy+unsubscr...@googlegroups.com.
>>>> To post to this group, send email to sqlalchemy@googlegroups.com.
>>>> Visit this group at http://groups.google.com/group/sqlalchemy.
>>>> For more options, visit https://groups.google.com/groups/opt_out.
>>>> 
>>>> 
>>>> 
>>>> -- 
>>>> 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 http://groups.google.com/group/sqlalchemy.
>>>> For more options, visit https://groups.google.com/groups/opt_out.
>>> 
>>> 
>>> 
>>> -- 
>>> 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 http://groups.google.com/group/sqlalchemy.
>>> For more options, visit https://groups.google.com/groups/opt_out.
>> 
>> 
>> 
>> -- 
>> 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 http://groups.google.com/group/sqlalchemy.
>> For more options, visit https://groups.google.com/groups/opt_out.
> 
> 
> 
> -- 
> 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 http://groups.google.com/group/sqlalchemy.
> For more options, visit https://groups.google.com/groups/opt_out.

Attachment: signature.asc
Description: Message signed with OpenPGP using GPGMail

Reply via email to