Commit 1e8520da authored by Florian Sachs's avatar Florian Sachs

Reuseable functions for handling of inherited pks

Two new functions on `contrib.sql.tools`:

  - `is_inherited_primary_key(prop)` : Returns True if the `ColumnProperty` **prop** is an inherited primary key. Occuring Exceptions are not handled but raised. *Should* work with arbitrary levels of inheritance.
  - `get_column_for_current_model(prop)` : Return the `Column` that belongs to the model of the `ColumnProperty` **prop**. So if the ColumnProperty is for the parent, the Column for the parent ist returned. If the ColumnProperty if for the Child, the Column for the child is returned. Works with arbitrary levels of inheritance.

`AdminModelConverter.convert()`  and `ModelView.scaffold_list_columns()` are adapted to use the new functions.
parent 3d897ee8
...@@ -12,6 +12,7 @@ from flask.ext.admin._compat import iteritems ...@@ -12,6 +12,7 @@ from flask.ext.admin._compat import iteritems
from .validators import Unique from .validators import Unique
from .fields import QuerySelectField, QuerySelectMultipleField, InlineModelFormList from .fields import QuerySelectField, QuerySelectMultipleField, InlineModelFormList
from .tools import is_inherited_primary_key, get_column_for_current_model
try: try:
# Field has better input parsing capabilities. # Field has better input parsing capabilities.
...@@ -132,17 +133,8 @@ class AdminModelConverter(ModelConverterBase): ...@@ -132,17 +133,8 @@ 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:
# Check if all columns are primary keys and _one_ does not have a foreign key -> looks like joined if is_inherited_primary_key(prop):
# table inheritance: http://docs.sqlalchemy.org/en/latest/orm/inheritance.html with "standard column = get_column_for_current_model(prop)
# practice" of same column name
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: else:
raise TypeError('Can not convert multiple-column properties (%s.%s)' % (model, prop.key)) raise TypeError('Can not convert multiple-column properties (%s.%s)' % (model, prop.key))
else: else:
......
...@@ -25,3 +25,39 @@ def get_primary_key(model): ...@@ -25,3 +25,39 @@ def get_primary_key(model):
return p.key return p.key
return None return None
def is_inherited_primary_key(prop):
"""
Return True, if the ColumnProperty is an inherited primary 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
practice" of same column name.
:param prop: The ColumnProperty to check
:return: Boolean
:raises: Exceptions as they occur - no ExceptionHandling here
"""
return 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
def get_column_for_current_model(prop):
"""
Return the Column() of the ColumnProperty "prop", that refers to the current model
When using inheritance, a ColumnProperty may contain multiple columns. This function
returns the Column(), the belongs to the Model of the ColumnProperty - the "current"
model
:param prop: The ColumnProperty
:return: The column for the current model
:raises: TypeError if not exactely one Column() for the current model could be found.
All other Exceptions not handled here but raised
"""
candidates = [column for column in prop.columns if column.expression == prop.expression]
if len(candidates) != 1:
raise TypeError('Not exactly one column for the current model found. ' +
'Found %d columns for property %s' % (len(candidates), prop))
else:
return candidates[0]
...@@ -15,7 +15,7 @@ from flask.ext.admin._backwards import ObsoleteAttr ...@@ -15,7 +15,7 @@ from flask.ext.admin._backwards import ObsoleteAttr
from flask.ext.admin.contrib.sqla import form, filters, tools from flask.ext.admin.contrib.sqla import form, filters, tools
from .typefmt import DEFAULT_FORMATTERS from .typefmt import DEFAULT_FORMATTERS
from .tools import is_inherited_primary_key, get_column_for_current_model
class ModelView(BaseModelView): class ModelView(BaseModelView):
""" """
...@@ -310,25 +310,16 @@ class ModelView(BaseModelView): ...@@ -310,25 +310,16 @@ class ModelView(BaseModelView):
elif hasattr(p, 'columns'): elif hasattr(p, 'columns'):
column_inherited_primary_key = False column_inherited_primary_key = False
if len(p.columns) != 1: if len(p.columns) != 1:
# Check if all columns are primary keys and _one_ does not have a foreign key -> looks like joined if is_inherited_primary_key(p):
# table inheritance: http://docs.sqlalchemy.org/en/latest/orm/inheritance.html with "standard column = get_column_for_current_model(p)
# 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: else:
raise TypeError('Can not convert multiple-column properties (%s.%s)' % (model, p.key)) raise TypeError('Can not convert multiple-column properties (%s.%s)' % (model, p.key))
else: else:
# Grab column # Grab column
column = p.columns[0] column = p.columns[0]
if column.foreign_keys and not column_inherited_primary_key: # An inherited primary key has a foreign key as well
if column.foreign_keys and not is_inherited_primary_key(p):
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