Registrations ============= To make iommi understand the specifics of your code base you can register various handlers and behaviors. Django custom fields -------------------- To tell iommi how to handle your custom fields you have these options: * `register_factory`: register behavior for everything at once * `register_column_factory`: specific to `Column` * `register_filter_factory`: specific to `Filter` * `register_field_factory`: specific to `Field` You use the `register_factory` function to register your own factory. The simplest way is: .. code-block:: python register_factory( TimeField, shortcut_name='time' ) When iommi then sees a Django `TimeField` it will call the `Column.time` shortcut to create a column, `Filter.time` to create a `Filter` and `Field.time` to create a field. If you need different behavior for the three classes you need to use the more specific registration functions. You can also register `None` to tell iommi to just ignore the field type whenever it sees it. For more advanced behavior you can pass a `Shortcut` instance or a callable that returns a shortcut. This is the iommi definition for booleans: .. code-block:: python register_field_factory( BooleanField, factory=lambda model_field, **kwargs: ( Shortcut(call_target__attribute='boolean') if not model_field.null else Shortcut(call_target__attribute='boolean_tristate') ) ) Related fields (foreign key/many-to-many) -------------------------------------------- `ForeignKey`, `OneToOneField`, and `ManyToManyField` don't carry their semantic meaning in a custom type the way a `TimeField` does -- they're all keyed on the *related* model. To register behavior for them you point at the related model instead of a field class: * `register_related_factory`: foreign key and one-to-one, for everything at once * `register_related_column_factory`/`register_related_field_factory`/`register_related_filter_factory`: foreign key and one-to-one, for a specific class * `register_related_multiple_factory`: many-to-many and one-to-many (reverse foreign key), for everything at once * `register_related_multiple_column_factory`/`register_related_multiple_field_factory`/`register_related_multiple_filter_factory`: many-to-many and one-to-many, for a specific class .. code-block:: python # foreign key and one-to-one register_related_factory(User, shortcut_name='user') # many-to-many and one-to-many (aka reverse foreign key) register_related_multiple_factory(Role, shortcut_name='roles') When iommi then sees a `ForeignKey` to `User` it will call the `Column.user`/`Filter.user`/`Field.user` shortcuts, and a `ManyToManyField` to `Role` will use the `Column.roles`/`Filter.roles`/`Field.roles` shortcuts. You will need to add those shortcuts on your subclasses of `Column`, `Field`, and `Filter`. This is the primary mechanism behind :ref:`semantic-models`, see that page for a fuller example. Rendering of your custom types in a table ----------------------------------------- iommi renders `bool`, `list`, `set`, `tuple`, `QuerySet` and any type that has a `__html__` method with special logic to make it look nice in a table. If you have a type where you can't or don't want to implement a `__html__` method (or you want more complex rendering) you can plug into this system yourself with `register_cell_formatter`: .. code-block:: python register_cell_formatter(MyType, lambda value, **_: f'hello {value}') The callable you register gets the keyword arguments `value`, `table`, `column` and `row`. The search fields of your Django models --------------------------------------- When searching for an object with `Query` we need to know which fields to use to find the object. This enables the advanced query language to be `my_car_brand='toyota'` instead of `my_car_brand.pk=42` which is a lot nicer. iommi will automatically use a field called `name` if it exists and is unique. If you have other fields you want iommi to use to find objects you can register it like this: .. code-block:: python register_search_fields(model=Album, search_fields=['year'], allow_non_unique=True) On startup iommi registers just this one particular canonical name for you, since you probably want it. Note also that you can use `__` separated paths here if you have a one-to-one with another model where the name field exists. Custom styles ------------- You can register your own styles with `register_style`. By default the style `bootstrap` is used. You can use it as the basis of your custom look and feel or start with the `base` style and work from there.