Commit 3d897ee8 authored by Florian Sachs's avatar Florian Sachs

process inherited primary keys correctely

When using joined table inheritance (http://docs.sqlalchemy.org/en/latest/orm/inheritance.html), it is common practice to name the pk-property the same like the pk-property of the parent. The child-property ist a pk itself and has a foreign-key relationship to the pk-property of the parent.

Example:

    class BaseWahl(Model):
        __tablename__ = 'basewahlen'

        id = Column(Integer, primary_key=True, autoincrement=True)
        discriminator = db.Column(db.String(50))

        __mapper_args__ = {
        'polymorphic_identity':'basewahl',
        'polymorphic_on': discriminator
        }

    class Wahl(BaseWahl):
        __tablename__ = 'wahlen'

        id = Column(Integer, ForeignKey('basewahlen.id'), primary_key=True)
        __mapper_args__ = {
            'polymorphic_identity':'wahl',
        }

`AdminModelConverter.convert()` does not allow Properties with multiple columns, but will raise a `TypeError`. I changed it into the following way:

- If
  - more than 1 column for the property
  - all columns are primary keys
  - only *one* does not have a Foreign key
  - only one column corresponds to the current model
- select the column, that corresponds to the current model

I applied the same code to `ModelView.scaffold_list_columns()` and extended it, the primary key in this special constellation actually is *not* ignored, even if it has a foreign key property, so it can be displayed using `column_display_pk = True`.

This solution actually works for me. I do not have enough insight into *Flask-Admin* and definitely even less into *sqlalchemy* to think my solution is the correct one for every situation, but it may be something to think about.
parent 7a35ce85
...@@ -132,10 +132,22 @@ class AdminModelConverter(ModelConverterBase): ...@@ -132,10 +132,22 @@ class AdminModelConverter(ModelConverterBase):
if hasattr(prop, 'columns'): if hasattr(prop, 'columns'):
# Check if more than one column mapped to the property # Check if more than one column mapped to the property
if len(prop.columns) != 1: if len(prop.columns) != 1:
raise TypeError('Can not convert multiple-column properties (%s.%s)' % (model, prop.key)) # Check if all columns are primary keys and _one_ does not have a foreign key -> looks like joined
# table inheritance: http://docs.sqlalchemy.org/en/latest/orm/inheritance.html with "standard
# Grab column # practice" of same column name
column = prop.columns[0] if len([column for column in prop.columns if column.primary_key]) == len(prop.columns) and \
len([column for column in prop.columns if column.foreign_keys]) == len(prop.columns)-1:
# Get the column(s) of the current model - should always be only one, I think (but not sure)
candidates = [column for column in prop.columns if column.expression == prop.expression]
if len(candidates) != 1:
raise TypeError('Can not convert multiple-column pk-Property (%s.%s)' % (model, prop.key))
else:
column = candidates[0]
else:
raise TypeError('Can not convert multiple-column properties (%s.%s)' % (model, prop.key))
else:
# Grab column
column = prop.columns[0]
# Do not display foreign keys - use relations # Do not display foreign keys - use relations
if column.foreign_keys: if column.foreign_keys:
......
...@@ -308,10 +308,27 @@ class ModelView(BaseModelView): ...@@ -308,10 +308,27 @@ class ModelView(BaseModelView):
if self.column_display_all_relations or p.direction.name == 'MANYTOONE': if self.column_display_all_relations or p.direction.name == 'MANYTOONE':
columns.append(p.key) columns.append(p.key)
elif hasattr(p, 'columns'): elif hasattr(p, 'columns'):
# TODO: Check for multiple columns column_inherited_primary_key = False
column = p.columns[0] if len(p.columns) != 1:
# Check if all columns are primary keys and _one_ does not have a foreign key -> looks like joined
# table inheritance: http://docs.sqlalchemy.org/en/latest/orm/inheritance.html with "standard
# practice" of same column name
if len([column for column in p.columns if column.primary_key]) == len(p.columns) and \
len([column for column in p.columns if column.foreign_keys]) == len(p.columns)-1:
# Get the column(s) of the current model - should always be only one, I think (but not sure)
candidates = [column for column in p.columns if column.expression == p.expression]
if len(candidates) != 1:
raise TypeError('Can not convert multiple-column pk-Property (%s.%s)' % (model, p.key))
else:
column = candidates[0]
column_inherited_primary_key = True
else:
raise TypeError('Can not convert multiple-column properties (%s.%s)' % (model, p.key))
else:
# Grab column
column = p.columns[0]
if column.foreign_keys: if column.foreign_keys and not column_inherited_primary_key:
continue continue
if not self.column_display_pk and column.primary_key: if not self.column_display_pk and column.primary_key:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment