{"id":49000025,"url":"https://github.com/tbrlpld/django-blockinclude","last_synced_at":"2026-04-18T18:04:41.624Z","repository":{"id":343111888,"uuid":"1171305223","full_name":"tbrlpld/django-blockinclude","owner":"tbrlpld","description":"An extension of Django's `include` tag to allow sections of markup to be passed to the included template.","archived":false,"fork":false,"pushed_at":"2026-04-01T22:39:47.000Z","size":248,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-02T07:34:19.673Z","etag":null,"topics":["blocks","components","django","slots","tailwindcss","templates"],"latest_commit_sha":null,"homepage":"https://github.com/tbrlpld/django-blockinclude","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tbrlpld.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-03T04:44:10.000Z","updated_at":"2026-04-01T22:14:06.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tbrlpld/django-blockinclude","commit_stats":null,"previous_names":["tbrlpld/django-blockinclude"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tbrlpld/django-blockinclude","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tbrlpld%2Fdjango-blockinclude","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tbrlpld%2Fdjango-blockinclude/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tbrlpld%2Fdjango-blockinclude/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tbrlpld%2Fdjango-blockinclude/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tbrlpld","download_url":"https://codeload.github.com/tbrlpld/django-blockinclude/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tbrlpld%2Fdjango-blockinclude/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31978810,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T17:30:12.329Z","status":"ssl_error","status_checked_at":"2026-04-18T17:29:59.069Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blocks","components","django","slots","tailwindcss","templates"],"created_at":"2026-04-18T18:04:35.177Z","updated_at":"2026-04-18T18:04:41.614Z","avatar_url":"https://github.com/tbrlpld.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Django Block Include 🧱\n\n[![License: BSD-3-Clause](https://img.shields.io/github/license/tbrlpld/django-blockinclude)](https://github.com/tbrlpld/django-blockinclude/blob/main/LICENSE)\n[![PyPI version](https://img.shields.io/pypi/v/django-blockinclude)](https://pypi.org/project/django-blockinclude/)\n[![Block Include CI](https://github.com/tbrlpld/django-blockinclude/actions/workflows/test.yml/badge.svg)](https://github.com/tbrlpld/django-blockinclude/actions/workflows/test.yml)\n[![codecov](https://codecov.io/gh/tbrlpld/django-blockinclude/graph/badge.svg?token=HR5IGX3XTL)](https://codecov.io/gh/tbrlpld/django-blockinclude)\n[![Published on Django Packages](https://img.shields.io/badge/Published%20on-Django%20Packages-0c3c26)](https://djangopackages.org/packages/p/django-blockinclude)\n\n***\n\n**An extension of Django's `include` tag to pass sections of markup to the included template.**\n\n***\n\n## Getting started\n\n### Installation\n\nFirst, install with pip:\n```sh\n$ python -m pip install django-blockinclude\n```\n\nThen, add to your installed apps:\n\n```python\n# settings.py\n\nINSTALLED_APPS = [..., \"blockinclude\", ...]\n```\n\n### Basic usage\n\nThis is an extension of Django's default `include` tag and supports all of its features.\nAdditionally, it allows you to pass a section of rendered markup to the included template.\n\nLet's presume we have the following template `my-box.html`:\n\n```django\n{# my-box.html #}\n\n\u003cdiv class=\"p-12 border-2 border-black\"\u003e\n    {{ content }}\n\u003c/div\u003e\n```\n\nNow we want to fill the box with some content.\nTo 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](https://docs.djangoproject.com/en/stable/ref/templates/builtins/#include).\nUnlike the `include` tag, `blockinclude` is a block tag and comes with the `endblockinclude` end tag.\nThis allows you to pass a section of content from the parent template (`my-page.html`) to the included template (`my-box.html`), like so:\n\n```django\n{# my-page.html #}\n\n{% load blockinclude %}\n\n{% blockinclude \"my-box.html\" %}\n    The body content of the box.\n{% endblockinclude %}\n```\n\nIn 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.\nThus, the result of rendering `my-page.html` will be:\n\n```html\n\u003cdiv class=\"p-12 border-2 border-black\"\u003e\n    The body content of the box.\n\u003c/div\u003e\n```\n\nOf course, passing a single line of text is not very interesting.\nThat would also be possible with a [keyword argument to the default `include` tag](https://docs.djangoproject.com/en/6.0/ref/templates/builtins/#:~:text=You%20can%20pass%20additional%20context%20to%20the%20template%20using%20keyword%20arguments).\n\nBut, `blockinclude` is not limited to simple strings.\nYou can wrap any HTML markup or even template logic between the opening and closing tags.\n\nThe 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.\n\nLet's change `my-page.html` to the following:\n\n```django\n{# my-page.html #}\n\n{% load blockinclude %}\n\n{% blockinclude \"my-box.html\" %}\n    \u003cul\u003e\n        {% for item in items %}\n            \u003cli\u003e{{ item }}\u003c/li\u003e\n        {% endfor %}\n    \u003c/ul\u003e\n{% endblockinclude %}\n```\n\nNow, if `my-page.html` is rendered with `items = [\"Apple\", \"Banana\"]` in the context, then the rendered result will be:\n\n```html\n\u003cdiv class=\"p-12 border-2 border-black\"\u003e\n    \u003cul\u003e\n        \u003cli\u003eApple\u003c/li\u003e\n        \u003cli\u003eBanana\u003c/li\u003e\n    \u003c/ul\u003e\n\u003c/div\u003e\n```\n\n### Passing multiple sections with `slot`\n\nIf 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`.\n\nLet's assume we have the following `my-slotted-box.html` template.\nThis template does not only use the `content` variable, but also the `header` variable.\n\n```django\n{# my-slotted-box.html #}\n\n\u003cdiv class=\"p-12 border-2 border-black\"\u003e\n    {% if header %}\n        \u003cheader\u003e\n            {{ header }}\n        \u003c/header\u003e\n    {% endif %}\n\n    \u003cdiv\u003e\n        {{ content }}\n    \u003c/div\u003e\n\u003c/div\u003e\n```\n\nNow, if we want to fill both of these variables with multiple lines of markup, we can include the template like so:\n\n```django\n{# my-page.html #}\n\n{% load blockinclude %}\n\n{% blockinclude \"my-slotted-box.html\" %}\n    {% slot \"header\" %}\n        Header of the box\n    {% endslot %}\n\n    The body content of the box.\n{% endblockinclude %}\n```\n\nThe result of rendering `my-page.html` will be:\n\n```html\n\u003cdiv class=\"p-12 border-2 border-black\"\u003e\n    \u003cheader\u003e\n        Header of the box\n    \u003c/header\u003e\n\n    \u003cdiv\u003e\n        The body content of the box.\n    \u003c/div\u003e\n\u003c/div\u003e\n```\n\nYou can use as many `slot` tags inside a `blockinclude` as you like.\n\n```django\n{# my-slotted-box.html #}\n\n\u003cdiv class=\"p-12 border-2 border-black\"\u003e\n    {% if header %}\n        \u003cheader\u003e\n            {{ header }}\n        \u003c/header\u003e\n    {% endif %}\n\n    \u003cdiv\u003e\n        {{ content }}\n    \u003c/div\u003e\n\n    {% if footer %}\n        \u003cfooter\u003e\n            {{ footer }}\n        \u003c/footer\u003e\n    {% endif %}\n\u003c/div\u003e\n```\n\n```django\n{# my-page.html #}\n\n{% load blockinclude %}\n\n{% blockinclude \"my-slotted-box.html\" %}\n    {% slot \"header\" %}\n        Header of the box\n    {% endslot %}\n\n    The body content of the box.\n\n    {% slot \"footer\" %}\n        Footer of the box\n    {% endslot %}\n{% endblockinclude %}\n```\n\nJust like the `blockinclude` itself, the content of the `slot` tag can be markup or even template logic.\nTemplate logic in a `slot` is executed with the context of the parent template, just like logic inside the `blockinclude`.\n\n### Additional notes about tag usage\n\n#### `blockinclude`\n\n* `blockinclude` supports all features of the [`include` tag](https://docs.djangoproject.com/en/stable/ref/templates/builtins/#include).\n* By default, the included template receives the whole parent context.\n* You can pass extra context via [keyword arguments](https://docs.djangoproject.com/en/6.0/ref/templates/builtins/#:~:text=You%20can%20pass%20additional%20context%20to%20the%20template%20using%20keyword%20arguments), `{% blockinclude ... with extra=\"My value\" %}`.\n* You can prevent passing of the parent context and limit the passed context to only the keyword arguments and the tag content, by appending `only` to the opening tag, `{% blockinclude ... with extra=\"My value\" only %}`.\n* If you use `content=...` as a keyword argument, this will be overridden by the content of `blockinclude` tag.\n\n#### `slot`\n\n* The name of the slot needs to be quoted. `{% slot \"header\" %}` is ok, while `{% slot header %}` is not.\n* You can not use `\"content\"` as a slot name, because that name is reserved for the content of the `blockinclude`. `{% slot \"content\" %}` is not ok.\n* The definition order of the `slot` tags in the parent template does not matter.\n* If you reuse the same slot name, then the latter definition overrides a prior one (in source order).\n* All content in the `blockinclude` outside of `slot` tags is merged into the `content` variable.\n* The `slot` has to be a direct child of the `blockinclude` and can not be nested in other template block tags (`if` or `for`) inside the `blockinclude`.\n  The `blockinclude` itself can be nested inside of other template tag blocks just fine.\n* 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.\n* When you use `only` on the `blockinclude`, the slot contents are still passed.\n\n## About Django Block Include\n\n### Supported versions\n\n- Python \u003e= 3.10\n- Django \u003e= 4.2\n\n## Contributing\n\n### Install\n\nTo make changes to this project, first clone this repository:\n\n```sh\n$ git clone https://github.com/tbrlpld/django-blockinclude.git\n$ cd django-blockinclude\n```\n\nWith your preferred virtualenv activated, install the development dependencies:\n\n#### Using pip\n\n```sh\n$ python -m pip install --upgrade pip\u003e=21.3\n$ python -m pip install -e '.[dev]' -U\n```\n\n#### Using flit\n\n```sh\n$ python -m pip install flit\n$ flit install\n```\n\n### pre-commit\n\nNote that this project uses [pre-commit](https://github.com/pre-commit/pre-commit).\nIt is included in the project testing requirements. To set up locally:\n\n```shell\n# initialize pre-commit\n$ pre-commit install\n\n# Optional, run all checks once for this, then the checks will run only on the changed files\n$ git ls-files --others --cached --exclude-standard | xargs pre-commit run --files\n```\n\n### How to run tests\n\nNow you can run all tests like so:\n\n```sh\n$ tox\n```\n\nOr, you can run them for a specific environment:\n\n```sh\n$ tox -e python3.13-django5.2\n```\n\nOr, run only a specific test:\n\n```sh\n$ tox -e python3.13-django5.2 blockinclude.tests.test_file.TestClass.test_method\n```\n\nTo run the test app interactively, use:\n\n```sh\n$ tox -e interactive\n```\n\nYou can now visit `http://localhost:8020/`.\n\n#### Testing with coverage\n\n`tox` is configured to run tests with coverage.\nThe coverage report is combined for all environments.\nThis is done by using the `--append` flag when running coverage in `tox`.\nThis means it will also include previous results.\n\nYou can see the coverage report by running:\n\n```sh\n$ coverage report\n```\n\nTo get a clean report, you can run `coverage erase` before running `tox`.\n\n#### Running tests without `tox`\n\nIf you want to run tests without `tox`, you can use the `testmanage.py` script.\nThis script is a wrapper around Django's `manage.py` and will run tests with the correct settings.\n\nTo make this work, you need to have the `testing` dependencies installed.\n\n```sh\n$ python -m pip install -e '.[testing]' -U\n```\n\nThen you can run tests with:\n\n```sh\n$ ./testmanage.py test\n````\n\nTo run tests with coverage, use:\n\n```sh\n$ coverage run ./testmanage.py test\n```\n\n#### Running the example app\n\nSometimes you may want to confirm the rendering in the browser with your own eyes instead of test assertions.\n\nYou can run the example app with:\n\n```sh\n$ ./testmanage.py runserver 8000\n```\n\nNow you can visit the app in the browser at `http://localhost:8000/`.\n\n### Python version management\n\nTox will attempt to find installed Python versions on your machine.\n\nIf you use `pyenv` to manage multiple versions, you can tell `tox` to use those versions.\nTo ensure that `tox` will find Python versions installed with `pyenv` you need [`virtualenv-pyenv`](https://pypi.org/project/virtualenv-pyenv/) (note: this is not `pyenv-virtualenv`).\n`virtualenv-pyenv` is part of the development dependencies (just like `tox` itself).\nAdditionally, you have to set the environment variable `VIRTUALENV_DISCOVERY=pyenv`.\n\n### Publishing\n\nThis project uses the [Trusted Publisher model for PyPI releases](https://docs.pypi.org/trusted-publishers/).\nThis means that publishing is done through GitHub Actions when a [new release is created on GitHub](https://github.com/tbrlpld/django-blockinclude/releases/new).\n\nBefore publishing a new release, make sure to update\n\n- [ ] the changelog in `CHANGELOG.md`, and\n- [ ] the version number in `blockinclude/__init__.py`.\n\nTo update these files, you will have to create a release-prep branch and PR.\nOnce that PR is merged into `main` you are ready to create the release.\n\nTo manually test publishing the package, you can use `flit`.\nBe sure to configure the `testpypi` repository in your `~/.pypirc` file according to the Flit [documentation](https://flit.pypa.io/en/stable/upload.html#controlling-package-uploads).\nIf your PyPI account is using 2FA, you'll need to create a [PyPI API token](https://test.pypi.org/help/#apitoken) and use that as your password and `__token__` as the username.\n\nWhen you're ready to test the publishing, run:\n\n```shell\n$ flit build\n$ flit publish --repository testpypi\n```\n\nOnce you are ready to actually release the new version, you need to first create a git tag.\nThe tag name should be the version number prefixed with a `v` (e.g. `v0.1.0`).\n\nTo create the tag on the command line:\n\n```sh\n$ git switch main\n$ git pull\n$ git tag v0.1.1\n$ git push --tags\n```\n\nOnce the tag is on GitHub, you can visit the [Tags screen](https://github.com/tbrlpld/django-blockinclude/tags).\nThere you click \"create release\" in the overflow menu of the tag that you have just created.\nOn the release screen you can click \"generate release notes\", which will compile release notes based on the merged PRs since the last release.\nEdit the generated release notes to make them a bit more concise (e.g. remove small fix-up PRs or group related changes).\n\nOnce the release notes are ready, click \"publish release\".\nThis will trigger the release workflow, which you can observe on the [\"Actions\" tab](https://github.com/tbrlpld/django-blockinclude/actions).\nWhen the workflow completes, check the new release on [PyPI](https://pypi.org/project/blockinclude/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftbrlpld%2Fdjango-blockinclude","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftbrlpld%2Fdjango-blockinclude","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftbrlpld%2Fdjango-blockinclude/lists"}