Given the arbitrary example below, I can't ever recall actually using the 
FooKeyword association table other than to set up mappings. I came up with 
a brute force method to generate the "secondary" table for me 
automatically, and I'm hoping someone can show me a better way to do this. 

My goal was to take something like this (imports excluded for brevity):
    class Keyword(Base):
        __tablename__ = 'keyword'
        id = Column(Integer, primary_key = True)
        name = Column(String, unique = True, nullable = False)

    class Foo(Base):
        __tablename__ = 'foo'
        id = Column(Integer, primary_key = True)
        ... snip ...
        keywords = relationship(Keyword, secondary='foo_keyword')

    class FooKeyword(Base):
        __tablename__ = 'foo_keyword'
        foo_id = Column(Integer, ForeignKey('foo.id'), primary_key = True, 
nullable = False)
        keyword_id = Column(Integer, ForeignKey('keyword.id'), primary_key 
= True, nullable = False)


And replace it with something like this:
    class Keyword(Base):
        .... snip  (same as before) ....
    
    class Foo(Base):
        .... snip  ....
        keywords = generate_many_to_many_for_me('Foo', 'Keyword')


So in the code above, the FooKeyword table would get generated 
automatically, and a relationship like "relationship(Keyword, 
secondary='foo_keyword') would automatically get returned.

Here is my super naive solution that sort of works:
    """So below here, a call like "secondary_relationship(Foo, Keyword)"
    would automatically generate a class called FooKeyword that is 
    mapped to a table called 'foo_keyword'. It then uses these to return a
    relationship equivalent to relationship(Keyword, 
secondary='foo_keyword').

    You can specify the generated tablename using the tblname arg like so:
        "secondary_relationship(Foo, Keyword, "my_tablename")        
    """
    def secondary_relationship(cls1, cls2, tblname = None):
        new_class_name = cls1.__name__ + cls2.__name__
        tn1, tn2 = cls1.__tablename__, cls2.__tablename__
        tbl = '%s_%s' % (tn1, tn2) if tblname is None else tblname

        # Generate the "FooKeyword" table
        t = type(new_class_name, (Base,), {
            '__tablename__': tbl,
            tn1 + '_id': Column(Integer,
                                ForeignKey('%s.id' % tn1),
                                primary_key = True, nullable = False),
            tn2 + '_id': Column(Integer,
                                ForeignKey('%s.id' % tn2),
                                primary_key = True, nullable = False)
            })
        return relationship(cls2, secondary = tbl)

    # the Keyword and Foo classes identical to first example...
    class Keyword(Base):
        .... snip  (same as before) ....
    class Foo(Base):
        .... snip (same as before except keywords defined below) ....

    # And this builds the many to many for us without having to build 
FooKeyword class...
    Foo.keywords = secondary_relationship(Foo, Keyword)

    # You could also do like below to control the tablename generated:
    Foo.keywords = secondary_relationship(Foo, Keyword, 'my_tablename')


This actually works, but you can't use this until *after* the definition 
for Foo. I'm looking for a way to do this inline in Foo like so:
    class Foo(Base):
        .... snip ....
        keywords = secondary_relationship('Foo', 'Keyword')


Is there a better way? I have spent the better part of the day reading the 
source code to see how the declarative extension allows string class names 
for relationship, but I still haven't been able to figure this out...

Thanks,
Jeff Peck




-- 
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.

Reply via email to