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:
class Album(Model):
name = CharField(max_length=255, db_index=True)
artist = ForeignKey(Artist, on_delete=CASCADE, related_name='albums')
year = IntegerField(default=1980)
genres = ManyToManyField(Genre, related_name='albums')
published_date = DateField(null=True, blank=True)
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.
table = Table(
model=Album,
columns=dict(
name=Column(),
),
)
This simple table can be written as a class definition:
class MyTable(Table):
class Meta:
model = Album
name = Column()
There are two things to notice here:
Variables declared in
class Metain iommi means they get passed into the constructor.model = AlbuminMetais exactly the same asTable(model=Album).The
namecolumn is declared on the class itself, and thecolumnspart of the argument (Table(columns=dict(...)) is implicit. ForPagethe same implicit name is calledparts, and forFormit’s calledfields.
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.
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:
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:
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):
form = Form.create(
auto__model=Album,
auto__exclude=['artist'],
)
form = Form.create(
auto=dict(
model=Album,
exclude=['artist'],
),
)
form = Form.create(
auto__model=Album,
fields__artist__include=False,
)
class AlbumForm(Form):
class Meta:
auto__model = Album
auto__exclude = ['artist']
form = AlbumForm.create()
class AlbumForm(Form):
class Meta:
auto__model = Album
auto__include = ['name', 'year']
form = AlbumForm.create()
class AlbumForm(Form):
class Meta:
auto__model = Album
fields__artist__include = False
form = AlbumForm.create()
Without using the auto features:
class AlbumForm(Form):
name = Field()
year = Field.integer()
class Meta:
title = 'Create album'
actions__submit__post_handler = create_album
form = AlbumForm()
form = Form(
fields__name=Field(),
fields__year=Field.integer(),
title='Create album',
actions__submit__post_handler=create_album,
)
You can read more about this in the philosophy section under Declarative/programmatic hybrid API.