@@ -41,6 +41,13 @@ def plural_noun(self, noun): # needed for backrefs
4141 return inflect_engine .plural_noun (noun )
4242
4343
44+ # In SQLAlchemy 0.x, constraint.columns is sometimes a list, on 1.x onwards, always a ColumnCollection
45+ def _get_column_names (constraint ):
46+ if isinstance (constraint .columns , list ):
47+ return constraint .columns
48+ return list (constraint .columns .keys ())
49+
50+
4451def _convert_to_valid_identifier (name ):
4552 assert name , 'Identifier cannot be empty'
4653 if name [0 ].isdigit () or iskeyword (name ):
@@ -71,7 +78,7 @@ def visit_bindparam(self, bindparam, within_columns_clause=False, literal_binds=
7178def _get_constraint_sort_key (constraint ):
7279 if isinstance (constraint , CheckConstraint ):
7380 return 'C{0}' .format (constraint .sqltext )
74- return constraint .__class__ .__name__ [0 ] + repr (constraint . columns )
81+ return constraint .__class__ .__name__ [0 ] + repr (_get_column_names ( constraint ) )
7582
7683
7784def _get_common_fk_constraints (table1 , table2 ):
@@ -177,7 +184,7 @@ def render_fk_options(*opts):
177184 remote_column = '{0}.{1}' .format (constraint .column .table .fullname , constraint .column .name )
178185 return _flask_prepend + 'ForeignKey({0})' .format (render_fk_options (remote_column ))
179186 elif isinstance (constraint , ForeignKeyConstraint ):
180- local_columns = constraint . columns
187+ local_columns = _get_column_names ( constraint )
181188 remote_columns = ['{0}.{1}' .format (fk .column .table .fullname , fk .column .name )
182189 for fk in constraint .elements ]
183190 return _flask_prepend + 'ForeignKeyConstraint({0})' .format (render_fk_options (local_columns , remote_columns ))
@@ -313,7 +320,8 @@ def __init__(self, table, association_tables, inflect_engine, detect_joined):
313320 for constraint in sorted (table .constraints , key = _get_constraint_sort_key ):
314321 if isinstance (constraint , ForeignKeyConstraint ):
315322 target_cls = self ._tablename_to_classname (constraint .elements [0 ].column .table .name , inflect_engine )
316- if detect_joined and self .parent_name == 'Base' and set (constraint .columns ) == pk_column_names :
323+ if (detect_joined and self .parent_name == 'Base' and
324+ set (_get_column_names (constraint )) == pk_column_names ):
317325 self .parent_name = target_cls
318326 else :
319327 relationship_ = ManyToOneRelationship (self .name , target_cls , constraint , inflect_engine )
@@ -452,7 +460,8 @@ class ManyToOneRelationship(Relationship):
452460 def __init__ (self , source_cls , target_cls , constraint , inflect_engine ):
453461 super (ManyToOneRelationship , self ).__init__ (source_cls , target_cls )
454462
455- colname = constraint .columns [0 ]
463+ column_names = _get_column_names (constraint )
464+ colname = column_names [0 ]
456465 tablename = constraint .elements [0 ].column .table .name
457466 if not colname .endswith ('_id' ):
458467 self .preferred_name = inflect_engine .singular_noun (tablename ) or tablename
@@ -462,7 +471,7 @@ def __init__(self, source_cls, target_cls, constraint, inflect_engine):
462471
463472 # Add uselist=False to One-to-One relationships
464473 if any (isinstance (c , (PrimaryKeyConstraint , UniqueConstraint )) and
465- set (col .name for col in c .columns ) == set (constraint . columns )
474+ set (col .name for col in c .columns ) == set (column_names )
466475 for c in constraint .table .constraints ):
467476 self .kwargs ['uselist' ] = 'False'
468477
@@ -481,7 +490,7 @@ def __init__(self, source_cls, target_cls, constraint, inflect_engine):
481490 self .kwargs ['primaryjoin' ] = "'and_({0})'" .format (', ' .join (['{0}.{1} == {2}.{3}' .format (source_cls , k .parent .name , target_cls , k .column .name )
482491 for k in constraint .elements ]))
483492 else :
484- self .kwargs ['primaryjoin' ] = "'{0}.{1} == {2}.{3}'" .format (source_cls , constraint . columns [0 ], target_cls ,
493+ self .kwargs ['primaryjoin' ] = "'{0}.{1} == {2}.{3}'" .format (source_cls , column_names [0 ], target_cls ,
485494 constraint .elements [0 ].column .name )
486495
487496
@@ -492,16 +501,16 @@ def __init__(self, source_cls, target_cls, assocation_table, inflect_engine):
492501 self .kwargs ['secondary' ] = repr (assocation_table .schema + '.' + assocation_table .name )
493502 constraints = [c for c in assocation_table .constraints if isinstance (c , ForeignKeyConstraint )]
494503 constraints .sort (key = _get_constraint_sort_key )
495- colname = constraints [1 ]. columns [0 ]
504+ colname = _get_column_names ( constraints [1 ]) [0 ]
496505 tablename = constraints [1 ].elements [0 ].column .table .name
497506 self .preferred_name = tablename if not colname .endswith ('_id' ) else colname [:- 3 ] + 's'
498507 self .backref_name = inflect_engine .plural_noun (self .backref_name )
499508
500509 # Handle self referential relationships
501510 if source_cls == target_cls :
502511 self .preferred_name = 'parents' if not colname .endswith ('_id' ) else colname [:- 3 ] + 's'
503- pri_pairs = zip (constraints [0 ]. columns , constraints [0 ].elements )
504- sec_pairs = zip (constraints [1 ]. columns , constraints [1 ].elements )
512+ pri_pairs = zip (_get_column_names ( constraints [0 ]) , constraints [0 ].elements )
513+ sec_pairs = zip (_get_column_names ( constraints [1 ]) , constraints [1 ].elements )
505514 pri_joins = ['{0}.{1} == {2}.c.{3}' .format (source_cls , elem .column .name , assocation_table .name , col )
506515 for col , elem in pri_pairs ]
507516 sec_joins = ['{0}.{1} == {2}.c.{3}' .format (target_cls , elem .column .name , assocation_table .name , col )
0 commit comments