Wagtail customizing struct block template on admin - django

I have read the 'custom-editing-interfaces-for-structblock' section of documentation. Then I have written:
settings = StructBlock([
('width', blocks.CharBlock(required=False, max_length=20 ) ),
('background_color', blocks.CharBlock(max_length=10, required=False))
], form_template = 'personal_web/admin_blocks/section_settings.html' )
I have copied the code in wagtail/admin/templates/wagtailadmin/block_forms/struct.html to my custom struct template for better customizing.
--- my section_settings.html ---
<div class="{{ classname }}">
{% if help_text %}
<div class="object-help help">{{ help_text }}</div>
{% endif %}
<ul class="fields">
{% for child in children.values %}
<li{% if child.block.required %} class="required"{% endif %}>
{% if child.block.label %}
<label{% if child.id_for_label %} for="{{ child.id_for_label }}"{% endif %}>{{ child.block.label }}:</label>
{% endif %}
{{ child.render_form }}
</li>
{% endfor %}
</ul>
</div>
On the admin there is an error:
'builtin_function_or_method' object is not iterable... In template /Users/burakk/BurakWorks/Web/VIRTUAL_ENVIRONMENTS/python3.6.1_dj_1_11/lib/python3.6/site-packages/wagtail/wagtailadmin/templates/wagtailadmin/block_forms/sequence_member.html, error at line 23
By the way I am using jinja as template renderer, django 1.11.6, python 3.6
I checked to see if this was an jinja2 issue I have changed the children.values properties children.values() etc...
Now on the admin I see all the elements as 'html string' not the actual input fields...
Thanks

I think this might be happening because your section_settings.html template is inside the location normally used for jinja2 templates - probably some_app/jinja2/personal_web/admin_blocks/section_settings.html (if you're using the configuration shown at http://docs.wagtail.io/en/v1.13.1/advanced_topics/jinja2.html) - and ends up being interpreted as a Jinja2 template. Mixing Jinja2 and Django templates in this way is untested, and likely to fail in unpredictable ways (such as the HTML being double-escaped, as you're seeing).
Try moving it to some_app/templates/personal_web/admin_blocks/section_settings.html, with the template changed back to Django syntax (children.values() changed back to children.values, etc).

Related

Why can't I use this django template variable in conditions?

Following the advice here, I have access to the allowed_contributors variable in the template and I can print it out, but using it in any kind of if-else statement doesn't work. It doesn't give me a 500 error, but it acts like it's empty.
The file I'm loading from templatetags:
from django import template
from django.conf import settings
register = template.Library()
#register.simple_tag
def allowed_contributors():
return getattr(settings, "ALLOWED_CONTRIBUTORS", "")
Here's what I've put in the template (not showing the "load" command at the top, but I guess that must be working).
<div class="container">
<h1>Create new project</h1>
<p> {% allowed_contributors %} </p>
{% if "true" in allowed_contributors %}
<p>"true" found in allowed_contributors!</p>
{% endif %}
{% if "false" in allowed_contributors %}
<p>"false" found in allowed_contributors!</p>
{% endif %}
</div>
The HTML output looks like:
<div class="container">
<h1>Create new project</h1>
<p> ('auth', 'false') </p>
</div>
I've tried outputting the allowed_contributors multiple times in case it's being consumed the first time, but it seems to make no difference.
Do I need to reference it in a different way when I'm using it as a condition for an if statement?
If it helps I'm using Django 1.8
EDIT: Neither of the sensible answers provided worked for me, probably due to some other config on this project that I'm not aware of. I've worked around it by using the slightly more involved context_processor solution.
The same code works for me.
Note: <p> {% allowed_contributors %} </p> needs to be <p> {{ allowed_contributors }} </p>
Maybe that's throwing off your code?
I see
Create new project
('auth', 'false')
"false" found in allowed_contributors!
{% allowed_contributors %}
This does not set a value in the context, it just outputs the result of the tag.
To assign the value, do
{% allowed_contributors as contributors %}
Then you can display the value,
{{ contributors }}
and use it in other tags:
{% if "true" in contributors %}
<p>"true" found</p>
{% endif %}
In Django 1.8 and earlier, you can't do {% allowed_contributors as contributors %}
with the simple_tag decorator. You need to use assignment_tag instead.
#register.assignment_tag
def allowed_contributors():
return getattr(settings, "ALLOWED_CONTRIBUTORS", "")

Access StructBlock in a StreamField of a Page.get_children in Wagtail

I try to render a StreamField of a child page in a Page. I don't manage to render the different StructField within the StreamField. Here is my code
class DefinitionPage(Page):
body = StreamField([
('definition', blocks.StructBlock([
('heading', blocks.CharBlock(label='Titre')),
('paragraph', blocks.RichTextBlock(label='Paragraphe')),
]))
])
content_panels = Page.content_panels + [
StreamFieldPanel('body'),
]
my template. (DefinitionPage is a child of this page.)
{% for post in page.get_children %}
<h2>{{ post.title }}</h2>
{% for block in post.body %}
{% include_block block %}
{% endfor %}
{% endfor %}
post.title is ok but it's like there is no block in post.body.
I tried so many things and {% include_block block %} is certainly wrong. I also tried to add a custom template for the StructBlock without success.
How can I do ? I am using Django 2.0 and wagtail 2.0 (I'm new to wagtail but I read the doc)
Best regards
You need to use page.get_children.specific - get_children only returns the basic Page information common to all page types, which doesn't include the body field in DefinitionPage.
Thank you I changed my code
In the parent PageModel I added:
def get_context(self, request):
context = super().get_context(request)
definitions = self.get_children().specific()
context['definitions'] = definitions
return context
And now in my template:
{% for definition in definitions %}
{% for block in definition.body %}
{% include_block block %}
{% endfor %}
{% endfor %}
I also created a custom template for my definition (simple it's just en test):
{% load wagtailcore_tags %}
<div class="definition">
<h2>{{ value.bound_blocks.heading }}</h2>
{{ value.bound_blocks.paragraph }}
</div>
Thanks a lot
Just last questions: Is there a better practice ? .specific in template or in a custom get_context() ? Is it a good practice to add custom template to StructBlock ?

How to pass HTML to an included template

Using Djangos template language I would like to pass HTML generated through the template language into an included template like in the following simplified example:
field.html
<input name="input_name" />
<div>{{ input_desc_html }}</div>
form.html
{% with input_name="test" input_desc_html=... %}
... should read {% for this in that %} {{ this }} {% endfor %}
{% include 'field.html' %}
{% endwith %}
Obvioulsy there is something missing at ... and I do not know how to advance from here. Is this approach even possible and if yes, how?
If not, what else would you suggest? I thought about using blocks and extending field.html, but it seems to me that this needs every field in its own file - or can I have local templates?

How to add top save button on django admin change list?

I want to have a save button on top of django admin change list page. It seems that django don't have this functionality built-in. The save_on_top option only controls behavior on change form page. Any suggestion is welcome.
In Django 3 (and maybe earlier, not sure) in your custom admin form add save_on_top = True
class MyAdmin(admin.ModelAdmin):
save_on_top = True
First, you need a way to extend the template found at django/contrib/admin/templates/admin/change_list.html. If you don't already know how to do that, check out this answer and this answer.
Next, you need to create your own change_list.html template and put code similar to the following in it. For the sake of simplicity, I've included inline CSS. However, this is bad practice, so you should not do it. Assuming you move the CSS to an external file, you won't need to load admin_static. Lastly, the extends line that you use might not be exactly the same as what I've shown here.
{% extends "contrib/admin/templates/admin/change_list.html" %}
{% load i18n admin_static %}
{% block result_list %}
{% if cl.formset and cl.result_count %}
<div style="border-bottom: 1px solid #ccc; background: white url({% static "admin/img/nav-bg.gif" %}) 0 180% repeat-x; overflow: hidden;">
<p>
<input type="submit" name="_save" class="default" value="{% trans 'Save' %}"/>
</p>
</div>
{% endif %}
{{ block.super }}
{% endblock %}
The {% if %} tag and the <input> tag inside of it is from django/contrib/admin/templates/admin/pagination.html.
The CSS is based on the CSS for #changelist .paginator and is found in django/contrib/admin/static/admin/css/changelists.css.
If you don't mind having pagination links at the top of the page as well, you can do it with a few lines of template code, worksforme in Django 2.0.
Create my_app/templates/admin/my_app/my_model/change_list.html:
{% extends "admin/change_list.html" %}
{% load admin_list %}
{% block result_list %}
{% pagination cl %}
{{ block.super }}
{% endblock %}
This will render the pagination and the save button:
Could benefit from a line or two of CSS, though...

Django variable substitution in the include template tag

I have an internationalized Django 1.3 site and want to do this:
{% include "snippets/button.html" with button_text=_("Logout {{ user.username }} now") %}
And snippets/button.html looks like this:
<button
type="{{ button_type|default:_('submit') %}"
class="all_my special classes"
{% if button_title %} title="{{ button_title }}"{% endif %}>
<span class=ui-button-text>{{ button_text|default:_("Submit") }}</span>
</button>
The only way I can see to do this is something like:
{% include "snippets/button.html" with button_text="Logout "|add:user.username|add:" now" %}
But this is not acceptable as the strings to translate need to include where the variable substitution will occur. I have seen Interpolate Django template include variable but that doesn't cover this usage.
I think your best bet in this case is to add the already translated string to your context.
In your views.py:
...
'button_text': _("Logout {} now").format(user.username),
...
Then in your template:
{% include "snippets/button.html" with button_text=button_text %}
Something like this may let you go on:
{% blocktrans with value|filter as myvar %}
This will have {{ myvar }} inside.
{% endblocktrans %}
from here http://www.djangobook.com/en/1.0/chapter18/
It should work with includes, but I haven't tested.