django-render-block-0.8.1/0000755000076500000240000000000013742120342015725 5ustar clokepstaff00000000000000django-render-block-0.8.1/CHANGELOG.rst0000644000076500000240000000650213742120210017743 0ustar clokepstaff00000000000000.. :changelog: Changelog ######### 0.8.1 (October 15, 2020) ======================== * Fixes a regression in v0.8 where a ``Context`` could not be re-used. See `#25 `_, contributed by @evanbrumley. 0.8 (October 6, 2020) ===================== * ``render_block_to_string`` now forwards the ``Context`` passed as ``context`` parameter. (`#21 `_, by @bblanchon) * Drop support for Python 3.5, officially support Python 3.9. 0.7 (July 13, 2020) =================== * Drop support for Django < 2.2. * Officially support Django 3.0, 3.1. * Drop support for Python 2.7. * Officially support Python 3.8. 0.6 (May 8, 2019) ================= * Supports Django 1.11, Django 2.1, and Django 2.2. * Supports Python 2.7, 3.5, 3.6, and 3.7. * ``render_block_to_string`` now optionally accepts a ``request`` parameter. If given, a ``RequestContext`` instead of a ``Context`` is used when rendering with the Django templating engine. See `#15 `_, thanks to @vintage. 0.5 (September 1, 2016) ======================= * Fixes a major issue with inheriting templates and rendering a block found in the parent template, but overwriting part of it in the child template. (`#8 `_) 0.4 (August 4, 2016) ==================== * Initial support for using the `Jinja2 `_ templating engine. See README for caveats. (`#3 `_) * Support Django 1.10. (`#5 `_) * Support Python 3. (`#6 `_) 0.3.1 (June 1, 2016) ==================== * Refactoring to make more generic (for potentially supporting multiple templating engines). 0.3 (May 27, 2016) ================== * Largely rewritten. * Updated to support modern Django (1.8, 1.9): * Guards against different template backends. * Uses internal APIs for each node. * Removed ``context_instance`` parameter. * Support for calling ``{{ block.super }}``. 0.2.2 (January 10, 2011) ======================== * Updated per `comment 3466 on Django Snippet 942 `_ to fix an issue with nested extends. The specific bug was not reproducible, but the additional code shouldn't hurt. 0.2.1 (August 27, 2010) ======================= * Updated per `comment 3237 on Django Snippet 942 `_ to remove a pointless render. The specific bug was not reproducible, but the code is extraneous. 0.2 (August 4, 2008) ==================== * Updated version from `Django Snippet 942 `_ by zbyte64. * Improves include: 1. Simpler/better handling of "extends" block tag 2. Searches If/Else blocks 3. Less code 4. Allow list of templates to be passed which is closer to the behavior of render_to_response 0.1 (May 22, 2008) ================== * Initial version from `Django Snippet 769 `_ by sciyoshi. * Supports Django 0.96. django-render-block-0.8.1/LICENSE0000644000076500000240000000133413616533551016745 0ustar clokepstaff00000000000000Copyright (c) 2016, Patrick Cloke Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. django-render-block-0.8.1/MANIFEST.in0000644000076500000240000000007113616533551017473 0ustar clokepstaff00000000000000include LICENSE include README.rst include CHANGELOG.rst django-render-block-0.8.1/PKG-INFO0000644000076500000240000002476613742120342017041 0ustar clokepstaff00000000000000Metadata-Version: 2.1 Name: django-render-block Version: 0.8.1 Summary: Render a particular block from a template to a string. Home-page: https://github.com/clokep/django-render-block Author: Patrick Cloke Author-email: clokep@patrick.cloke.us License: UNKNOWN Download-URL: https://github.com/clokep/django-render-block Description: Django Render Block ################### .. image:: https://travis-ci.org/clokep/django-render-block.svg?branch=master :target: https://travis-ci.org/clokep/django-render-block Render the content of a specific block tag from a Django template. Works for arbitrary template inheritance, even if a block is defined in the child template but not in the parent. Generally it works like ``render_to_string`` from Django, but allows you to specify a block to render. Features ======== * Render a specific block from a template * Fully supports the Django templating engine * Partially supports the `Jinja2 `__ engine: it does not currently process the ``extends`` tag. Requirements ============ Django Render Block supports Django 2.2, 3.0, and 3.1 on Python 3.6, 3.7, 3.8, and 3.9. Examples ======== In ``test1.html``: .. code-block:: jinja {% block block1 %}block1 from test1{% endblock %} {% block block2 %}block2 from test1{% endblock %} In ``test2.html``: .. code-block:: jinja {% extends 'test1.html' %} {% block block1 %}block1 from test2{% endblock %} And from the Python shell: .. code-block:: python >>> from render_block import render_block_to_string >>> print render_block_to_string('test2.html', 'block1') u'block1 from test2' >>> print render_block_to_string('test2.html', 'block2') u'block2 from test1' It can also accept a context as a ``dict`` (just like ``render_to_string``), in ``test3.html``: .. code-block:: jinja {% block block3 %}Render this {{ variable }}!{% endblock %} And from Python: .. code-block:: python >>> print render_block_to_string('test3.html', 'block3', {'variable': 'test'}) u'Render this test!' API Reference ============= The API is simple and attempts to mirror the built-in ``render_to_string`` API. ``render_block_to_string(template_name, block_name, context=None, request=None)`` ``template_name`` The name of the template to load and render. If it’s a list of template names, Django uses ``select_template()`` instead of ``get_template()`` to find the template. ``block_name`` The name of the block to render from the above template. ``context`` A ``dict`` to be used as the template’s context for rendering. A ``Context`` object can be provided for Django templates. ``context`` is optional. If not provided, an empty context will be used. ``request`` The request object used to render the template. ``request`` is optional and works only for Django templates. If both context and request are provided, a ``RequestContext`` will be used instead of a ``Context``. Exceptions ---------- Like ``render_to_string`` this will raise the following exceptions: ``TemplateDoesNotExists`` Raised if the template(s) specified by ``template_name`` cannot be loaded. ``TemplateSyntaxError`` Raised if the loaded template contains invalid syntax. There are also two additional errors that can be raised: ``BlockNotFound`` Raised if the block given by ``block_name`` does not exist in the template. ``UnsupportedEngine`` Raised if a template backend besides the Django backend is used. Contributing ============ If you find a bug or have an idea for an improvement to Django Render Block, please `file an issue `_ or provide a pull request! Check the `list of issues `_ for ideas of what to work on. Attribution =========== This is based on a few sources: * Originally `Django Snippet 769 `__ * Updated version `Django Snippet 942 `__ * A version of the snippets was ported as `Django-Block-Render `_ * Additionally inspired by part of `django-templated-email `_ * Also based on a `StackOverflow answer 2687173 `_ .. :changelog: Changelog ######### 0.8.1 (October 15, 2020) ======================== * Fixes a regression in v0.8 where a ``Context`` could not be re-used. See `#25 `_, contributed by @evanbrumley. 0.8 (October 6, 2020) ===================== * ``render_block_to_string`` now forwards the ``Context`` passed as ``context`` parameter. (`#21 `_, by @bblanchon) * Drop support for Python 3.5, officially support Python 3.9. 0.7 (July 13, 2020) =================== * Drop support for Django < 2.2. * Officially support Django 3.0, 3.1. * Drop support for Python 2.7. * Officially support Python 3.8. 0.6 (May 8, 2019) ================= * Supports Django 1.11, Django 2.1, and Django 2.2. * Supports Python 2.7, 3.5, 3.6, and 3.7. * ``render_block_to_string`` now optionally accepts a ``request`` parameter. If given, a ``RequestContext`` instead of a ``Context`` is used when rendering with the Django templating engine. See `#15 `_, thanks to @vintage. 0.5 (September 1, 2016) ======================= * Fixes a major issue with inheriting templates and rendering a block found in the parent template, but overwriting part of it in the child template. (`#8 `_) 0.4 (August 4, 2016) ==================== * Initial support for using the `Jinja2 `_ templating engine. See README for caveats. (`#3 `_) * Support Django 1.10. (`#5 `_) * Support Python 3. (`#6 `_) 0.3.1 (June 1, 2016) ==================== * Refactoring to make more generic (for potentially supporting multiple templating engines). 0.3 (May 27, 2016) ================== * Largely rewritten. * Updated to support modern Django (1.8, 1.9): * Guards against different template backends. * Uses internal APIs for each node. * Removed ``context_instance`` parameter. * Support for calling ``{{ block.super }}``. 0.2.2 (January 10, 2011) ======================== * Updated per `comment 3466 on Django Snippet 942 `_ to fix an issue with nested extends. The specific bug was not reproducible, but the additional code shouldn't hurt. 0.2.1 (August 27, 2010) ======================= * Updated per `comment 3237 on Django Snippet 942 `_ to remove a pointless render. The specific bug was not reproducible, but the code is extraneous. 0.2 (August 4, 2008) ==================== * Updated version from `Django Snippet 942 `_ by zbyte64. * Improves include: 1. Simpler/better handling of "extends" block tag 2. Searches If/Else blocks 3. Less code 4. Allow list of templates to be passed which is closer to the behavior of render_to_response 0.1 (May 22, 2008) ================== * Initial version from `Django Snippet 769 `_ by sciyoshi. * Supports Django 0.96. Keywords: django,template,block,templates,render,context Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Environment :: Web Environment Classifier: Topic :: Internet Classifier: Framework :: Django Classifier: Framework :: Django :: 2.2 Classifier: Framework :: Django :: 3.0 Classifier: Framework :: Django :: 3.1 Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: License :: OSI Approved :: ISC License (ISCL) Requires-Python: >=3.5 Description-Content-Type: text/x-rst django-render-block-0.8.1/README.rst0000644000076500000240000001025113737155637017436 0ustar clokepstaff00000000000000Django Render Block ################### .. image:: https://travis-ci.org/clokep/django-render-block.svg?branch=master :target: https://travis-ci.org/clokep/django-render-block Render the content of a specific block tag from a Django template. Works for arbitrary template inheritance, even if a block is defined in the child template but not in the parent. Generally it works like ``render_to_string`` from Django, but allows you to specify a block to render. Features ======== * Render a specific block from a template * Fully supports the Django templating engine * Partially supports the `Jinja2 `__ engine: it does not currently process the ``extends`` tag. Requirements ============ Django Render Block supports Django 2.2, 3.0, and 3.1 on Python 3.6, 3.7, 3.8, and 3.9. Examples ======== In ``test1.html``: .. code-block:: jinja {% block block1 %}block1 from test1{% endblock %} {% block block2 %}block2 from test1{% endblock %} In ``test2.html``: .. code-block:: jinja {% extends 'test1.html' %} {% block block1 %}block1 from test2{% endblock %} And from the Python shell: .. code-block:: python >>> from render_block import render_block_to_string >>> print render_block_to_string('test2.html', 'block1') u'block1 from test2' >>> print render_block_to_string('test2.html', 'block2') u'block2 from test1' It can also accept a context as a ``dict`` (just like ``render_to_string``), in ``test3.html``: .. code-block:: jinja {% block block3 %}Render this {{ variable }}!{% endblock %} And from Python: .. code-block:: python >>> print render_block_to_string('test3.html', 'block3', {'variable': 'test'}) u'Render this test!' API Reference ============= The API is simple and attempts to mirror the built-in ``render_to_string`` API. ``render_block_to_string(template_name, block_name, context=None, request=None)`` ``template_name`` The name of the template to load and render. If it’s a list of template names, Django uses ``select_template()`` instead of ``get_template()`` to find the template. ``block_name`` The name of the block to render from the above template. ``context`` A ``dict`` to be used as the template’s context for rendering. A ``Context`` object can be provided for Django templates. ``context`` is optional. If not provided, an empty context will be used. ``request`` The request object used to render the template. ``request`` is optional and works only for Django templates. If both context and request are provided, a ``RequestContext`` will be used instead of a ``Context``. Exceptions ---------- Like ``render_to_string`` this will raise the following exceptions: ``TemplateDoesNotExists`` Raised if the template(s) specified by ``template_name`` cannot be loaded. ``TemplateSyntaxError`` Raised if the loaded template contains invalid syntax. There are also two additional errors that can be raised: ``BlockNotFound`` Raised if the block given by ``block_name`` does not exist in the template. ``UnsupportedEngine`` Raised if a template backend besides the Django backend is used. Contributing ============ If you find a bug or have an idea for an improvement to Django Render Block, please `file an issue `_ or provide a pull request! Check the `list of issues `_ for ideas of what to work on. Attribution =========== This is based on a few sources: * Originally `Django Snippet 769 `__ * Updated version `Django Snippet 942 `__ * A version of the snippets was ported as `Django-Block-Render `_ * Additionally inspired by part of `django-templated-email `_ * Also based on a `StackOverflow answer 2687173 `_ django-render-block-0.8.1/django_render_block.egg-info/0000755000076500000240000000000013742120342023372 5ustar clokepstaff00000000000000django-render-block-0.8.1/django_render_block.egg-info/PKG-INFO0000644000076500000240000002476613742120341024505 0ustar clokepstaff00000000000000Metadata-Version: 2.1 Name: django-render-block Version: 0.8.1 Summary: Render a particular block from a template to a string. Home-page: https://github.com/clokep/django-render-block Author: Patrick Cloke Author-email: clokep@patrick.cloke.us License: UNKNOWN Download-URL: https://github.com/clokep/django-render-block Description: Django Render Block ################### .. image:: https://travis-ci.org/clokep/django-render-block.svg?branch=master :target: https://travis-ci.org/clokep/django-render-block Render the content of a specific block tag from a Django template. Works for arbitrary template inheritance, even if a block is defined in the child template but not in the parent. Generally it works like ``render_to_string`` from Django, but allows you to specify a block to render. Features ======== * Render a specific block from a template * Fully supports the Django templating engine * Partially supports the `Jinja2 `__ engine: it does not currently process the ``extends`` tag. Requirements ============ Django Render Block supports Django 2.2, 3.0, and 3.1 on Python 3.6, 3.7, 3.8, and 3.9. Examples ======== In ``test1.html``: .. code-block:: jinja {% block block1 %}block1 from test1{% endblock %} {% block block2 %}block2 from test1{% endblock %} In ``test2.html``: .. code-block:: jinja {% extends 'test1.html' %} {% block block1 %}block1 from test2{% endblock %} And from the Python shell: .. code-block:: python >>> from render_block import render_block_to_string >>> print render_block_to_string('test2.html', 'block1') u'block1 from test2' >>> print render_block_to_string('test2.html', 'block2') u'block2 from test1' It can also accept a context as a ``dict`` (just like ``render_to_string``), in ``test3.html``: .. code-block:: jinja {% block block3 %}Render this {{ variable }}!{% endblock %} And from Python: .. code-block:: python >>> print render_block_to_string('test3.html', 'block3', {'variable': 'test'}) u'Render this test!' API Reference ============= The API is simple and attempts to mirror the built-in ``render_to_string`` API. ``render_block_to_string(template_name, block_name, context=None, request=None)`` ``template_name`` The name of the template to load and render. If it’s a list of template names, Django uses ``select_template()`` instead of ``get_template()`` to find the template. ``block_name`` The name of the block to render from the above template. ``context`` A ``dict`` to be used as the template’s context for rendering. A ``Context`` object can be provided for Django templates. ``context`` is optional. If not provided, an empty context will be used. ``request`` The request object used to render the template. ``request`` is optional and works only for Django templates. If both context and request are provided, a ``RequestContext`` will be used instead of a ``Context``. Exceptions ---------- Like ``render_to_string`` this will raise the following exceptions: ``TemplateDoesNotExists`` Raised if the template(s) specified by ``template_name`` cannot be loaded. ``TemplateSyntaxError`` Raised if the loaded template contains invalid syntax. There are also two additional errors that can be raised: ``BlockNotFound`` Raised if the block given by ``block_name`` does not exist in the template. ``UnsupportedEngine`` Raised if a template backend besides the Django backend is used. Contributing ============ If you find a bug or have an idea for an improvement to Django Render Block, please `file an issue `_ or provide a pull request! Check the `list of issues `_ for ideas of what to work on. Attribution =========== This is based on a few sources: * Originally `Django Snippet 769 `__ * Updated version `Django Snippet 942 `__ * A version of the snippets was ported as `Django-Block-Render `_ * Additionally inspired by part of `django-templated-email `_ * Also based on a `StackOverflow answer 2687173 `_ .. :changelog: Changelog ######### 0.8.1 (October 15, 2020) ======================== * Fixes a regression in v0.8 where a ``Context`` could not be re-used. See `#25 `_, contributed by @evanbrumley. 0.8 (October 6, 2020) ===================== * ``render_block_to_string`` now forwards the ``Context`` passed as ``context`` parameter. (`#21 `_, by @bblanchon) * Drop support for Python 3.5, officially support Python 3.9. 0.7 (July 13, 2020) =================== * Drop support for Django < 2.2. * Officially support Django 3.0, 3.1. * Drop support for Python 2.7. * Officially support Python 3.8. 0.6 (May 8, 2019) ================= * Supports Django 1.11, Django 2.1, and Django 2.2. * Supports Python 2.7, 3.5, 3.6, and 3.7. * ``render_block_to_string`` now optionally accepts a ``request`` parameter. If given, a ``RequestContext`` instead of a ``Context`` is used when rendering with the Django templating engine. See `#15 `_, thanks to @vintage. 0.5 (September 1, 2016) ======================= * Fixes a major issue with inheriting templates and rendering a block found in the parent template, but overwriting part of it in the child template. (`#8 `_) 0.4 (August 4, 2016) ==================== * Initial support for using the `Jinja2 `_ templating engine. See README for caveats. (`#3 `_) * Support Django 1.10. (`#5 `_) * Support Python 3. (`#6 `_) 0.3.1 (June 1, 2016) ==================== * Refactoring to make more generic (for potentially supporting multiple templating engines). 0.3 (May 27, 2016) ================== * Largely rewritten. * Updated to support modern Django (1.8, 1.9): * Guards against different template backends. * Uses internal APIs for each node. * Removed ``context_instance`` parameter. * Support for calling ``{{ block.super }}``. 0.2.2 (January 10, 2011) ======================== * Updated per `comment 3466 on Django Snippet 942 `_ to fix an issue with nested extends. The specific bug was not reproducible, but the additional code shouldn't hurt. 0.2.1 (August 27, 2010) ======================= * Updated per `comment 3237 on Django Snippet 942 `_ to remove a pointless render. The specific bug was not reproducible, but the code is extraneous. 0.2 (August 4, 2008) ==================== * Updated version from `Django Snippet 942 `_ by zbyte64. * Improves include: 1. Simpler/better handling of "extends" block tag 2. Searches If/Else blocks 3. Less code 4. Allow list of templates to be passed which is closer to the behavior of render_to_response 0.1 (May 22, 2008) ================== * Initial version from `Django Snippet 769 `_ by sciyoshi. * Supports Django 0.96. Keywords: django,template,block,templates,render,context Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Environment :: Web Environment Classifier: Topic :: Internet Classifier: Framework :: Django Classifier: Framework :: Django :: 2.2 Classifier: Framework :: Django :: 3.0 Classifier: Framework :: Django :: 3.1 Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: License :: OSI Approved :: ISC License (ISCL) Requires-Python: >=3.5 Description-Content-Type: text/x-rst django-render-block-0.8.1/django_render_block.egg-info/SOURCES.txt0000644000076500000240000000066513742120341025264 0ustar clokepstaff00000000000000CHANGELOG.rst LICENSE MANIFEST.in README.rst setup.py django_render_block.egg-info/PKG-INFO django_render_block.egg-info/SOURCES.txt django_render_block.egg-info/dependency_links.txt django_render_block.egg-info/requires.txt django_render_block.egg-info/top_level.txt render_block/__init__.py render_block/base.py render_block/django.py render_block/exceptions.py render_block/jinja2.py tests/__init__.py tests/settings.py tests/tests.pydjango-render-block-0.8.1/django_render_block.egg-info/dependency_links.txt0000644000076500000240000000000113742120341027437 0ustar clokepstaff00000000000000 django-render-block-0.8.1/django_render_block.egg-info/requires.txt0000644000076500000240000000001413742120341025764 0ustar clokepstaff00000000000000django>=2.2 django-render-block-0.8.1/django_render_block.egg-info/top_level.txt0000644000076500000240000000002313742120341026116 0ustar clokepstaff00000000000000render_block tests django-render-block-0.8.1/render_block/0000755000076500000240000000000013742120342020356 5ustar clokepstaff00000000000000django-render-block-0.8.1/render_block/__init__.py0000644000076500000240000000017213616533551022501 0ustar clokepstaff00000000000000from render_block.base import render_block_to_string from render_block.exceptions import BlockNotFound, UnsupportedEngine django-render-block-0.8.1/render_block/base.py0000644000076500000240000000312613737155634021664 0ustar clokepstaff00000000000000from django.template import loader from django.template.backends.django import Template as DjangoTemplate try: from django.template.backends.jinja2 import Template as Jinja2Template except ImportError: # Most likely Jinja2 isn't installed, in that case just create a class since # we always want it to be false anyway. class Jinja2Template: pass from render_block.django import django_render_block from render_block.exceptions import UnsupportedEngine def render_block_to_string(template_name, block_name, context=None, request=None): """ Loads the given template_name and renders the given block with the given dictionary as context. Returns a string. template_name The name of the template to load and render. If it's a list of template names, Django uses select_template() instead of get_template() to find the template. """ # Like render_to_string, template_name can be a string or a list/tuple. if isinstance(template_name, (tuple, list)): t = loader.select_template(template_name) else: t = loader.get_template(template_name) # Create the context instance. context = context or {} # The Django backend. if isinstance(t, DjangoTemplate): return django_render_block(t, block_name, context, request) elif isinstance(t, Jinja2Template): from render_block.jinja2 import jinja2_render_block return jinja2_render_block(t, block_name, context) else: raise UnsupportedEngine( 'Can only render blocks from the Django template backend.') django-render-block-0.8.1/render_block/django.py0000644000076500000240000001110213742117767022206 0ustar clokepstaff00000000000000from copy import copy from django.template import Context, RequestContext from django.template.context import RenderContext from django.template.base import TextNode from django.template.loader_tags import (BLOCK_CONTEXT_KEY, BlockContext, BlockNode, ExtendsNode) from render_block.exceptions import BlockNotFound def django_render_block(template, block_name, context, request=None): # Create a Django Context if needed if isinstance(context, Context): # Make a copy of the context and reset the rendering state. # Trying to re-use a RenderContext in multiple renders can # lead to TemplateNotFound errors, as Django will skip past # any template files it thinks it has already rendered in a # template's inheritance stack. context_instance = copy(context) context_instance.render_context = RenderContext() elif request: context_instance = RequestContext(request, context) else: context_instance = Context(context) # Get the underlying django.template.base.Template object. template = template.template # Bind the template to the context. with context_instance.bind_template(template): # Before trying to render the template, we need to traverse the tree of # parent templates and find all blocks in them. parent_template = _build_block_context(template, context_instance) try: return _render_template_block(template, block_name, context_instance) except BlockNotFound: # The block wasn't found in the current template. # If there's no parent template (i.e. no ExtendsNode), re-raise. if not parent_template: raise # Check the parent template for this block. return _render_template_block( parent_template, block_name, context_instance) def _build_block_context(template, context): """Populate the block context with BlockNodes from parent templates.""" # Ensure there's a BlockContext before rendering. This allows blocks in # ExtendsNodes to be found by sub-templates (allowing {{ block.super }} and # overriding sub-blocks to work). if BLOCK_CONTEXT_KEY not in context.render_context: context.render_context[BLOCK_CONTEXT_KEY] = BlockContext() block_context = context.render_context[BLOCK_CONTEXT_KEY] for node in template.nodelist: if isinstance(node, ExtendsNode): compiled_parent = node.get_parent(context) # Add the parent node's blocks to the context. (This ends up being # similar logic to ExtendsNode.render(), where we're adding the # parent's blocks to the context so a child can find them.) block_context.add_blocks( {n.name: n for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)}) _build_block_context(compiled_parent, context) return compiled_parent # The ExtendsNode has to be the first non-text node. if not isinstance(node, TextNode): break def _render_template_block(template, block_name, context): """Renders a single block from a template.""" return _render_template_block_nodelist(template.nodelist, block_name, context) def _render_template_block_nodelist(nodelist, block_name, context): """Recursively iterate over a node to find the wanted block.""" # Attempt to find the wanted block in the current template. for node in nodelist: # If the wanted block was found, return it. if isinstance(node, BlockNode): # No matter what, add this block to the rendering context. context.render_context[BLOCK_CONTEXT_KEY].push(node.name, node) # If the name matches, you're all set and we found the block! if node.name == block_name: return node.render(context) # If a node has children, recurse into them. Based on # django.template.base.Node.get_nodes_by_type. for attr in node.child_nodelists: try: new_nodelist = getattr(node, attr) except AttributeError: continue # Try to find the block recursively. try: return _render_template_block_nodelist(new_nodelist, block_name, context) except BlockNotFound: continue # The wanted block_name was not found. raise BlockNotFound("block with name '%s' does not exist" % block_name) django-render-block-0.8.1/render_block/exceptions.py0000644000076500000240000000046613737155634023137 0ustar clokepstaff00000000000000from django.core.exceptions import ImproperlyConfigured from django.template.base import TemplateSyntaxError class BlockNotFound(TemplateSyntaxError): """The expected block was not found.""" class UnsupportedEngine(ImproperlyConfigured): """An engine that we cannot render blocks from was used.""" django-render-block-0.8.1/render_block/jinja2.py0000644000076500000240000000112013737155634022117 0ustar clokepstaff00000000000000from render_block.exceptions import BlockNotFound def jinja2_render_block(template, block_name, context): # Get the underlying jinja2.environment.Template object. template = template.template # Create a new Context instance. context = template.new_context(context) # Try to find the wanted block. try: gen = template.blocks[block_name](context) except KeyError: raise BlockNotFound("block with name '%s' does not exist" % block_name) # The result from above is a generator which yields unicode strings. return ''.join([s for s in gen]) django-render-block-0.8.1/setup.cfg0000644000076500000240000000004613742120342017546 0ustar clokepstaff00000000000000[egg_info] tag_build = tag_date = 0 django-render-block-0.8.1/setup.py0000644000076500000240000000305113742120231017433 0ustar clokepstaff00000000000000import codecs from setuptools import find_packages, setup def long_description(): result = '' for filename in ['README.rst', 'CHANGELOG.rst']: with codecs.open(filename, encoding='utf8') as f: result += f.read() + '\n' return result setup( name='django-render-block', packages=find_packages(), version='0.8.1', description='Render a particular block from a template to a string.', long_description=long_description(), long_description_content_type='text/x-rst', author='Patrick Cloke', author_email='clokep@patrick.cloke.us', url='https://github.com/clokep/django-render-block', download_url='https://github.com/clokep/django-render-block', keywords=['django', 'template', 'block', 'templates', 'render', 'context'], classifiers=[ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Topic :: Software Development :: Libraries :: Python Modules', 'Environment :: Web Environment', 'Topic :: Internet', 'Framework :: Django', 'Framework :: Django :: 2.2', 'Framework :: Django :: 3.0', 'Framework :: Django :: 3.1', 'Programming Language :: Python', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'License :: OSI Approved :: ISC License (ISCL)', ], install_requires=[ 'django>=2.2', ], python_requires=">=3.5", ) django-render-block-0.8.1/tests/0000755000076500000240000000000013742120342017067 5ustar clokepstaff00000000000000django-render-block-0.8.1/tests/__init__.py0000644000076500000240000000000013616533551021200 0ustar clokepstaff00000000000000django-render-block-0.8.1/tests/settings.py0000644000076500000240000000151613701434405021307 0ustar clokepstaff00000000000000SECRET_KEY = 'not_empty' SITE_ID = 1 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:', } } TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages' ], }, }, ] MIDDLEWARE_CLASSES = tuple() EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend' INSTALLED_APPS = ( 'tests', ) AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.ModelBackend', ) django-render-block-0.8.1/tests/tests.py0000644000076500000240000002072513742117767020632 0ustar clokepstaff00000000000000from unittest import skip from django.template import Context from django.test import modify_settings, override_settings, TestCase, RequestFactory from render_block import render_block_to_string, BlockNotFound, UnsupportedEngine class TestDjango(TestCase): """Test the Django templating engine.""" def assertExceptionMessageEquals(self, exception, expected): self.assertEqual(expected, exception.args[0]) def test_block(self): """Test rendering an individual block.""" result = render_block_to_string('test1.html', 'block1') self.assertEqual(result, 'block1 from test1') # No reason this shouldn't work, but just in case. result = render_block_to_string('test1.html', 'block2') self.assertEqual(result, 'block2 from test1') def test_override(self): """This block is overridden in test2.""" result = render_block_to_string('test2.html', 'block1') self.assertEqual(result, 'block1 from test2') def test_inherit(self): """This block is inherited from test1.""" result = render_block_to_string('test2.html', 'block2') self.assertEqual(result, 'block2 from test1') def test_no_block(self): """Check if there's no block available an exception is raised.""" with self.assertRaises(BlockNotFound) as exc: render_block_to_string('test1.html', 'noblock') self.assertExceptionMessageEquals(exc.exception, "block with name 'noblock' does not exist") def test_include(self): """Ensure that an include tag in a block still works.""" result = render_block_to_string('test3_django.html', 'block1') self.assertEqual(result, 'included template') def test_super(self): """Test that block.super works.""" result = render_block_to_string('test3_django.html', 'block2') self.assertEqual(result, 'block2 from test3 - block2 from test1') def test_multi_super(self): result = render_block_to_string('test6_django.html', 'block2') self.assertEqual(result, 'block2 from test6 - block2 from test3 - block2 from test1') def test_super_with_same_context_on_multiple_executions(self): """Test that block.super works when fed the same context object twice.""" context = Context() result_one = render_block_to_string('test3_django.html', 'block2', context=context) result_two = render_block_to_string('test3_django.html', 'block2', context=context) self.assertEqual(result_one, result_two, 'block2 from test3 - block2 from test1') def test_subblock(self): """Test that a block within a block works.""" result = render_block_to_string('test5.html', 'block1') self.assertEqual(result, 'block3 from test5') result = render_block_to_string('test5.html', 'block3') self.assertEqual(result, 'block3 from test5') def test_subblock_no_parent(self): """ Test that a block within a block works if the parent block is only found in the base template. This is very similar to test_subblock, but the templates differ. In this test the sub-template does not replace the entire block from the parent template. """ result = render_block_to_string('test_sub.html', 'base') self.assertEqual(result, '\n\nbar\n\n') result = render_block_to_string('test_sub.html', 'first') self.assertEqual(result, '\nbar\n') def test_context(self): """Test that a context is properly rendered in a template.""" data = 'block2 from test5' result = render_block_to_string('test5.html', 'block2', {'foo': data}) self.assertEqual(result, data) def test_context_autoescape_off(self): """Test that the user can disable autoescape by providing a Context instance.""" data = "&'" result = render_block_to_string('test5.html', 'block2', Context({'foo': data}, autoescape=False)) self.assertEqual(result, data) @override_settings( TEMPLATES=[{ 'BACKEND': 'django.template.backends.dummy.TemplateStrings', 'DIRS': ['tests/templates'], 'APP_DIRS': True, }] ) def test_different_backend(self): """ Ensure an exception is thrown if a different backed from the Django backend is used. """ with self.assertRaises(UnsupportedEngine) as exc: render_block_to_string('test1.html', 'noblock') self.assertExceptionMessageEquals(exc.exception, "Can only render blocks from the Django template backend.") @modify_settings( INSTALLED_APPS={ 'prepend': [ 'django.contrib.auth', 'django.contrib.contenttypes', ], }, ) def test_request_context(self): """Test that a request context data are properly rendered in a template.""" request = RequestFactory().get('dummy-url') result = render_block_to_string('test_request_context.html', 'block1', {}, request) self.assertEqual(result, '/dummy-url') @override_settings( TEMPLATES=[{ 'BACKEND': 'django.template.backends.jinja2.Jinja2', 'DIRS': ['tests/templates'], 'APP_DIRS': True, }] ) class TestJinja2(TestCase): """Test the Django templating engine.""" def assertExceptionMessageEquals(self, exception, expected): self.assertEqual(expected, exception.args[0]) def test_block(self): """Test rendering an individual block.""" result = render_block_to_string('test1.html', 'block1') self.assertEqual(result, 'block1 from test1') # No reason this shouldn't work, but just in case. result = render_block_to_string('test1.html', 'block2') self.assertEqual(result, 'block2 from test1') def test_override(self): """This block is overridden in test2.""" result = render_block_to_string('test2.html', 'block1') self.assertEqual(result, 'block1 from test2') @skip('Not currently supported.') def test_inherit(self): """This block is inherited from test1.""" result = render_block_to_string('test2.html', 'block2') self.assertEqual(result, 'block2 from test1') def test_no_block(self): """Check if there's no block available an exception is raised.""" with self.assertRaises(BlockNotFound) as exc: render_block_to_string('test1.html', 'noblock') self.assertExceptionMessageEquals(exc.exception, "block with name 'noblock' does not exist") def test_include(self): """Ensure that an include tag in a block still works.""" result = render_block_to_string('test3_jinja2.html', 'block1') self.assertEqual(result, 'included template') @skip('Not currently supported.') def test_super(self): """Test that super() works.""" result = render_block_to_string('test3_jinja2.html', 'block2') self.assertEqual(result, 'block2 from test3 - block2 from test1') @skip('Not currently supported.') def test_multi_super(self): result = render_block_to_string('test6_jinja2.html', 'block2') self.assertEqual(result, 'block2 from test6 - block2 from test3 - block2 from test1') def test_subblock(self): """Test that a block within a block works.""" result = render_block_to_string('test5.html', 'block1') self.assertEqual(result, 'block3 from test5') result = render_block_to_string('test5.html', 'block3') self.assertEqual(result, 'block3 from test5') @skip('Not currently supported.') def test_subblock_no_parent(self): """ Test that a block within a block works if the parent block is only found in the base template. This is very similar to test_subblock, but the templates differ. In this test the sub-template does not replace the entire block from the parent template. """ result = render_block_to_string('test_sub.html', 'base') self.assertEqual(result, '\n\nbar\n\n') result = render_block_to_string('test_sub.html', 'first') self.assertEqual(result, '\nbar\n') def test_context(self): """Test that a context is properly rendered in a template.""" data = 'block2 from test5' result = render_block_to_string('test5.html', 'block2', {'foo': data}) self.assertEqual(result, data)