Semantic models¶
The standard way in Django to define models is something like this:
class User(Model):
name = CharField()
person_number = CharField()
birth_place = ForeignKey(Location, on_delete=CASCADE)
manager = ForeignKey('self', on_delete=CASCADE)
The issue with that is that the semantic meaning of each field is hidden behind the name, and not in the type. The name
and person_number
fields have the same type but should be handled differently. Since iommi shortcut registrations are
based on the type, you can’t customize the parsing or rendering of the person_number
or birth_place
fields on
the project level via the Style.
Moreover in this example the type information alone is not enough for other customization. For ForeignKey
, by default
in iommi you’ll get a select2 drop-down to select from all items in that table. This is not good UX for a location, and
it’s probably not good UX for a manager field either as that should most likely exclude non-active users, and/or limited
to users with a certain role.
For CharField
, the default is to present a text field, but in the above model we want a Swedish “person number”, which
has a specific storage format, can accept a variety of input formats that can be unambiguously parsed, and even has a
checksum that can be used to validate that the user input is correct.
A solution to this is to create additional specialized types to specify semantic model fields:
class PersonNumberField(CharField):
pass
class LocationField(ForeignKey):
def __init__(self, to=None, *args, **kwargs):
assert to is None
to = Location
super().__init__(to=to, *args, **kwargs)
class UserField(ForeignKey):
def __init__(self, to=None, *args, **kwargs):
assert to is None
to = User
super().__init__(to=to, *args, **kwargs)
These fields can then be registered in iommi:
register_factory(PersonNumberField, shortcut_name='person_number')
register_factory(LocationField, shortcut_name='location')
register_factory(UserField, shortcut_name='user')
You will then need to add shortcuts for these in your subclasses of Column
, Field
, and Filter
. These can start out empty, and configuration can be done in the style definition:
your_style = Style(
bootstrap5,
Field=dict(
shortcuts=dict(
person_number__parse=person_number__parse,
),
),
)
It requires a little bit more initial setup, but for commonly used field types, it will make new views correct by default and super easy to setup.