Metadata-Version: 2.4
Name: jinja2_fragments
Version: 1.12.0
Summary: Render Jinja2 template block as HTML page fragments on Python web frameworks.
Author-email: Sergi Pons Freixes <sergi@cub3.net>
License-Expression: MIT
Project-URL: Homepage, https://jinja2-fragments.readthedocs.io/
Project-URL: Source Code, https://github.com/sponsfreixes/jinja2-fragments
Project-URL: Issue Tracker, https://github.com/sponsfreixes/jinja2-fragments/issues
Project-URL: Changes, https://github.com/sponsfreixes/jinja2-fragments/blob/main/CHANGELOG.md
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Framework :: Flask
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Text Processing :: Markup :: HTML
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: jinja2<4.0.0,>=3.1.0
Dynamic: license-file

# Jinja2 fragments

Jinja2 Fragments allows rendering individual blocks from
[Jinja2 templates](https://palletsprojects.com/p/jinja/). This library was created
to enable the pattern of
[Template Fragments](https://htmx.org/essays/template-fragments/) with Jinja2. It's a
great pattern if you are using [HTMX](https://htmx.org/) or some other library that
leverages fetching partial HTML.

With jinja2, if you have a template block that you want to render by itself and
as part of another page, you are forced to put that block on a separate file and then
use the [include tag](https://jinja.palletsprojects.com/en/3.1.x/templates/#include)
(or [Jinja Partials](https://github.com/mikeckennedy/jinja_partials)) on the wrapping
template.

With Jinja2 Fragments, following the
[Locality of Behavior](https://htmx.org/essays/locality-of-behaviour/) design principle,
you have a single file for both cases. See below for examples.

## Documentation

The detailed documentation is available at [Read the Docs](https://jinja2-fragments.readthedocs.io/). The rest of this
README is a summarized version of it.

## Install

It's just `pip install jinja2-fragments` and you're all set. It's a pure Python package
that only needs `jinja2` (for obvious reasons!).

## Usage

This is an example of how to use the library with vanilla Jinja2. Given the template `page.html.jinja2`:

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>This is the title</title>
</head>
<body>
    <h1>This is a header</h1>
    {% block content %}
    <p>This is the magic number: {{ magic_number }}.</p>
    {% endblock %}
</body>
</html>
```

If you want to render only the `content` block, do:

```python
from jinja2 import Environment, FileSystemLoader, select_autoescape
from jinja2_fragments import render_block

environment = Environment(
    loader=FileSystemLoader("my_templates"),
    autoescape=select_autoescape(("html", "jinja2")),
)
rendered_html = render_block(
    environment, "page.html.jinja2", "content", magic_number=42
)
```

And this will only render:
```html
<p>This is the magic number: 42.</p>
```

### Rendering multiple blocks

With the variant `render_blocks` (notice the plural) it is also possible to render 
multiple blocks from the same template and concatenate them all to return them in a 
single response. This enables easier 
[out-of-band updates](https://htmx.org/attributes/hx-swap-oob/) when using HTMX.

## Usage with Flask

If you want to use Jinja2 Fragments with Flask, assuming the same template as the
example above, do:

```python
from flask import Flask, render_template
from jinja2_fragments.flask import render_block

app = Flask(__name__)


@app.get("/full_page")
def full_page():
    return render_template("page.html.jinja2", magic_number=42)


@app.get("/only_content")
def only_content():
    return render_block("page.html.jinja2", "content", magic_number=42)
```

## Usage with Quart

If you want to use Jinja2 Fragments with Quart, assuming the same template as the
example above, do:

```python
from quart import Quart, render_template
from jinja2_fragments.quart import render_block

app = Quart(__name__)


@app.get("/full_page")
async def full_page():
    return await render_template("page.html.jinja2", magic_number=42)


@app.get("/only_content")
async def only_content():
    return await render_block("page.html.jinja2", "content", magic_number=42)
```
## Usage with Starlette

You can use Jinja2 Fragments with Starlette through the `Jinja2Blocks` class, which extends Starlette's `Jinja2Templates`.

Assuming the same template as the examples above:

```py
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.routing import Route
from jinja2_fragments.starlette import Jinja2Blocks

templates = Jinja2Blocks(directory="path/to/templates")


async def full_page(request: Request):
    return templates.TemplateResponse(request, "page.html.jinja2", {"magic_number": 42})


async def only_content(request: Request):
    return templates.TemplateResponse(
        request, "page.html.jinja2", {"magic_number": 42}, block_name="content"
    )


routes = [
    Route("/full_page", full_page),
    Route("/only_content", only_content),
]

app = Starlette(routes=routes)
```

## Usage with FastAPI

You can also use Jinja2 Fragments with FastAPI. In this case, Jinja2 Fragments has a wrapper around the FastAPI `Jinja2Templates` object called `Jinja2Blocks`.

It functions exactly the same, but allows you to include an optional parameter to the `TemplateResponse` that includes the `block_name` you want to render.

Assuming the same template as the examples above:

```py
from fastapi import FastAPI
from fastapi.requests import Request
from jinja2_fragments.fastapi import Jinja2Blocks

app = FastAPI()

templates = Jinja2Blocks(directory="path/to/templates")


@app.get("/full_page")
async def full_page(request: Request):
    return templates.TemplateResponse(request, "page.html.jinja2", {"magic_number": 42})


@app.get("/only_content")
async def only_content(request: Request):
    return templates.TemplateResponse(
        request, "page.html.jinja2", {"magic_number": 42}, block_name="content"
    )
```

## Usage with Sanic
You can use jinja2-fragments's `render()` with Sanic as a drop-in replacement of the Sanic template extension's `render()`. Your request context and environment configuration will work the same as before. You must have `sanic_ext` and `Jinja2` installed.

By default, the full page is rendered (`block=None`) unless you provide a `block` keyword argument.

```py
from sanic import Sanic, Request
import sanic_ext
from jinja2_fragments.sanic import render

app = Sanic(__name__)
app.extend(config=sanic_ext.Config(templating_path_to_templates="path/to/templates"))


@app.get("/full_page")
async def full_page(request: Request):
    return await render("page.html.jinja2", context={"magic_number": 42})


@app.get("/only_content")
async def only_content(request: Request):
    return await render(
        "page.html.jinja2", block="content", context={"magic_number": 42}
    )
```

## Usage with Litestar
You can use Jinja2 Fragments with Litestar by using the `HTMXBlockTemplate` class. This gives you access to the `block_name` parameter when rendering the template.

By default, the full page is rendered unless you provide a `block_name` keyword argument.

> **Note**: `HTMXBlockTemplate` can be used as a drop-in replacement for Litestar's `Template` class. However, passing multiple positional arguments to `HTMXBlockTemplate` is deprecated and will be removed in a future version. Use `template_name` as the only positional argument and pass all other parameters as keyword arguments.

```py
try:
    # litestar>=2.13.0
    from litestar.plugins.htmx import HTMXRequest
except ImportError:
    # litestar<2.13.0
    from litestar.contrib.htmx.request import HTMXRequest

from litestar import get, Litestar
from litestar.response import Template

from litestar.contrib.jinja import JinjaTemplateEngine
from litestar.template.config import TemplateConfig
from jinja2_fragments.litestar import HTMXBlockTemplate


@get("/full_page")
def full_page(request: HTMXRequest) -> Template:
    return HTMXBlockTemplate(
        template_name="page.html.jinja2", context={"magic_number": 42}
    )


@get("/only_content")
def only_content(request: HTMXRequest) -> Template:
    return HTMXBlockTemplate(
        template_name="page.html.jinja2",
        block_name="content",
        context={"magic_number": 42},
    )


app = Litestar(
    route_handlers=[full_page, only_content],
    request_class=HTMXRequest,
    template_config=TemplateConfig(
        directory="path/to/templates",
        engine=JinjaTemplateEngine,
    ),
)
```


## How to collaborate

This project uses pre-commit hooks to run Ruff and format Python code examples in documentation with `blacken-docs` on each commit.
To have that running automatically on your environment, install the project with:

```shell
pip install -e .[dev]
```

And then run once:

```shell
pre-commit install
```

From now on, every time you commit your files on this project, they will be automatically processed by Ruff and `blacken-docs`.

## How to run tests

You can install pytest and other required dependencies with:

```shell
pip install -e .[tests]
```

And then run the test suite with:

```shell
pytest
```

