fix: exclude FK fields by default, for model forms
e.g. `person_uuid` and such
This commit is contained in:
parent
c800ebf4e4
commit
c4fe90834e
|
@ -32,6 +32,7 @@ import uuid as _uuid
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy import orm
|
||||||
|
|
||||||
import colander
|
import colander
|
||||||
from webhelpers2.html import HTML, tags
|
from webhelpers2.html import HTML, tags
|
||||||
|
@ -478,24 +479,36 @@ def render_csrf_token(request, name='_csrf'):
|
||||||
return HTML.tag('div', tags.hidden(name, value=token, id=None), style='display:none;')
|
return HTML.tag('div', tags.hidden(name, value=token, id=None), style='display:none;')
|
||||||
|
|
||||||
|
|
||||||
def get_model_fields(config, model_class=None):
|
def get_model_fields(config, model_class, include_fk=False):
|
||||||
"""
|
"""
|
||||||
Convenience function to return a list of field names for the given
|
Convenience function to return a list of field names for the given
|
||||||
model class.
|
:term:`data model` class.
|
||||||
|
|
||||||
This logic only supports SQLAlchemy mapped classes and will use
|
This logic only supports SQLAlchemy mapped classes and will use
|
||||||
that to determine the field listing if applicable. Otherwise this
|
that to determine the field listing if applicable. Otherwise this
|
||||||
returns ``None``.
|
returns ``None``.
|
||||||
"""
|
|
||||||
if not model_class:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
:param config: App :term:`config object`.
|
||||||
|
|
||||||
|
:param model_class: Data model class.
|
||||||
|
|
||||||
|
:param include_fk: Whether to include foreign key column names in
|
||||||
|
the result. They are excluded by default, since the
|
||||||
|
relationship names are also included and generally preferred.
|
||||||
|
|
||||||
|
:returns: List of field names, or ``None`` if it could not be
|
||||||
|
determined.
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
mapper = sa.inspect(model_class)
|
mapper = sa.inspect(model_class)
|
||||||
except sa.exc.NoInspectionAvailable:
|
except sa.exc.NoInspectionAvailable:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if include_fk:
|
||||||
fields = [prop.key for prop in mapper.iterate_properties]
|
fields = [prop.key for prop in mapper.iterate_properties]
|
||||||
|
else:
|
||||||
|
fields = [prop.key for prop in mapper.iterate_properties
|
||||||
|
if not prop_is_fk(mapper, prop)]
|
||||||
|
|
||||||
# nb. we never want the continuum 'versions' prop
|
# nb. we never want the continuum 'versions' prop
|
||||||
app = config.get_app()
|
app = config.get_app()
|
||||||
|
@ -505,6 +518,20 @@ def get_model_fields(config, model_class=None):
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
|
|
||||||
|
def prop_is_fk(mapper, prop):
|
||||||
|
""" """
|
||||||
|
if not isinstance(prop, orm.ColumnProperty):
|
||||||
|
return False
|
||||||
|
|
||||||
|
prop_columns = [col.name for col in prop.columns]
|
||||||
|
for rel in mapper.relationships:
|
||||||
|
rel_columns = [col.name for col in rel.local_columns]
|
||||||
|
if rel_columns == prop_columns:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def make_json_safe(value, key=None, warn=True):
|
def make_json_safe(value, key=None, warn=True):
|
||||||
"""
|
"""
|
||||||
Convert a Python value as needed, to ensure it is compatible with
|
Convert a Python value as needed, to ensure it is compatible with
|
||||||
|
|
|
@ -11,6 +11,8 @@ from fanstatic import Library, Resource
|
||||||
from pyramid import testing
|
from pyramid import testing
|
||||||
|
|
||||||
from wuttjamaican.conf import WuttaConfig
|
from wuttjamaican.conf import WuttaConfig
|
||||||
|
from wuttjamaican.testing import ConfigTestCase
|
||||||
|
|
||||||
from wuttaweb import util as mod
|
from wuttaweb import util as mod
|
||||||
|
|
||||||
|
|
||||||
|
@ -463,14 +465,10 @@ class TestGetFormData(TestCase):
|
||||||
self.assertEqual(data, {'foo2': 'baz'})
|
self.assertEqual(data, {'foo2': 'baz'})
|
||||||
|
|
||||||
|
|
||||||
class TestGetModelFields(TestCase):
|
class TestGetModelFields(ConfigTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
self.config = WuttaConfig()
|
|
||||||
self.app = self.config.get_app()
|
|
||||||
|
|
||||||
def test_empty_model_class(self):
|
def test_empty_model_class(self):
|
||||||
fields = mod.get_model_fields(self.config)
|
fields = mod.get_model_fields(self.config, None)
|
||||||
self.assertIsNone(fields)
|
self.assertIsNone(fields)
|
||||||
|
|
||||||
def test_unknown_model_class(self):
|
def test_unknown_model_class(self):
|
||||||
|
@ -482,6 +480,19 @@ class TestGetModelFields(TestCase):
|
||||||
fields = mod.get_model_fields(self.config, model.Setting)
|
fields = mod.get_model_fields(self.config, model.Setting)
|
||||||
self.assertEqual(fields, ['name', 'value'])
|
self.assertEqual(fields, ['name', 'value'])
|
||||||
|
|
||||||
|
def test_include_fk(self):
|
||||||
|
model = self.app.model
|
||||||
|
|
||||||
|
# fk excluded by default
|
||||||
|
fields = mod.get_model_fields(self.config, model.User)
|
||||||
|
self.assertNotIn('person_uuid', fields)
|
||||||
|
self.assertIn('person', fields)
|
||||||
|
|
||||||
|
# fk can be included
|
||||||
|
fields = mod.get_model_fields(self.config, model.User, include_fk=True)
|
||||||
|
self.assertIn('person_uuid', fields)
|
||||||
|
self.assertIn('person', fields)
|
||||||
|
|
||||||
def test_avoid_versions(self):
|
def test_avoid_versions(self):
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue