Configure Template Engine

To configure your Django application to use PH7 templates, update settings.py to use PH7's Django template engine.

TEMPLATES = [
    {
        "NAME": "PH7",
        "BACKEND": "ph7.django.PH7Templates",
        "DIRS": [],
        "APP_DIRS": True,
        "OPTIONS": {},
    }
]

Templates

|_ home
 |_ templates
   |_ __init__.py # default view

Define your view in __init__.py, when defining the view you need to name the default view template, for example

from ph7.html import body, div, html, title

template = html(
    title("Hello World"),
    body(
        div(
            "Hello, World!",
        )
    ),
)

When rendering the view, refer to this template using relative path

from django.shortcuts import render


def home(request):
    return render(
        request=request,
        template_name="home",  # or "home:template"
    )

If you name your view anything other than template, you can specify the view name using module:view format. For example if you name your view home, you can specify the template as app:home.

Forms

Forms can be included directly using form tag.

from django import forms

from ph7.django import csrf_token
from ph7.html import body, button, div, form, html, title


class UserForm(forms.Form):
    """User form class."""

    name = forms.CharField(label="username")
    email = forms.EmailField(label="email")
    password = forms.CharField(label="password", widget=forms.PasswordInput())


template = html(
    title("Forms Example"),
    body(
        div(
            form(
                UserForm(),
                csrf_token,
                button("submit", type="submit"),
            ),
        )
    ),
)
<html>
  <head>
    <title>Forms Example</title>
  </head>
  <body>
    <div>
      <form>
        <label for="id_name">username:</label>
        <input type="text" name="name" required="" id="id_name" />
        <label for="id_email">email:</label>
        <input type="email" name="email" maxlength="320" required="" id="id_email"/>
        <label for="id_password">password:</label>
        <input type="password" name="password" required="" id="id_password" />
        <input
          name="csrfmiddlewaretoken"
          value="aaAAAaAAaAAAaaAAAAAAAaAaaAaAAAaaAAAaaaaaaAAaAAaaAAaaaAAaAAAAaaaa"
          id="csrfmiddlewaretoken"
          hidden="True"
        /><button type="submit">submit</button>
      </form>
    </div>
  </body>
</html>

Stylesheets

Define your stylesheet using CSSObject and use Static Context to include the stylesheets in your views. First let's define templates/styles.py

from ph7.css import CSSObject


class container(CSSObject):
    display = "flex"
    align_items = "center"
    justify_content = "center"

    height = "100vh"
    width = "100vw"

    box_sizing = "border-box"
    margin = "0px"
    padding = "0px"

Next include the stylesheet by importing it and using one of the style classes.

from static_files.templates import styles as stylesheet

from ph7.context import ctx
from ph7.html import body, div, head, html, title

ctx.static.view(__name__)


template = html(
    title("Static Files Example"),
    head(
        ctx.static.include,
    ),
    body(
        div(
            "Hello, World!",
            class_name=stylesheet.container,
        )
    ),
)

This is what the rendered view looks like

<html>
  <head>
    <title>Static Files Example</title>
    <link
      href="/static/css/static_files_templates_styles.css"
      rel="stylesheet"
      id="css.static_files.templates.styles"
    />
  </head>
  <body>
    <div class="main">Hello, World!</div>
  </body>
</html>

And this is what the rendered stylesheet looks like

.main {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
  width: 100vw;
  box-sizing: border-box;
  margin: 0px;
  padding: 0px;
}

Note: In the final render of the stylesheet generated by static context, only the classes which have been used in a template will be included.

Scripts

Write your scripts using the ph7.js APIs.

from ph7.js import alert, console, document, fetch, js_callable


@js_callable
async def fetchDog():
    console.log("Fetching dog")
    response = await fetch(
        "https://dog.ceo/api/breeds/image/random",
        {
            "method": "GET",
        },
    )
    data = await response.json()
    console.log("Dog fetched")
    document.getElementById("image").src = data.message

Use the function in the view

from javascript.templates.script import fetchDog
from javascript.templates.styles import container, image

from ph7.context import ctx
from ph7.html import body, button, div, head, html, img

ctx.static.view(__name__)


template = html(
    head(
        ctx.static.include,
    ),
    body(
        div(
            img(
                src="#",
                id="image",
                alt="Click to fetch dog",
                class_name=image,
            ),
            button(
                "Click to fetch a dog",
                on={
                    "click": fetchDog(),
                },
            ),
            class_name=container,
        )
    ),
)


if __name__ == "__main__":
    print(template.render({"_view": __name__}))

This is what the rendered view looks like

<html>
  <head>
    <link
      href="/static/css/javascript_templates_styles.css"
      rel="stylesheet"
      id="css.javascript.templates.styles"
    />
    <script
      src="/static/js/javascript_templates_script.js"
      id="js.javascript.templates.script"
    ></script>
  </head>
  <body>
    <div class="main">
      <img src="#" alt="Click to fetch dog" id="image" class="image" /><button
        onclick="fetchDog()"
      >
        Click to fetch a dog
      </button>
    </div>
  </body>
</html>

And this is what the rendered script looks like

async function fetchDog() {
  console.log("Fetching dog");
  let response = await fetch("https://dog.ceo/api/breeds/image/random", {
    method: "GET",
  });
  let data = await response.json();
  console.log("Dog fetched");
  document.getElementById("image").src = data.message;
}