I often want to display the list of related models for one-to-many relationship so that they can easily be navigated to via the links. The default formatting of printing all model attributes is often unreadable, and is sometimes unusable for larger models.
This is easily solved for one-to-one relationships or the "one-side" of a one-to-many relationship by updating the views column_type_formatters
with a format function which returns a readable representation. This does not work for the "many-side" of a one-many relationship because ModelView._default_formatter
only checks the "outer type", which returns list
. I would expect the formatter to also check the "inner type", which in the below example would be Hero
for Team.heros.
class Team(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
heroes: List["Hero"] = Relationship(back_populates="team")
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
uuid: UUID = Field(default_factory=uuid4)
name: str = Field(index=True, max_length=5)
secret_name: str
age: Optional[int] = None
team_id: Optional[int] = Field(default=None, foreign_key="team.id")
team: Optional[Team] = Relationship(back_populates="heroes")
def HeroAdmin(ModelView, model=Hero):
column_list = [Hero.name, Hero.team]
def team_format(team:Team):
return team.name
# This works
HeroAdmin.column_type_formatters.update({Team: team_format})
class TeamAdmin(ModelView, model=Team):
column_list = [Team.name, Team.heroes]]
def hero_format(hero:Hero):
return hero.name
# this does not work
TeamAdmin.column_type_formatters.update({Hero: hero_format})
I would like to see an implementation like the the below for ModelView._default_formatter
. I believe this to be a robust solution as the existing behavior is maintained, even if a formatter for type list
is registered with the model. If someone specifies a formatter for T
on a model which specifies a values: List[T] = Relationship(back_populates="something")
field, I think it is safe to assume they expect that formatter to work for that field. It also works for an empty list.
class ModelView(BaseView, metaclass=ModelViewMeta):
...
def _default_formatter(self, value: Any) -> Any:
if type(value) in self.column_type_formatters:
formatter = self.column_type_formatters[type(value)]
return formatter(value)
if isinstance(value, list) and value:
inner_type = type(value[0])
if inner_type in self.column_type_formatters:
formatter = self.column_type_formatters[inner_type]
return [formatter(val) for val in value]
return value
....
A potential workaround which requires no changes is to specify a formatter for type list
, and use a bunch of checks within the function like the below. This doesn't feel like a clean solution to me though.
def list_format(list_item:list):
if not list_item:
return
inner_type = type(list_item[0])
if inner_type is Hero:
return [hero.name for hero in list_item]
if inner_type is OtherType:
return [thing.id for thing in list_item]
# etc....
return list_item
Pay now to fund the work behind this issue.
Get updates on progress being made.
Maintainer is rewarded once the issue is completed.
You're funding impactful open source efforts
You want to contribute to this effort
You want to get funding like this too