a polymorphic association is hard.   that's why I have three examples of them 
and soon a fourth.    Though they are a subset of a larger batch of "tricks" 
that I've been using in my own work with declarative for the past year to 
automate lots of different kinds of patterns, perhaps there's a learning curve 
but once three or four techniques are mastered they come pretty easily.




On Feb 13, 2011, at 4:35 PM, Martijn Moeling wrote:

> Michael,
> 
> I looked at the code and I can not say more than that its very interesting, I 
> have to see how it works and more importantly how It fits into my objects but 
> it seems clear enough to do so.
> I really appreciate your work on SQLAlchemy and all the time you spend to 
> help us "users" out.
> 
> Your solution is definitively one I could not have put together myself. 
> Although I have tried. SQLAlchemy is so powerful that it is hard to find "the 
> right options" for the job.
> 
> Thank you again!
> 
> Martijn
> 
> On Feb 13, 2011, at 21:19 , Michael Bayer wrote:
> 
>> 
>> On Feb 13, 2011, at 6:14 AM, Martijn Moeling wrote:
>> 
>>> You are right in the misunderstood relation.
>>> 
>>> I see the primary key in extra to be wrong, extra should have it's own I'd 
>>> column being an auto number. In extra it should be possible to have many 
>>> records pointing to 1 ext variant. Sorry for that.
>>> 
>>> The extra, should also work with tables without a discriminator, there the 
>>> link should be made to  table name which is in my case always 
>>> class.__name__ ...... On those tables, the relation needs to be different 
>>> since on of the "local" columns, discriminator is not present and it 
>>> somehow should be linked to __table name__
>> 
>> OK what you are trying to do is exactly a "polymorphic association".   The 
>> technique of placing "tablename" in the table of "related" records, then 
>> using that "tablename" to indicate which parent table should be matched at 
>> query time, is a common, but IMHO relationally incorrect pattern.   I 
>> blogged extensively about the Ruby on Rails approach, how to duplicate 
>> Rails' approach in SQLAlchemy, and then an alternate system which maintains 
>> referential integrity, four years ago at 
>> http://techspot.zzzeek.org/2007/05/29/polymorphic-associations-with-sqlalchemy/
>>  .
>> 
>> That's a really old example and we're into 0.7 now, so I've created a new 
>> version of the poly assoc example that uses declarative techniques and the 
>> association proxy, which is attached.  I'm going to further fix up this 
>> example and add it to the distribution as a fourth example of polymorphic 
>> association, which is in examples/poly_assoc/.
>> 
>> Additionally I'd like the association proxy to work more smoothly in queries 
>> so I've added ticket #2054 for some of those issues which weren't accounted 
>> for when we first added any(), contains() operators to the association proxy.
>> 
>> 
>> 
>> 
>> 
>>> 
>>> It is all part of the ACL examples it talked about walker, where extra must 
>>> be seen as the ACL. That is where the mapper extension comes in....
>>> 
>>> It is getting a "all technologies" mixed in situation 
>>> 
>>> Verstuurd vanaf mijn iPad
>>> 
>>> Op Feb 12, 2011 om 17:05 heeft "Michael Bayer" <mike...@zzzcomputing.com> 
>>> het volgende geschreven:
>>> 
>>>> OK I can show you the version of your code that does most of this but 
>>>> there are some fundamental relational misunderstandings in this schema if 
>>>> I am interpreting correctly.
>>>> 
>>>> Extra:
>>>> 
>>>> tableid         tablename
>>>> -------         ---------
>>>> 1               ext1
>>>> 2               ext1
>>>> 3               ext2
>>>> 4               ext2
>>>> 5               ext3
>>>> 
>>>> ext1:
>>>> 
>>>> id       discriminator     (-> FK to Extra.tableid, Extra.tablename)
>>>> --       -------------
>>>> 1        ext1
>>>> 2        ext1
>>>> 3        ext2
>>>> 4        ext2
>>>> 5        ext3
>>>> 
>>>> ext2:
>>>> 
>>>> id
>>>> --
>>>> 3 
>>>> 4
>>>> 
>>>> ext3:
>>>> 
>>>> id
>>>> --
>>>> 
>>>> 5
>>>> 
>>>> given ext1 ID #3, discriminator "ext2" - how can more than one Extra row 
>>>> be referenced?  Why is "extras" assumed to be one-to-many when it can only 
>>>> be many-to-one ?
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> 
>>>> On Feb 12, 2011, at 9:57 AM, Martijn Moeling wrote:
>>>> 
>>>>> This whole thing is driving me crazy, What I want:
>>>>> 
>>>>> 
>>>>> 
>>>>> class Extra(Base):
>>>>> __tablename__            = "extra"
>>>>> # Primary key consists of two different columns !!!
>>>>> tableId                    = Column(Integer, primary_key=true)
>>>>> tablename                = Column(Unicode(20), primary_key=True)
>>>>> 
>>>>> info                        = Column(........) #Not relevant
>>>>> 
>>>>> 
>>>>> 
>>>>> 
>>>>> class ex1(Base):
>>>>> Id                  = Column(Integer, primary_key=True)
>>>>> discriminator       = Column(Unicode(20))
>>>>> 
>>>>> @declared_attr
>>>>> def __tablename__(self):
>>>>>    return self.__name__.lower()
>>>>> 
>>>>> @declared_attr
>>>>> def __mapper_args__(self):
>>>>>    if self.__name__ == 'ext1':
>>>>>        return {'polymorphic_on': self.discriminator, 
>>>>>                'polymorphic_identity':unicode(self.__name__.lower()),
>>>>>                'extension': FilePropertiesMapperExtension(),
>>>>>                'batch' : False}
>>>>>    else:
>>>>>        return {'polymorphic_identity':unicode(self.__name__.lower()),
>>>>>                'inherit_condition': self.Id == extra.Id,                  
>>>>>         #needed for something else in this config (multiple self 
>>>>> reference)
>>>>>                'extension': FilePropertiesMapperExtension(),              
>>>>>   #Needed for something else, not relevant for this sample
>>>>>                'batch' : False}                                           
>>>>>  # ,,            ,,            ,,            ,,
>>>>> 
>>>>> 
>>>>> # Set up foreignkey and relation to Extra....
>>>>> __table_args__      = (ForeignKeyConstraint(['discriminator', 'Id'], 
>>>>> ['extra.Table','extra.TableId']),{})
>>>>> 
>>>>> 
>>>>> extras                = relation('Extra', cascade="all", lazy="dynamic" 
>>>>> backref="owner")
>>>>> 
>>>>> 
>>>>> ....
>>>>> ....
>>>>> 
>>>>> 
>>>>> class ext2(ext1):
>>>>> Id                  = Column(Integer,ForeignKey('ext1.Id'), 
>>>>> primary_key=True)
>>>>> ......
>>>>> 
>>>>> 
>>>>> class ext3(ext1):
>>>>> Id                  = Column(Integer,ForeignKey('ext1.Id'), 
>>>>> primary_key=True)
>>>>> .....
>>>>> 
>>>>> 
>>>>> 
>>>>> 
>>>>> Now I want:
>>>>> 
>>>>> Ext2 = ext2()
>>>>> Extra_info = extra()
>>>>> Ext2.extras.append(Extra_Info)
>>>>> 
>>>>> 
>>>>> Ext2.discriminator should be "ext2"
>>>>> Ext2.Id should be 1 for the first record
>>>>> 
>>>>> Extra_Info should be created in the database, with its columns : id set 
>>>>> to the Ext2.id and tablename to Ext.discriminator ......
>>>>> Extra_Info.owner would point to Ext2
>>>>> 
>>>>> If Ext2 is deleted, all related extrainfo record would be delete too
>>>>> 
>>>>> if one Extra_Info is deleted, Extra_Info.owner should stay in place as 
>>>>> well as all other related 
>>>>> 
>>>>> Extra is many to one polymorhic version of ext1
>>>>> 
>>>>> I hope this clarifies more what I want....
>>>>> 
>>>>> I really need the @declared_attr way of doing stuff and that is not 
>>>>> related to this question but might influence this question so I left it 
>>>>> in......
>>>>> 
>>>>> Martijn
>>>>> 
>>>>> 
>>>>> On Feb 10, 2011, at 18:13 , Michael Bayer wrote:
>>>>> 
>>>>>> 
>>>>>> On Feb 10, 2011, at 4:20 AM, Martijn Moeling wrote:
>>>>>> 
>>>>>>> Another small thing:
>>>>>>> 
>>>>>>> I took a look at:
>>>>>>> 
>>>>>>> ForeignKeyConstraint(['invoice_id', 'ref_num'], ['invoice.invoice_id', 
>>>>>>> 'invoice.ref_num'])
>>>>>>> 
>>>>>>> Now for Polymorphic tables:
>>>>>>> 
>>>>>>> 
>>>>>>> in baseclass:
>>>>>>> 
>>>>>>> baseclass.discriminator happens to be the __tablename__ of the 
>>>>>>> polymorphic 
>>>>>>> 
>>>>>>> ForeignKeyConstraint('['baseclass.disciminator', baseclass.Id'], 
>>>>>>> ['someotherclass.tablename','someotherclass.tableId']
>>>>>>> relationship('someotherclass', backref=baseclass, cascade="all", 
>>>>>>> lazy="dynamic")
>>>>>>> 
>>>>>>> 
>>>>>>> in someotheclass:
>>>>>>> 
>>>>>>> tablename = column(Unicode(20), primary_key=True)
>>>>>>> tableId    = column(Integer, primary_key=True)
>>>>>>> 
>>>>>>> seems Ok to me.
>>>>>>> 
>>>>>>> Now I need to make someotherclass work with non-polymorphic tables too!!
>>>>>>> 
>>>>>>> anotherclass:
>>>>>>> Id = column(Integer, primary_key=True)
>>>>>>> ForeignKeyConstaint('[anotherclass.__tablename__,'anotherclass.Id'],['someotherclass.tablename','someotherclass.tableId'])
>>>>>>> relation('someotherclass', backref=baseclass, cascade="all", 
>>>>>>> lazy="dynamic")
>>>>>>> 
>>>>>>> Is there any way to get this working without configuring it as 
>>>>>>> polymorphic an do no Inhiritance, I do not want each anotherclass 
>>>>>>> record to have a column discriminator with its own tablename!
>>>>>>> 
>>>>>>> or can I use anotherclass.__tablename__ in the ForeignKeyConstaint?
>>>>>>> 
>>>>>>> This has to do with the someotherclass being the "ACL" I talked about 
>>>>>>> in a previous post if that gives extra info. I am trying to implement 
>>>>>>> the MapperExtension.before_append where I need to refer to the "ACL" 
>>>>>>> records in a way like:
>>>>>>> 
>>>>>>> for A in instance.ACL:
>>>>>>> .....
>>>>>> 
>>>>>> yeah, sorry, this use case continues to be 98% opaque to me.   I don't 
>>>>>> understand what you mean by "make someotherclass work with 
>>>>>> non-polymorphic tables", a class is mapped in just one way, either with 
>>>>>> or without a discriminator column.  A single class can't be mapped in 
>>>>>> both ways.    If there's no discriminator, there's just one class that 
>>>>>> can be used for returned rows.
>>>>>> 
>>>>>> If you could create a small test that illustrates a mapping and an 
>>>>>> expected result, perhaps I can attempt to find a way to get the ORM 
>>>>>> behavior you're looking for.
>>>>>> 
>>>>>> Your mapper extension would continue to be used normally with a 
>>>>>> "dynamic" relationship since it uses query(cls) just like a regular 
>>>>>> query.
>>>>>> 
>>>>>> 
>>>>>> 
>>>>>> -- 
>>>>>> You received this message because you are subscribed to the Google 
>>>>>> Groups "sqlalchemy" group.
>>>>>> To post to this group, send email to sqlalchemy@googlegroups.com.
>>>>>> To unsubscribe from this group, send email to 
>>>>>> sqlalchemy+unsubscr...@googlegroups.com.
>>>>>> For more options, visit this group at 
>>>>>> http://groups.google.com/group/sqlalchemy?hl=en.
>>>>>> 
>>>>> 
>>>>> -- 
>>>>> You received this message because you are subscribed to the Google Groups 
>>>>> "sqlalchemy" group.
>>>>> To post to this group, send email to sqlalchemy@googlegroups.com.
>>>>> To unsubscribe from this group, send email to 
>>>>> sqlalchemy+unsubscr...@googlegroups.com.
>>>>> For more options, visit this group at 
>>>>> http://groups.google.com/group/sqlalchemy?hl=en.
>>>>> 
>>>> 
>>>> -- 
>>>> You received this message because you are subscribed to the Google Groups 
>>>> "sqlalchemy" group.
>>>> To post to this group, send email to sqlalchemy@googlegroups.com.
>>>> To unsubscribe from this group, send email to 
>>>> sqlalchemy+unsubscr...@googlegroups.com.
>>>> For more options, visit this group at 
>>>> http://groups.google.com/group/sqlalchemy?hl=en.
>>>> 
>>> 
>>> -- 
>>> You received this message because you are subscribed to the Google Groups 
>>> "sqlalchemy" group.
>>> To post to this group, send email to sqlalchemy@googlegroups.com.
>>> To unsubscribe from this group, send email to 
>>> sqlalchemy+unsubscr...@googlegroups.com.
>>> For more options, visit this group at 
>>> http://groups.google.com/group/sqlalchemy?hl=en.
>>> 
>> 
>> <martijnmoeling.py>-- 
>> You received this message because you are subscribed to the Google Groups 
>> "sqlalchemy" group.
>> To post to this group, send email to sqlalchemy@googlegroups.com.
>> To unsubscribe from this group, send email to 
>> sqlalchemy+unsubscr...@googlegroups.com.
>> For more options, visit this group at 
>> http://groups.google.com/group/sqlalchemy?hl=en.
>> 
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "sqlalchemy" group.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> To unsubscribe from this group, send email to 
> sqlalchemy+unsubscr...@googlegroups.com.
> For more options, visit this group at 
> http://groups.google.com/group/sqlalchemy?hl=en.
> 

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to