Templating
Nexios provides a powerful templating system built on top of Jinja2, offering features like template inheritance, context management, custom filters, and more.
Prerequisites
Before using templating features, you need to install the required dependencies:
pip install nexios[templating]
Quick Start
from nexios import NexiosApp
from nexios.templating import render,TemplateEngine
app = NexiosApp()
engine = TemplateEngine()
engine.setup_environment()
@app.get("/")
async def home(request, response):
return await render("home.html", {"title": "Welcome"}, request=request)
Tip
Without setting up the templating engine , the render function throws a Notimpemented error
Customizing Default Configuration
There are several ways to customize the templating system:
1. Using setup_environment
The simplest way is to set template options in your app configuration:
from nexios.templating import TemplateConfig
template_config = TemplateConfig(
template_dir = "templates"
)
engine.setup_environment(template_config)
2. Using App Config
You can also nexios app config optionally
from nexios import NexiosApp,MakeConfig
from nexios.templating import TemplateConfig
config = MakeConfig(
{
"templating" : TemplateConfig(
template_dir = "templates"
)
}
)
app = NexiosApp(config = config)
3. Runtime Configuration Updates
You can update configuration at runtime:
from nexios.templating import TemplateEngine
from pathlib import Path
engine = TemplateEngine()
engine.setup_environment()
engine.env.filters["custom"] = my_custom_filter
# Add new globals
engine.env.globals.update({
"api_version": "2.0",
"debug": True
})
engine.config.template_dir = Path("new_templates")
engine._setup_environment()
Configuration Options
The TemplateConfig
class supports the following options:
Option | Type | Default | Description |
---|---|---|---|
template_dir | str/Path | "templates" | Template directory path |
cache_size | int | 100 | Maximum templates to cache |
auto_reload | bool | True | Reload changed templates |
encoding | str | "utf-8" | Template file encoding |
enable_async | bool | True | Enable async rendering |
trim_blocks | bool | True | Strip first newline after block |
lstrip_blocks | bool | True | Strip leading spaces and tabs |
custom_filters | Dict[str, callable] | {} | Custom template filters |
custom_globals | Dict[str, Any] | {} | Global template variables |
Template Inheritance
Base template (base.html
):
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<nav>{% block nav %}{% endblock %}</nav>
<main>{% block content %}{% endblock %}</main>
<footer>{% block footer %}{% endblock %}</footer>
</body>
</html>
Child template:
{% extends "base.html" %} {% block title %}Welcome{% endblock %} {% block
content %}
<h1>{{ title }}</h1>
{{ content }} {% endblock %}
Context Middleware
Add global and request-specific context to your templates:
from nexios.templating.middleware import template_context
async def user_context(request):
return {
"user": await get_current_user(request),
"messages": await get_flash_messages(request)
}
app.add_middleware(template_context(
default_context={
"version": "1.0.0",
"nav_links": [...]
},
context_processor=user_context
))
Utility Functions
The templating system includes several utility functions:
from nexios.templating.utils import (
truncate,
format_datetime,
static_hash,
merge_dicts
)
# Truncate text
{{ long_text|truncate(100) }}
# Format dates
{{ date|format_datetime("%Y-%m-%d") }}
# Cache busting for static files
{{ static_url('style.css') }}?v={{ static_hash('static/style.css') }}
Best Practices
Template Organization
- Keep templates in a dedicated directory
- Use meaningful names and subdirectories
- Follow a consistent naming convention
Context Management
- Use middleware for global context
- Keep context processors focused and lightweight
- Cache expensive context operations
Performance
- Enable template caching in production
- Use async rendering for I/O operations
- Minimize template complexity
Security
- HTML escaping is enabled by default
- Use
|safe
filter carefully - Validate user input before rendering
API Reference
Render Function
async def render(
template_name: str,
context: Dict[str, Any] = None,
status_code: int = 200,
headers: Dict[str, str] = None,
request: Request = None,
**kwargs
) -> Response
Template Context Middleware
def template_context(
default_context: Optional[Dict[str, Any]] = None,
context_processor: Optional[
Callable[[Request], Awaitable[Dict[str, Any]]]
] = None
) -> TemplateContextMiddleware
Utility Functions
def truncate(text: str, length: int = 100, suffix: str = "...") -> str
def format_datetime(value: datetime, fmt: str = "%Y-%m-%d %H:%M:%S") -> str
def static_hash(filepath: str) -> str
def merge_dicts(*dicts: Dict[str, Any]) -> Dict[str, Any]
Advanced Templating
This guide covers advanced features and patterns for the Nexios templating system.
Custom Filters
Create and register custom template filters:
from nexios.templating import TemplateConfig
def markdown_to_html(text: str) -> str:
import markdown
return markdown.markdown(text)
def currency(value: float, symbol: str = "$") -> str:
return f"{symbol}{value:,.2f}"
config = TemplateConfig(
custom_filters={
"markdown": markdown_to_html,
"currency": currency
}
)
Usage in templates:
{{ post.content|markdown }} {{ product.price|currency("€") }}
Macros
Create reusable template components:
{# macros/forms.html #} {% macro input(name, value='', type='text', label='') %}
<div class="form-group">
{% if label %}
<label for="{{ name }}">{{ label }}</label>
{% endif %}
<input
type="{{ type }}"
name="{{ name }}"
value="{{ value }}"
id="{{ name }}"
/>
</div>
{% endmacro %} {# Usage in templates #} {% from "macros/forms.html" import input
%}
<form method="post">
{{ input('username', label='Username') }} {{ input('password',
type='password', label='Password') }}
</form>
Async Template Functions
Create async template functions for database queries or API calls:
from nexios.templating import TemplateConfig
from functools import partial
async def get_user_posts(user_id: int):
# Async database query
return await db.query("SELECT * FROM posts WHERE user_id = $1", user_id)
config = TemplateConfig(
custom_globals={
"get_posts": get_user_posts
}
)
Usage in templates:
{% set posts = await get_posts(user.id) %} {% for post in posts %}
<article>{{ post.title }}</article>
{% endfor %}
Context Processors
Advanced context processor patterns:
from typing import Dict, Any
from nexios.templating.middleware import template_context
from nexios.cache import cached
class ContextBuilder:
def __init__(self):
self.processors = []
def add(self, processor):
self.processors.append(processor)
return self
async def build(self, request) -> Dict[str, Any]:
context = {}
for proc in self.processors:
context.update(await proc(request))
return context
@cached(ttl=300) # Cache for 5 minutes
async def get_site_stats(request):
return {
"total_users": await db.count("users"),
"total_posts": await db.count("posts")
}
async def get_user_data(request):
if user := await get_current_user(request):
return {
"user": user,
"notifications": await get_user_notifications(user.id)
}
return {}
# Combine processors
context_builder = (
ContextBuilder()
.add(get_site_stats)
.add(get_user_data)
)
app.add_middleware(template_context(
context_processor=context_builder.build
))
Template Caching
Implement template fragment caching:
from nexios.cache import Cache
from nexios.templating import TemplateConfig
cache = Cache()
async def cached_fragment(key: str, ttl: int = 300):
"""Template fragment cache."""
if content := await cache.get(key):
return content
return None
config = TemplateConfig(
custom_globals={
"cached_fragment": cached_fragment,
"cache": cache
}
)
Usage in templates:
{% set cache_key = 'sidebar_' + user.id %} {% set cached = await
cached_fragment(cache_key) %} {% if cached %} {{ cached }} {% else %} {% set
content %} {# Expensive sidebar rendering #}
<aside>...</aside>
{% endset %} {{ cache.set(cache_key, content, ttl=300) }} {{ content }} {% endif
%}
Error Handling
Custom error templates and handling:
from nexios.templating import render
from nexios.exceptions import TemplateError
@app.exception_handler(404)
async def not_found(request, exc):
return await render(
"errors/404.html",
{"path": request.url.path},
status_code=404
)
@app.exception_handler(TemplateError)
async def template_error(request, exc):
return await render(
"errors/template.html",
{
"error": str(exc),
"template": exc.template_name,
"lineno": exc.lineno
},
status_code=500
)
Testing Templates
Write tests for your templates:
import pytest
from nexios.templating import TemplateEngine, TemplateConfig
from nexios.testing import TestClient
@pytest.fixture
def template_engine():
config = TemplateConfig(template_dir="tests/templates")
return TemplateEngine(config)
async def test_template_rendering(template_engine):
content = await template_engine.render(
"welcome.html",
{"name": "User"}
)
assert "Welcome, User!" in content
async def test_template_filters(template_engine):
content = await template_engine.render(
"product.html",
{"price": 99.99}
)
assert "€99.99" in content
async def test_context_middleware(client: TestClient):
response = await client.get("/")
assert response.status_code == 200
assert "Welcome, Test User!" in response.text