sponsfreixes / jinja2-fragments

Render Jinja2 template block as HTML page fragments on Python web frameworks.
MIT License
245 stars 13 forks source link

AIOHTTP template render_block with HTMX #5

Closed ystekno closed 1 year ago

ystekno commented 1 year ago

Django & HTMX - Template Fragments with django-render-block https://www.youtube.com/watch?v=BsGak1t23QA

I tried to make the django example at this address to be aiohttp jinja2. I couldn't adapt it to aiohttp. Sample code is attached.

import jinja2
import aiohttp_jinja2
from aiohttp import web
from pathlib import Path
from models import Device
from tortoise.contrib.aiohttp import register_tortoise
from jinja2_fragments import render_block

here = Path(__file__).resolve().parent

##########################################################
@aiohttp_jinja2.template("home.html")
async def home(request):
    return {}

@aiohttp_jinja2.template("device_list.html")
async def device_list(request):
    objects = await Device.all().order_by('name')
    return {'objects': objects, 'title': 'DEVICE LIST HTMX'}

async def device_add(request: web.Request) -> web.Response:
    form = await request.post()
    code = form['code']
    name = form['name']

    await Device.create(code=code, name=name)
    headers={'HX-Trigger': "device_list_changed"}

    #html =  render_block("device_list.jinja2", "device_form_block", context)
    #response = web.Response(html)

    return web.Response(status=204, headers=headers)

async def device_all(request):
    objects = await Device.all().order_by('name')
    """
    headers={
                    'HX-Trigger': json.dumps({
                        "device_list_changed": None,
                        "showMessage": f"device added.",
                        "objects": objects
                    })
                    }

    """
    context = {'objects': objects}
    html =  render_block("device_list.jinja2", "device_list_block", context)
    return web.Response(html)
#################################################################
app = web.Application()
app.router.add_get('/', home, name="home")
app.router.add_get('/device/list', device_list, name="device-list")
app.router.add_post('/device/add', device_add, name='device-add')
[aiohttp_template_render_block.zip](https://github.com/sponsfreixes/jinja2-fragments/files/10138379/aiohttp_template_render_block.zip)

app.router.add_get('/device_all', device_all, name="device_all")
app.add_routes([web.static('/static', here / 'static' )])
register_tortoise(
    app,
    db_url='postgres://postgres:root@localhost:5433/myweb',
    modules={'models': ['models']},
    generate_schemas=True
)
aiohttp_jinja2.setup(
    app,
    loader=jinja2.FileSystemLoader('templates'),
    autoescape=jinja2.select_autoescape(("html", "jinja2"))
)
web.run_app(app, port=8000)
{% extends "base.html" %}

{% block content %}

    <div class="container">

        <div class="row">
            <h3>{{ title }}</h3>
        </div>

        {% block device_form_block %}
        <div class="row" id="add-form-container">
                <form class="" hx-post="/device/add" hx-target="#add-form-container" hx-swap="outerHTML">

                            <div class="row mb-2">
                                <label for="code" class="col-md-2 col-form-label">Code</label>
                                <div class="col-md-10">
                                  <input type="text" class="form-control" name="code" value="" placeholder="code.....">
                                </div>
![error1](https://user-images.githubusercontent.com/51874093/205223392-76401124-dbb6-4d11-97b4-a5ccf6f559cc.png)

                            </div>
                            <div class="row mb-2">
                                <label for="name" class="col-md-2 col-form-label">Name</label>
                                <div class="col-md-10">
                                  <input type="text" class="form-control" name="name" value="" placeholder="name....." required>
                                </div>
                            </div>

                            <div class="row mb-2">
                                <label for="" class="col-md2 col-form-label"></label>
                                <div class="col-md-10">
                                  <input type="submit" class="btn btn-success" value="Save">
                                </div>
                            </div>
                        </form>
        </div>
        {% endblock %}

        {% block device_list_block %}
        <div class="row" id="add-form-container">
            <table class="table">
              <thead>
                <tr>
                  <th scope="col">Code</th>
                  <th scope="col">Name</th>

                </tr>
              </the
![error1](https://user-images.githubusercontent.com/51874093/205223502-f673e368-5162-4f74-a6fa-08f26520c44c.png)
ad>
              <tbody hx-trigger="device_list_changed from:body" hx-get="/device_all" hx-swap="outerHTML">
                {% for row in objects %}
                    <tr>
                    <td>{{ row.code }}</td>
                    <td>{{ row.name }}</td>

                    </tr>
                {% endfor %}
              </tbody>
            </table>
        </div>
        {% endblock %}
    </div>
{% endblock %}
ystekno commented 1 year ago

sample project:

aiohttp_template_render_block.zip

error1

sponsfreixes commented 1 year ago

@ystekno when you call render_block from jinja2_fragments, the first argument you need to pass is the Jinja environment.

On your example, you are passing a string (the template name) instead:

html =  render_block("device_list.jinja2", "device_list_block", context)

From what I read on the docs of aiohttp-jinja, it seems you can retrieve the environment with aiohttp_jinja2.get_env. Hence, you should be able to use it this way:

env = aiohttp_jinja2.get_env(app)
html =  render_block(env, "device_list.jinja2", "device_list_block", context)

Let me know if that works. Ideally, we would add an integration for aiohttp-jinja2 that removes that extra step of having to pass the environment.

ystekno commented 1 year ago

Thans a lot.I solved it with your help.I got the solution as below:

async def device_all(request):
    objects = await Device.all().order_by('name')
    context = {'objects': objects}
    env = aiohttp_jinja2.get_env(app)
    html =  render_block(env, "device_list.html", "device_list_block", context)
    return web.Response(text=html)
<div class="row" id="add-form-container">
            <table class="table">
              <thead>
                <tr>
                  <th scope="col">Code</th>
                  <th scope="col">Name</th>

                </tr>
              </thead>
              {% block device_list_block %}
              <tbody hx-trigger="device_list_changed from:body" hx-get="/device_all" hx-swap="outerHTML">
                {% for row in objects %}
                    <tr>
                    <td>{{ row.code }}</td>
                    <td>{{ row.name }}</td>

                    </tr>
                {% endfor %}
              </tbody>
              {% endblock %}
            </table>
        </div>
sponsfreixes commented 1 year ago

Happy to hear that!