Open caffeinatedMike opened 2 years ago
I've been able to add BooleanField support of association proxies with a bit of hackiness.
flask_admin/contrib/sqla/form.py - AdminModelConverter
def _convert_relation(self, name, prop, property_is_association_proxy, kwargs):
# Check if relation is specified
form_columns = getattr(self.view, 'form_columns', None)
if form_columns and name not in form_columns:
return None
try: # newly-added
remote_model = prop.mapper.class_
column = prop.local_remote_pairs[0][0]
# If this relation points to local column that's not foreign key, assume
# that it is backref and use remote column data
if not column.foreign_keys:
column = prop.local_remote_pairs[0][1]
except AttributeError: # newly-added
remote_model = prop.parent.class_ # newly-added
column = prop.expression # newly-added
kwargs['label'] = self._get_label(name, kwargs)
kwargs['description'] = self._get_description(name, kwargs)
# determine optional/required, or respect existing
requirement_options = (validators.Optional, validators.InputRequired)
requirement_validator_specified = any(isinstance(v, requirement_options) for v in kwargs['validators'])
if property_is_association_proxy or column.nullable or prop.direction.name != 'MANYTOONE':
kwargs['allow_blank'] = True
if not requirement_validator_specified:
kwargs['validators'].append(validators.Optional())
else:
kwargs['allow_blank'] = False
if not requirement_validator_specified:
kwargs['validators'].append(validators.InputRequired())
# Override field type if necessary
override = self._get_field_override(prop.key)
if override:
if override is fields.BooleanField: # newly-added
kwargs.pop("allow_blank") # newly-added
return override(**kwargs)
multiple = (property_is_association_proxy or
(prop.direction.name in ('ONETOMANY', 'MANYTOMANY') and prop.uselist))
return self._model_select_field(prop, multiple, remote_model, **kwargs)
Then, making sure to add a creator
function to my association proxy
@declarative_mixin
class EntityRelationshipMixin:
@classmethod
def join_builder(cls, remote_cls):
return and_(
foreign(remote_cls.entity_type) == cls.__tablename__,
foreign(remote_cls.entity_id) == cls.id
)
@declared_attr
def gs(cls):
return db.relationship(
"GoogleStorage",
primaryjoin=lambda: cls.join_builder(GoogleStorage),
backref=cls.__tablename__,
overlaps=f"reporter,account,gs,{cls.__tablename__}",
uselist=False # only a single entry will be related back to the record
)
@declared_attr
def use_gs(cls):
return association_proxy(
"gs",
"use_gs",
creator=lambda value: GoogleStorage(
use_gs=value,
entity_type=cls.__tablename__,
entity_id=cls.id
)
)
And finally, adding the association fields to form_overrides
, assigning the appropriate field type
class ReporterView(ModelView):
column_list = ("module", "use_gs")
column_editable_list = column_list
column_filters = column_list
form_overrides = {"use_gs": BooleanField}
@michaelbukachi Any interest in having a look at "properly" implementing better support for specifying association/relationship fields in column_editable_list
?
@caffeinatedMike sure thing. I'll take this up.
Hey @michaelbukachi Any luck with this? I'm finding a need for it with relationships more than association proxies now.
Can anyone provide a way to make an
association_proxy
value work ineditable_column_list
? I'd like to have a boolean field from a related table displayed in list view. I did find this StackOverflow question from almost 5 years ago that asks about the same thing, but the solution does not work for me (and I'm assuming a lot has changed with flask-admin and sqlalchemy since then).Unfortunately, in my attempts, I keep running into this error in the
_convert_relation
function.Here is a minimal, reproducible example