Hi Jonathan,
I agree it may be one step to far but I thought it may be worth discussing
as it opens up an interesting option for overcoming some of the
shortcomings of the current 'include' tag. It actually isn't to hard to
implement, I think my code should be pretty close to what would end up in
use if it is accepted. The alternative to the namespaces could be adding a
'public' option to the tag that makes the blocks inside available to
overwrite?
I am particularly keen on being able to override multiple blocks, I think
this is a good example of what could be constructed with this tag.
Bootstrap has a Modal component and the code below uses its markup as an
example::
# modal.html
<div class="modal fade {{ extra_class }}"
id="{{ modal_id }}"
tabindex="-1"
role="dialog"
{% if used_block.header %}aria-labelledby="{{ modal_id }}_label"{% endif
%}
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
{% if used_block.header %}
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span
aria-hidden="true">×</span><span class="sr-only">Close</span></button>
<h4 class="modal-title" id="{{ modal_id }}_label">{% block title
%}{% endblock %}</h4>
</div>
{% endif %}
{% if used_block.body %}
<div class="modal-body">
{% block body %}{% endblock %}
</div>
{% endif %}
{% if used_block.footer %}
<div class="modal-footer">
{% block footer %}{% endblock %}
</div>
{% endif %}
</div>
</div>
</div>
This could then be included in a page in the following ways:
# my_page.html
{% use 'modal.html' with modal_id='settings_modal' %}
{% block title %}
Your Settings
{% endblock %}
{% block body %}
... A settings form? ...
{% endblock %}
{% block footer %}
<button type="button" class="btn btn-default" data-dismiss="modal">Close
</button>
<button type="button" class="btn btn-primary">Save changes</button>
{% endblock %}
{% enduse %}
{% use 'modal.html' with extra_class='large' modal_id='annother_modal' %}
{% block title %}
Another Modal
{% endblock %}
{% block body %}
This one has no footer/
{% endblock %}
{% enduse %}
{% use 'modal.html' with extra_class='small' block body %}
This one doesn't even have a title, and as we are just overriding a
single block we loose the block tags.
{% enduse %}
In the template we are using the 'used_blocks' context variable to decide
when to include the div's that wrap the head, body and footer of the modal.
We are also using the extra context variables assigned by the use tag to
choose if its a normal, large or small modal and give it an id.
This can obviously already be implemented with a hierarchy of extended
templates but I think this tag creates a very clean way of structuring
these components that doesn't require making custom template tags or lots
of template files.
Sam
On Friday, September 5, 2014 2:55:28 PM UTC+1, Jonathan Slenders wrote:
>
> There I think you are going one step too far.
>
> I think people should not be able to override blocks inside an included
> template in a "use"-block. That's one level too deep. It's confusing and
> you have other solutions for that.
> In the template that you "use", you could put a {% block %} around the {%
> include %} if you want to be able to replace that. It's a little more work,
> but in my opinion, it's not worth introducing the concept of name spaces.
>
>
>
> Aside from that, there's one example that I would like to share, why we
> would need something like this.
> Ofter we end up writing code like this:
>
> {% include "start_table.html" %}
> {% include "content.html" %}
> {% endclude "end_table.html" %}
>
>
> "start_table.html" would contain:
> <table><thead><tr><td>...</td><td>...</td></tr></thead>"
>
> "end_table.html" would contain:
> </table>
>
> Now open any of the "start/end_table.html" files in an editor, and the
> editor will not be able to find the matching tags.
> On the other hand, if you can have:
>
> {% use "table.html" %}
> {% include "content.html" %}
> {% enduse %}
>
>
> Then we have the HTLM open and close tags in the same file.
> (I agree that in this specific example, there is an alternative: use
> extends in the included template. But there are many cases where you don't
> want that, because you need e.g. the decorated and non-decorated version of
> the include.)
>
> Now, whether or not to allow overriding multiple blocks of the "used"
> template. I don't know. It's technically also possible to support both
> cases. (E.g. replace the content of the "content" block if no blocks are
> defined between "use" and "enduse". Otherwise, find matching block namess,
> like with {% extends %}.)
>
>
>
>
>
> Le jeudi 4 septembre 2014 19:11:57 UTC+2, Sam Willis a écrit :
>>
>> Hi again,
>>
>> One thing that I wasn't happy with in the implementation I posted
>> yesterday was that you could not override templates included using 'use' in
>> another child template. Effectively all blocks inside the 'use' tag and its
>> included template where invisible outside of it. This needed to be the case
>> so that you didn't get block names clashing with each other and could use
>> the 'use' tag to include the same template with the same blocks overridden
>> multiple times in one template
>>
>> One potential solution to this is to enable blocks to have namespaces, so
>> for example if you had the template:
>>
>> # page_heading.html
>> <div class="page-heading">
>> <h1>{% block heading %}My Heading{% endblock %}</h1>
>> <h2>{% block sub_heading %}My Subheading{% endblock %}<h2>
>> </div>
>>
>> and included it a template such as (note the 'ns page_heading'):
>>
>> # base_page.html
>> ...
>> {% use 'page_heading.html' ns page_heading %}
>> <h1>{% block heading %}Basic Page Heading{% endblock %}</h1>
>> {% enduse %}
>> ...
>>
>> You could then override the 'heading' block in a child template of
>> 'base_page.html' by doing:
>>
>> # actual_page.html
>> {% extends 'base_page.html' %}
>> {% block page_heading.heading %}Basic Page Heading{% endblock %}
>>
>> I have updated my implementation to support this here:
>> https://gist.github.com/samwillis/af3992f69c2597a16252
>>
>> It requires that the BlockContext supports namespaces, in my
>> implementation there is NsBlockContext which is patched in when required
>> but if this was accepted into core then NsBlockContext would replace
>> BlockContext.
>>
>> This is (almost) fully backwards compatible with existing code as blocks
>> already named with periods will just end up in a namespace but continue to
>> function fully. The only exception is if you have blocks named both
>> 'global.block_name' and 'block_name' as these would now address the same
>> block. We could give the global namespace a more obscure name (such as
>> '__global__') to prevent this but it would make the trick below more clunky.
>>
>> One interesting use for the 'use' tag with this addition is breaking up
>> more complex templates into multiple files. While researching various
>> options before developing this I found allot of people discovering that you
>> couldn't override a block inside an 'include'ed template. With this 'use'
>> tag you would be able to do this, for example you could have base templates
>> like this:
>>
>> # base.html
>> <html>
>> <head>
>> {% use 'head.html' ns global %}{% enduse %}
>> </head>
>> <body>
>> ...
>> </body>
>> </html>
>>
>> # head.html
>> <title>{% block title %}{% endblock %}</title>
>>
>> and the page template:
>>
>> # actual_page.html
>> {% extends 'base.html' %}
>> {% block title %}My Page Title{% endblock %}
>>
>> If we switched the 'use' tag to use a namespace other than global:
>>
>> {% use 'head.html' ns html_head %}{% enduse %}
>>
>> The page template would then be:
>>
>> # actual_page.html
>> {% extends 'base.html' %}
>> {% block html_head.title %}My Page Title{% endblock %}
>>
>> I think this will provide very useful in structuring complex templates.
>>
>> Maybe with this functionality the tag should be called 'embed'?
>>
>> What do you think?
>>
>>
>> Sam
>>
>>
>> On Thursday, September 4, 2014 5:28:34 PM UTC+1, Sam Willis wrote:
>>>
>>> Curtis,
>>>
>>> You are welcome to include it in your project and I'm happy to help out.
>>> Depending on what happens here I may still release it as a simple
>>> standalone app though.
>>>
>>> I do think this is the kind of thing that would do best in core though
>>> as it would ensure that there was a standardised patten for creating these
>>> reusable template components.
>>>
>>>
>>> Marc,
>>>
>>> I am happy to have a look around (and have already to some extent when
>>> developing this) and put together a review of the various existing options.
>>> This is my first foray into contributing to Django and so I am keep to help
>>> in any way.
>>>
>>> Does anyone know of anything particular I should be looking at?
>>>
>>>
>>>
>>> On Thursday, September 4, 2014 2:06:15 PM UTC+1, Marc Tamlyn wrote:
>>>>
>>>> I would like to see someone do a review of the various third party
>>>> implementations of concepts like this, I think there's a place in core for
>>>> some variant, it's just working out which one the "right" one is.
>>>>
>>>> The other issue with adding new tags to core (especially with "common"
>>>> names) is name clashes with third party code. This shouldn't be too
>>>> significant though if we have enough use.
>>>>
>>>>
>>>> On 4 September 2014 12:09, Josh Smeaton <[email protected]> wrote:
>>>>
>>>>> I really like this idea, and have wanted something similar every time
>>>>> I start a new project and begin building out the main template. I think
>>>>> including this (or something like this) in core is a great idea. Just
>>>>> because it *can* be implemented in 3rd party code, doesn't mean it has to
>>>>> be.
>>>>>
>>>>>
>>>>> On Thursday, 4 September 2014 02:10:33 UTC+10, Sam Willis wrote:
>>>>>>
>>>>>> Hi All,
>>>>>>
>>>>>> I would like to propose a new template tag to be included in Django,
>>>>>> it is sort of a cross between 'include' and 'extends'. You can find an
>>>>>> implementation here: https://gist.github.com/
>>>>>> samwillis/af3992f69c2597a16252
>>>>>>
>>>>>> The main use case for this tag is being able to create reusable
>>>>>> 'components' for rendering a website. Say for example a page header, a
>>>>>> panel with headers and footers, or a modal window (as seen in the
>>>>>> Bootstrap
>>>>>> framework). Rather than needing to repeat the html everywhere you need
>>>>>> it
>>>>>> and having to search out all occurrences to make a change to the
>>>>>> structure
>>>>>> you can create a simple template and include it.
>>>>>>
>>>>>> To some extent this can currently be done with either an included
>>>>>> template using the '{% include "template.html" with var="value" %}'
>>>>>> syntax
>>>>>> or using a custom template tag. However, the former isn't suitable for
>>>>>> changing whole blocks in the include template, and the latter can be
>>>>>> overkill and may not be suitable for a designer with little knowledge of
>>>>>> Python and the Django Template API.
>>>>>>
>>>>>> The 'use' tag loads a template and renders it with the current
>>>>>> context similar to the 'include' tag. You can pass additional context
>>>>>> using
>>>>>> keyword arguments as well as override blocks in the included template.
>>>>>>
>>>>>> Example (simple) template:
>>>>>>
>>>>>> <div class="page-heading {{ extra_class }}">
>>>>>> <h1>{% block heading %}{% endblock %}</h1>
>>>>>> </div>
>>>>>>
>>>>>> Example 'use' tag use with the above template:
>>>>>>
>>>>>> {% use "page_header.html" %}
>>>>>> {% block heading %}Some Title{% endblock %}
>>>>>> {% enduse %}
>>>>>>
>>>>>> {% use "page_header.html" with extra_class="large" %}
>>>>>> {% block heading %}Some Title{% endblock %}
>>>>>> {% enduse %}
>>>>>>
>>>>>> As with 'include' use the 'only' argument to exclude the current
>>>>>> context when rendering the included template:
>>>>>>
>>>>>> {% use "page_header.html" only %}
>>>>>> {% block heading %}Some Title{% endblock %}
>>>>>> {% enduse %}
>>>>>>
>>>>>> {% use "page_header.html" with extra_class="large" only %}
>>>>>> {% block heading %}Some Title{% endblock %}
>>>>>> {% enduse %}
>>>>>>
>>>>>> The included template receives an additional context variable called
>>>>>> 'used_blocks' which is a Dict indicating which blocks were overridden in
>>>>>> the 'use' tag. Using this you can conditionally show content around the
>>>>>> block. For example, if you had this template for generating a page
>>>>>> heading:
>>>>>>
>>>>>> <div class="page-heading">
>>>>>> <h1>{% block heading %}{% endblock %}</h1>
>>>>>> {% if used_blocks.sub_heading %}
>>>>>> <h2>{% block sub_heading %}{% endblock %}<h2>
>>>>>> {% endif %}
>>>>>> </div>
>>>>>>
>>>>>> and included it using:
>>>>>>
>>>>>> {% use "page_header.html" %}
>>>>>> {% block heading %}My Page Title{% endblock %}
>>>>>> {% enduse %}
>>>>>>
>>>>>> it would exclude the '<h2>' tags from the empty subheading.
>>>>>>
>>>>>> Finally, as syntactic sugar if you are just overriding a single block
>>>>>> you can express it as:
>>>>>>
>>>>>> {% use "page_header.html" block heading %}
>>>>>> My Page Title
>>>>>> {% enduse %}
>>>>>>
>>>>>> These example are a little simple, but this could be incredibly
>>>>>> useful for more complex components such a modal windows.
>>>>>>
>>>>>> I have made a first pass at an implementation here:
>>>>>> https://gist.github.com/samwillis/af3992f69c2597a16252.
>>>>>>
>>>>>> Although I have implemented this with the 'use' word, there may be a
>>>>>> better word. I considered 'embed' but thought 'use' was a little cleaner
>>>>>>
>>>>>> Thanks,
>>>>>> Sam
>>>>>>
>>>>>> --
>>>>> You received this message because you are subscribed to the Google
>>>>> Groups "Django developers" group.
>>>>> To unsubscribe from this group and stop receiving emails from it, send
>>>>> an email to [email protected].
>>>>> To post to this group, send email to [email protected].
>>>>> Visit this group at http://groups.google.com/group/django-developers.
>>>>> To view this discussion on the web visit
>>>>> https://groups.google.com/d/msgid/django-developers/bb252615-f881-4233-a69b-ae40faf3a694%40googlegroups.com
>>>>>
>>>>> <https://groups.google.com/d/msgid/django-developers/bb252615-f881-4233-a69b-ae40faf3a694%40googlegroups.com?utm_medium=email&utm_source=footer>
>>>>> .
>>>>>
>>>>> For more options, visit https://groups.google.com/d/optout.
>>>>>
>>>>
>>>>
--
You received this message because you are subscribed to the Google Groups
"Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/django-developers.
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-developers/37602593-ebeb-47ff-b0e9-19ca600ae719%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.