Phase 7: Replace render_template() with s-expression rendering in all POST/PUT/DELETE routes

Eliminates all render_template() calls from POST/PUT/DELETE handlers across
all 7 services. Moves sexp_components.py into sexp/ packages per service.

- Blog: like toggle, snippets, cache clear, features/sumup/entry panels,
  create/delete market, WYSIWYG editor panel (render_editor_panel)
- Federation: like/unlike/boost/unboost, follow/unfollow, actor card,
  interaction buttons
- Events: ticket widget, checkin, confirm/decline/provisional, tickets
  config, posts CRUD, description edit/save, calendar/slot/ticket_type
  CRUD, payments, buy tickets, day main panel, entry page
- Market: like toggle, cart add response
- Account: newsletter toggle
- Cart: checkout error pages (3 handlers)
- Orders: checkout error page (1 handler)

Remaining render_template() calls are exclusively in GET handlers and
internal services (email templates, fragment endpoints).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 01:15:29 +00:00
parent e65232761b
commit 838ec982eb
64 changed files with 2920 additions and 545 deletions

View File

@@ -109,15 +109,9 @@ def register():
session_id=session_id,
)
# Render OOB template
nav_oob = await render_template(
"_types/day/admin/_nav_entries_oob.html",
confirmed_entries=visible.confirmed_entries,
post=g.post_data["post"],
calendar=calendar,
day_date=day_date,
)
return nav_oob
# Render OOB nav
from sexp.sexp_components import render_day_entries_nav_oob
return render_day_entries_nav_oob(visible.confirmed_entries, calendar, day_date)
async def get_post_nav_oob(entry_id: int):
"""Helper to generate OOB update for post entries nav when entry state changes"""
@@ -152,13 +146,9 @@ def register():
)
).scalars().all()
# Render OOB template for this post's nav
nav_oob = await render_template(
"_types/post/admin/_nav_entries_oob.html",
associated_entries=associated_entries,
calendars=calendars,
post=post,
)
# Render OOB nav for this post
from sexp.sexp_components import render_post_nav_entries_oob
nav_oob = render_post_nav_entries_oob(associated_entries, calendars, post)
nav_oobs.append(nav_oob)
return "".join(nav_oobs)
@@ -250,19 +240,15 @@ def register():
@require_admin
async def get(entry_id: int, **rest):
from shared.browser.app.utils.htmx import is_htmx_request
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_entry_page, render_entry_oob
# Full template for both HTMX and normal requests
tctx = await get_template_context()
if not is_htmx_request():
# Normal browser request: full page with layout
html = await render_template(
"_types/entry/index.html",
)
html = await render_entry_page(tctx)
else:
html = await render_template(
"_types/entry/_oob_elements.html",
)
html = await render_entry_oob(tctx)
return await make_response(html, 200)
@bp.get("/edit/")
@@ -431,10 +417,11 @@ def register():
# Get nav OOB update
nav_oob = await get_day_nav_oob(year, month, day)
html = await render_template(
"_types/entry/index.html",
#entry=entry,
)
from shared.sexp.page import get_template_context
from sexp.sexp_components import render_entry_page
tctx = await get_template_context()
html = await render_entry_page(tctx)
return await make_response(html + nav_oob, 200)
@@ -457,8 +444,10 @@ def register():
day_nav_oob = await get_day_nav_oob(year, month, day)
post_nav_oob = await get_post_nav_oob(entry_id)
# redirect back to calendar admin or order page as you prefer
html = await render_template("_types/entry/_optioned.html")
# Re-read entry to get updated state
await g.s.refresh(g.entry)
from sexp.sexp_components import render_entry_optioned
html = render_entry_optioned(g.entry, g.calendar, day, month, year)
return await make_response(html + day_nav_oob + post_nav_oob, 200)
@bp.post("/decline/")
@@ -480,8 +469,10 @@ def register():
day_nav_oob = await get_day_nav_oob(year, month, day)
post_nav_oob = await get_post_nav_oob(entry_id)
# redirect back to calendar admin or order page as you prefer
html = await render_template("_types/entry/_optioned.html")
# Re-read entry to get updated state
await g.s.refresh(g.entry)
from sexp.sexp_components import render_entry_optioned
html = render_entry_optioned(g.entry, g.calendar, day, month, year)
return await make_response(html + day_nav_oob + post_nav_oob, 200)
@bp.post("/provisional/")
@@ -503,8 +494,10 @@ def register():
day_nav_oob = await get_day_nav_oob(year, month, day)
post_nav_oob = await get_post_nav_oob(entry_id)
# redirect back to calendar admin or order page as you prefer
html = await render_template("_types/entry/_optioned.html")
# Re-read entry to get updated state
await g.s.refresh(g.entry)
from sexp.sexp_components import render_entry_optioned
html = render_entry_optioned(g.entry, g.calendar, day, month, year)
return await make_response(html + day_nav_oob + post_nav_oob, 200)
@bp.post("/tickets/")
@@ -546,7 +539,9 @@ def register():
await g.s.flush()
# Return just the tickets fragment (targeted by hx-target="#entry-tickets-...")
html = await render_template("_types/entry/_tickets.html")
await g.s.refresh(g.entry)
from sexp.sexp_components import render_entry_tickets_config
html = render_entry_tickets_config(g.entry, g.calendar, request.view_args.get("day"), request.view_args.get("month"), request.view_args.get("year"))
return await make_response(html, 200)
@bp.get("/posts/search/")
@@ -596,11 +591,10 @@ def register():
entry_posts = await get_entry_posts(g.s, entry_id)
# Return updated posts list + OOB nav update
html = await render_template("_types/entry/_posts.html")
nav_oob = await render_template(
"_types/entry/admin/_nav_posts_oob.html",
entry_posts=entry_posts,
)
from sexp.sexp_components import render_entry_posts_panel, render_entry_posts_nav_oob
va = request.view_args or {}
html = render_entry_posts_panel(entry_posts, g.entry, g.calendar, va.get("day"), va.get("month"), va.get("year"))
nav_oob = render_entry_posts_nav_oob(entry_posts)
return await make_response(html + nav_oob, 200)
@bp.delete("/posts/<int:post_id>/")
@@ -619,11 +613,10 @@ def register():
entry_posts = await get_entry_posts(g.s, entry_id)
# Return updated posts list + OOB nav update
html = await render_template("_types/entry/_posts.html")
nav_oob = await render_template(
"_types/entry/admin/_nav_posts_oob.html",
entry_posts=entry_posts,
)
from sexp.sexp_components import render_entry_posts_panel, render_entry_posts_nav_oob
va = request.view_args or {}
html = render_entry_posts_panel(entry_posts, g.entry, g.calendar, va.get("day"), va.get("month"), va.get("year"))
nav_oob = render_entry_posts_nav_oob(entry_posts)
return await make_response(html + nav_oob, 200)
return bp