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')
Declarative/programmatic hybrid API¶
The programmatic API is pretty straight forward: 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 Meta
in iommi means they get passed into the constructor.model = Album
inMeta
is exactly the same asTable(model=Album)
.The
name
column is declared on the class itself, and thecolumns
part of the argument (Table(columns=dict(...)
) is implicit. ForPage
the same implicit name is calledparts
, and forForm
it’s calledfields
.
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 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.