Equivalence =========== In iommi there are two equivalence principles that are important to grasp: - declarative/programmatic hybrid API - double underscore as a short hand syntax for nesting dicts The model used for these examples is `Album`: .. literalinclude:: models.py :start-at: class Album :end-before: def __str__ :language: python Declarative/programmatic hybrid API ----------------------------------- The programmatic API is pretty straightforward: you have a class constructor that takes some arguments. The interesting part is how we can mirror that *exactly* into a declarative style. .. code-block:: python table = Table( model=Album, columns=dict( name=Column(), ), ) This simple table can be written as a class definition: .. code-block:: python class MyTable(Table): class Meta: model = Album name = Column() There are two things to notice here: 1. Variables declared in `class Meta` in iommi means they get passed into the constructor. `model = Album` in `Meta` is exactly the same as `Table(model=Album)`. 2. The `name` column is declared on the class itself, and the `columns` part of the argument (`Table(columns=dict(...)`) is implicit. For `Page` the same implicit name is called `parts`, and for `Form` it's called `fields`. Note that values set in `Meta` are *defaults*, not hard coded values, so you can still override them in the constructor call (or in a subclass). .. note:: Writing `MyTable().as_view()` in your `urlpatterns` looks superficially like a Django class based view, but the two are unrelated. Notice the parentheses after the class name: you are creating an *instance* and asking it for a view, which is why you can pass arguments at that point (`MyTable(page_size=2).as_view()`). Django CBVs and iommi classes are radically different concepts and can't be combined. .. _dunder-dict-equivalence: Double underscore short form ---------------------------- In iommi you can have very deeply nested object structures, and because you want to customize something deep inside a graph it would be cumbersome to nest dicts a lot. So `__` is used as a separator. Say we have a table, where we want to turn on filtering for a column, but we want to insert a special CSS class (called `special`) on the label of the search field: .. code-block:: python table = Table( auto__model=Model, # Enable filtering columns__name__filter__include=True, # Set the CSS class on the label columns__name__filter__field__label__attrs__class__special=True, ) We could also write this without using `__` for nesting: .. code-block:: python table = Table( auto=dict(model=Model), columns=dict( name=dict( filter=dict( # Enable filtering include=True, # Set the CSS class on the label field=dict( label=dict( attrs={ # have to use a dict literal here, # because `class` is a reserved keyword in Python 'class': dict( special=True, ), }, ), ), ), ), ), ) These two things have exactly the same meaning, but the `__` syntax is a lot shorter and cleaner. Further examples ---------------- We want to create a form to create an album for a specific artist. We already have the artist from the URL, so that field shouldn't be in the form. The following forms all accomplish this goal (you can use `form.as_view()` to create a view from a `Form` instance): .. code-block:: python form = Form.create( auto__model=Album, auto__exclude=['artist'], ) .. raw:: html
▼ Hide result
Toggle structure
.. code-block:: python form = Form.create( auto=dict( model=Album, exclude=['artist'], ), ) .. raw:: html
► Show result
Toggle structure
.. code-block:: python form = Form.create( auto__model=Album, fields__artist__include=False, ) .. raw:: html
► Show result
Toggle structure
.. code-block:: python class AlbumForm(Form): class Meta: auto__model = Album auto__exclude = ['artist'] form = AlbumForm.create() .. raw:: html
► Show result
Toggle structure
.. code-block:: python class AlbumForm(Form): class Meta: auto__model = Album auto__include = ['name', 'year'] form = AlbumForm.create() .. raw:: html
► Show result
Toggle structure
.. code-block:: python class AlbumForm(Form): class Meta: auto__model = Album fields__artist__include = False form = AlbumForm.create() .. raw:: html
► Show result
Toggle structure
Without using the `auto` features: .. code-block:: python class AlbumForm(Form): name = Field() year = Field.integer() class Meta: title = 'Create album' actions__submit__post_handler = create_album form = AlbumForm() .. raw:: html
► Show result
Toggle structure
.. code-block:: python form = Form( fields__name=Field(), fields__year=Field.integer(), title='Create album', actions__submit__post_handler=create_album, ) .. raw:: html
► Show result
Toggle structure
You can read more about this in the philosophy section under :ref:`philosophy_hybrid_api`.