Parts & Pages

How do I override part of a part/page?

This is all just standard iommi declarative magic, but as you are likely new to it this might take a while to get used to. Let’s say you created yourself a master template for your site.

class BasePage(Page):
    title = html.h1('My awesome webpage')
    subtitle = html.h2('It rocks')
▼ Hide result
Toggle structure

Which you can use like this:

class IndexPage(BasePage):
    body = 'body'

index = IndexPage(parts__subtitle__children__child='Still rocking...').as_view()
▼ Hide result
Toggle structure

or as a function based view:

def index(request):
    return IndexPage(parts__subtitle__children__child='Still rocking...')
► Show result
Toggle structure

Here you can see that Part s (Page s are themselves Part s) form a tree and the direct children are gathered in the parts namespace. Here we overwrote a leaf of an existing namespace, but you can also add new elements or replace bigger parts (and most of the time it doesn’t matter if you use the class Meta or the keyword arguments to init syntax):

class IndexPage(BasePage):
    title = html.img(
        attrs=dict(
            src='https://docs.iommi.rocks/_static/logo_with_outline.svg',
            alt='iommi logo',
            width='70px',
        ),
    )

index = IndexPage(parts__subtitle=None)
▼ Hide result
Toggle structure

In the above we replaced the title and removed the subtitle element completely. The latter of which shows one of the gotchas as only str, Part and the django template types are gathered into the parts structure when a Part class definition is processed. As None is not an instance of those types, you can remove things by setting their value to None.

How do I set the title of my page?

The title of a the page is text shown in the browser tab.

Page(title='The title in the browser')

Note that this is different from:

class MyPage(Page):
    title = Header('A header element in the dom')

MyPage()

Which is equivalent to:

Page(parts__title=Header('A header element in the dom'))

How do I specify the context used when a Template is rendered?

class MyPage(Page):
    body = Template("""A django template was rendered on {{today}}.""")


def index(request):
    context = {'today': date.today()}

    return MyPage(context=context)
▼ Hide result
Toggle structure

You can also insert items in the context via the declaration. This not only makes the above shorter, but also makes it easy to write abstractions that can be extended later:

my_page = Page(
    parts__body=Template("""A django template was rendered on {{today}}."""),
    context__today=lambda **_: date.today(),
).as_view()
▼ Hide result
Toggle structure

How do I make a menu?

A Menu is built from MenuItem s in its sub_menu. Each item’s url defaults to /<name>/, and the item whose regex matches the current URL gets the active_class (put that class on the item itself instead of its link with active_class_on_item). Nest items by giving a MenuItem its own sub_menu. By default items keep their declared order; set sort=True to sort them by name. The <a> tag of an item is configured via a, and the element wrapping the whole list of items via items_container:

menu = Menu(
    sub_menu=dict(
        root=MenuItem(url='/', display_name='Home'),
        albums=MenuItem(),
        artists=MenuItem(
            active_class_on_item=True,
            sub_menu=dict(
                black_sabbath=MenuItem(),
                ozzy=MenuItem(),
            ),
        ),
    ),
)
▼ Hide result
Toggle structure

How do I add a custom endpoint?

Any Part can have custom endpoints under endpoints. Give an endpoint a func that returns the response - an HttpResponse, a Part (which is rendered for you), or anything JSON-serializable. It answers ?/<name>. Allow POST by setting http_methods:

page = Page(
    parts__h1=html.h1('Hi!'),
    endpoints__echo__func=lambda value, **_: value,
)

How do I add CSS or JavaScript assets to a page?

Add Asset s under assets (on any Part, or in your Style). An Asset is a Fragment, so you set its tag and children like any other fragment. By default assets render in the <head>; set in_body=True to render them at the end of the <body> instead, which is handy for scripts:

page = Page(
    assets__custom_css=Asset.css(attrs__href='/static/custom.css'),
    assets__custom_script=Asset(
        tag='script',
        children__text='console.log("hi");',
        in_body=True,
    ),
)
▼ Hide result
Toggle structure

How do I set the content of a fragment-like part?

Many parts are Fragment s - an HTML tag plus a dict of children. Action, Header and a table’s container all work this way, so you build up their contents through children:

page = Page(
    parts__heading=Header(
        children__text='Albums',
        children__count=html.span(' (2)'),
    ),
    parts__link=Action(
        children__text='Add album',
        children__icon=html.i(attrs__class={'fa': True, 'fa-plus': True}),
        attrs__href='/albums/create/',
    ),
)
▼ Hide result
Toggle structure

How do I group actions together?

Give actions the same group and they are rendered together (for example in a dropdown):

form = Form(
    fields__name=Field(),
    actions__save=Action.submit(group='Manage'),
    actions__reset=Action.button(display_name='Reset', group='Manage'),
)
▼ Hide result
Toggle structure