An extension of Django's `include` tag to pass sections of markup to the included template.
Project description
Django Block Include 🧱
An extension of Django's include tag to pass sections of markup to the included template.
Getting started
Installation
First, install with pip:
$ python -m pip install django-blockinclude
Then, add to your installed apps:
# settings.py
INSTALLED_APPS = [..., "blockinclude", ...]
Basic usage
This is an extension of Django's default include tag and supports all of its features.
Additionally, it allows you to pass a section of rendered markup to the included template.
Let's presume we have the following template my-box.html:
{# my-box.html #}
<div class="p-12 border-2 border-black">
{{ content }}
</div>
Now we want to fill the box with some content.
To do that, you use the blockinclude tag and pass it the path to my-box.html, just like you would with the default include tag.
Unlike the include tag, blockinclude is a block tag and comes with the endblockinclude end tag.
This allows you to pass a section of content from the parent template (my-page.html) to the included template (my-box.html), like so:
{# my-page.html #}
{% load blockinclude %}
{% blockinclude "my-box.html" %}
The body content of the box.
{% endblockinclude %}
In the above example, the my-box.html template will have a content variable with the value "The body content of the box." in the context.
Thus, the result of rendering my-page.html will be:
<div class="p-12 border-2 border-black">
The body content of the box.
</div>
Of course, passing a single line of text is not very interesting.
That would also be possible with a keyword argument to the default include tag.
But, blockinclude is not limited to simple strings.
You can wrap any HTML markup or even template logic between the opening and closing tags.
The template logic is evaluated in the context of the parent (my-page.html) and then passed to the included template (my-box.html) as the content variable.
Let's change my-page.html to the following:
{# my-page.html #}
{% load blockinclude %}
{% blockinclude "my-box.html" %}
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endblockinclude %}
Now, if my-page.html is rendered with items = ["Apple", "Banana"] in the context, then the rendered result will be:
<div class="p-12 border-2 border-black">
<ul>
<li>Apple</li>
<li>Banana</li>
</ul>
</div>
Passing multiple sections with slot
If you wish to pass more than one section of markup with different names to the included template, you can add slot tags inside the blockinclude.
Let's assume we have the following my-slotted-box.html template.
This template does not only use the content variable, but also the header variable.
{# my-slotted-box.html #}
<div class="p-12 border-2 border-black">
{% if header %}
<header>
{{ header }}
</header>
{% endif %}
<div>
{{ content }}
</div>
</div>
Now, if we want to fill both of these variables with multiple lines of markup, we can include the template like so:
{# my-page.html #}
{% load blockinclude %}
{% blockinclude "my-slotted-box.html" %}
{% slot "header" %}
Header of the box
{% endslot %}
The body content of the box.
{% endblockinclude %}
The result of rendering my-page.html will be:
<div class="p-12 border-2 border-black">
<header>
Header of the box
</header>
<div>
The body content of the box.
</div>
</div>
You can use as many slot tags inside a blockinclude as you like.
{# my-slotted-box.html #}
<div class="p-12 border-2 border-black">
{% if header %}
<header>
{{ header }}
</header>
{% endif %}
<div>
{{ content }}
</div>
{% if footer %}
<footer>
{{ footer }}
</footer>
{% endif %}
</div>
{# my-page.html #}
{% load blockinclude %}
{% blockinclude "my-slotted-box.html" %}
{% slot "header" %}
Header of the box
{% endslot %}
The body content of the box.
{% slot "footer" %}
Footer of the box
{% endslot %}
{% endblockinclude %}
Just like the blockinclude itself, the content of the slot tag can be markup or even template logic.
Template logic in a slot is executed with the context of the parent template, just like logic inside the blockinclude.
Additional notes about tag usage
blockinclude
blockincludesupports all features of theincludetag.- By default, the included template receives the whole parent context.
- You can pass extra context via keyword arguments,
{% blockinclude ... with extra="My value" %}. - You can prevent passing of the parent context and limit the passed context to only the keyword arguments and the tag content, by appending
onlyto the opening tag,{% blockinclude ... with extra="My value" only %}. - If you use
content=...as a keyword argument, this will be overridden by the content ofblockincludetag.
slot
- The name of the slot needs to be quoted.
{% slot "header" %}is ok, while{% slot header %}is not. - You can not use
"content"as a slot name, because that name is reserved for the content of theblockinclude.{% slot "content" %}is not ok. - The definition order of the
slottags in the parent template does not matter. - If you reuse the same slot name, then the latter definition overrides a prior one (in source order).
- All content in the
blockincludeoutside ofslottags is merged into thecontentvariable. - The
slothas to be a direct child of theblockincludeand can not be nested in other template block tags (iforfor) inside theblockinclude. Theblockincludeitself can be nested inside of other template tag blocks just fine. - If you use the same name as a keyword argument and as a slot name, then the slot content overrides the value of the keyword argument.
- When you use
onlyon theblockinclude, the slot contents are still passed.
About Django Block Include
Supported versions
- Python >= 3.10
- Django >= 4.2
Contributing
Install
To make changes to this project, first clone this repository:
$ git clone https://github.com/tbrlpld/django-blockinclude.git
$ cd django-blockinclude
With your preferred virtualenv activated, install the development dependencies:
Using pip
$ python -m pip install --upgrade pip>=21.3
$ python -m pip install -e '.[dev]' -U
Using flit
$ python -m pip install flit
$ flit install
pre-commit
Note that this project uses pre-commit. It is included in the project testing requirements. To set up locally:
# initialize pre-commit
$ pre-commit install
# Optional, run all checks once for this, then the checks will run only on the changed files
$ git ls-files --others --cached --exclude-standard | xargs pre-commit run --files
How to run tests
Now you can run all tests like so:
$ tox
Or, you can run them for a specific environment:
$ tox -e python3.13-django5.2
Or, run only a specific test:
$ tox -e python3.13-django5.2 blockinclude.tests.test_file.TestClass.test_method
To run the test app interactively, use:
$ tox -e interactive
You can now visit http://localhost:8020/.
Testing with coverage
tox is configured to run tests with coverage.
The coverage report is combined for all environments.
This is done by using the --append flag when running coverage in tox.
This means it will also include previous results.
You can see the coverage report by running:
$ coverage report
To get a clean report, you can run coverage erase before running tox.
Running tests without tox
If you want to run tests without tox, you can use the testmanage.py script.
This script is a wrapper around Django's manage.py and will run tests with the correct settings.
To make this work, you need to have the testing dependencies installed.
$ python -m pip install -e '.[testing]' -U
Then you can run tests with:
$ ./testmanage.py test
To run tests with coverage, use:
$ coverage run ./testmanage.py test
Running the example app
Sometimes you may want to confirm the rendering in the browser with your own eyes instead of test assertions.
You can run the example app with:
$ ./testmanage.py runserver 8000
Now you can visit the app in the browser at http://localhost:8000/.
Python version management
Tox will attempt to find installed Python versions on your machine.
If you use pyenv to manage multiple versions, you can tell tox to use those versions.
To ensure that tox will find Python versions installed with pyenv you need virtualenv-pyenv (note: this is not pyenv-virtualenv).
virtualenv-pyenv is part of the development dependencies (just like tox itself).
Additionally, you have to set the environment variable VIRTUALENV_DISCOVERY=pyenv.
Publishing
This project uses the Trusted Publisher model for PyPI releases. This means that publishing is done through GitHub Actions when a new release is created on GitHub.
Before publishing a new release, make sure to update
- the changelog in
CHANGELOG.md, and - the version number in
blockinclude/__init__.py.
To update these files, you will have to create a release-prep branch and PR.
Once that PR is merged into main you are ready to create the release.
To manually test publishing the package, you can use flit.
Be sure to configure the testpypi repository in your ~/.pypirc file according to the Flit documentation.
If your PyPI account is using 2FA, you'll need to create a PyPI API token and use that as your password and __token__ as the username.
When you're ready to test the publishing, run:
$ flit build
$ flit publish --repository testpypi
Once you are ready to actually release the new version, you need to first create a git tag.
The tag name should be the version number prefixed with a v (e.g. v0.1.0).
To create the tag on the command line:
$ git switch main
$ git pull
$ git tag v0.1.1
$ git push --tags
Once the tag is on GitHub, you can visit the Tags screen. There you click "create release" in the overflow menu of the tag that you have just created. On the release screen you can click "generate release notes", which will compile release notes based on the merged PRs since the last release. Edit the generated release notes to make them a bit more concise (e.g. remove small fix-up PRs or group related changes).
Once the release notes are ready, click "publish release". This will trigger the release workflow, which you can observe on the "Actions" tab. When the workflow completes, check the new release on PyPI.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_blockinclude-0.1.1.tar.gz.
File metadata
- Download URL: django_blockinclude-0.1.1.tar.gz
- Upload date:
- Size: 16.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
630d7f6f156223b328de3c273b77bb14fd787079f52c7130473f1fc0985df1cb
|
|
| MD5 |
5d27a0c851433ecd50ed62766b20a991
|
|
| BLAKE2b-256 |
504b7681db3247efe05cfa0f6450b17f8109df31b6e0975fa8a4d27b17e4e766
|
Provenance
The following attestation bundles were made for django_blockinclude-0.1.1.tar.gz:
Publisher:
publish.yml on tbrlpld/django-blockinclude
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_blockinclude-0.1.1.tar.gz -
Subject digest:
630d7f6f156223b328de3c273b77bb14fd787079f52c7130473f1fc0985df1cb - Sigstore transparency entry: 1208473213
- Sigstore integration time:
-
Permalink:
tbrlpld/django-blockinclude@277cd17b34b1c921412973d3fc7bcf9b0e699896 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/tbrlpld
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@277cd17b34b1c921412973d3fc7bcf9b0e699896 -
Trigger Event:
release
-
Statement type:
File details
Details for the file django_blockinclude-0.1.1-py3-none-any.whl.
File metadata
- Download URL: django_blockinclude-0.1.1-py3-none-any.whl
- Upload date:
- Size: 14.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
edb48270b8240c405acd309fe7db6c80d98435bfee48054710577679fea3d022
|
|
| MD5 |
5aa07a20c0bfd4fcf0d6c986f05dca4f
|
|
| BLAKE2b-256 |
9ca8cb26fe614f01f7c09fc47c7d55304446aed15b58635316cab93c8eeaa1d3
|
Provenance
The following attestation bundles were made for django_blockinclude-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on tbrlpld/django-blockinclude
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_blockinclude-0.1.1-py3-none-any.whl -
Subject digest:
edb48270b8240c405acd309fe7db6c80d98435bfee48054710577679fea3d022 - Sigstore transparency entry: 1208473280
- Sigstore integration time:
-
Permalink:
tbrlpld/django-blockinclude@277cd17b34b1c921412973d3fc7bcf9b0e699896 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/tbrlpld
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@277cd17b34b1c921412973d3fc7bcf9b0e699896 -
Trigger Event:
release
-
Statement type: