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 ?
Related
My django admin interface looks like this:
Now I would like to add a view which does not correspond to a model.
I could overwrite the template of above page and add a custom link. But I think this would look ugly.
Example for overwriting admin/index.html:
{% extends "admin/index.html" %}
{% block content %}
{{ block.super }}
<div class="app-sonstiges module">
....
</div>
{% endblock %}
But maybe there is an official way to do add a custom view to the admin interface?
In my case I want to provide a form which can execute tcptraceroute to a remote server. The admin of my app needs this.
I used the same html tags. Now the link "tcptraceroute" looks nice, but unfortunately the messages moved down:
Is there a way to get a custom part like "Sontiges ... tcptraceroute" like in the screenshot, without moving the latest actions down?
Here is how the html structure looks like. My <div class="app-sonstiges"> is below content-main:
You have 3 options here:
Using third-party packages which provide menu
This is pretty straight forward, there are some good packages out there which support menu for admin, and some way to add your item to the menu.
An example of a 3rd party package would be django-admin-tools. The drawback is that it is a bit hard to learn. Another option would be to use django-adminplus but at the time of writing this, it does not support Django 2.2.
Hacking with django admin templates
You can always extend admin templates and override the parts you want, as mentioned in #Sardorbek answer, you can copy some parts from admin template and change them the way you want.
Using custom admin site
If your views are supposed to only action on admin site, then you can extend adminsite (and maybe change the default), beside from adding links to template, you should use this method to define your admin-only views as it's easier to check for permissions this way.
Considering you already extended adminsite, now you can use the previous methods to add your link to template, or even extend get_app_list method, example:
class MyAdminSite(admin.AdminSite):
def get_app_list(self, request):
app_list = super().get_app_list(request)
app_list += [
{
"name": "My Custom App",
"app_label": "my_test_app",
# "app_url": "/admin/test_view",
"models": [
{
"name": "tcptraceroute",
"object_name": "tcptraceroute",
"admin_url": "/admin/test_view",
"view_only": True,
}
],
}
]
return app_list
You can also check if current staff user can access to module before you show them the links.
It will look like this at then end:
Problem here that your div is not inside main-content. I suggest extending admin/index.html
Put in app/templates/admin/index.html
{% extends "admin/index.html" %}
{% load i18n static %}
{% block content %}
<div id="content-main">
{% if app_list %}
{% for app in app_list %}
<div class="app-{{ app.app_label }} module">
<table>
<caption>
{{ app.name }}
</caption>
{% for model in app.models %}
<tr class="model-{{ model.object_name|lower }}">
{% if model.admin_url %}
<th scope="row">{{ model.name }}</th>
{% else %}
<th scope="row">{{ model.name }}</th>
{% endif %}
{% if model.add_url %}
<td>{% trans 'Add' %}</td>
{% else %}
<td> </td>
{% endif %}
{% if model.admin_url %}
{% if model.view_only %}
<td>{% trans 'View' %}</td>
{% else %}
<td>{% trans 'Change' %}</td>
{% endif %}
{% else %}
<td> </td>
{% endif %}
</tr>
{% endfor %}
</table>
</div>
{% endfor %}
<!-- here you could put your div -->
<div class="app-sonstiges module">
....
</div>
<!-- here you could put your div -->
{% else %}
<p>{% trans "You don't have permission to view or edit anything." %}</p>
{% endif %}
</div>
{% endblock %}
The another approach is to add custom app to app_list. But it is far more ugly. So I suggest overriding template.
I assume you are overriding get_urls in AdminSite.
If you need a custom page to handle user input (from form), you may want to give django-etc 1.3.0+ a try:
from etc.admin import CustomModelPage
class MyPage(CustomModelPage):
title = 'My custom page' # set page title
# Define some fields you want to proccess data from.
my_field = models.CharField('some title', max_length=10)
def save(self):
# Here implement data handling.
super().save()
# Register the page within Django admin.
MyPage.register()
I seem to know where the issue is located since I can get around it, but for getting around it I have to sacrifice a function I really want to keep.
Here is the relevant code in the non-working state:
{% if sections %}
{% for item in sections %}
<a class="sections" href="{% url 'sections:generate' item.section.slug %}">{{ item.section.title }}</a>
{% for subsection in item.subsections %}
<p>{{ subsection.title }}</p>
{% endfor %}
{% endfor %}
{% else %}
<p>Error retrieving sections or no sections found</p>
{% endif %}
The problem part above is in the link tag. Let me explain by showing the related view.py:
def index(request):
sections = Section.objects.all()
context = {
'sections': [],
}
for section in sections:
context.get("sections").append(
{
'section': section,
'subsections': get_subsections(section),
}
)
return render(request=request, template_name='index.html', context=context)
So, 'sections' is an iterable list of items, containing for every items a dictionary with two entries. One, 'section' and one 'subsection'. There are multiple subsections for every section, this is what I really want to accomplish.
Ordinarily, when not bothering with subsections and simply iterating over a list of sections works fine. The template code for that would look something like:
{% for section in sections %}
{{ section.title }}
{% endfor %}
NOTE! The code above works just fine! But as soon as I add 'sections' as a list of dictionaries and have to reference the slug by item.section.slug the pages stop rendering.
Please advise.
Try using tuples:
View:
context['sections'] = [(section, tuple(get_subsections(section))) for section in sections]
Template:
{% for section, subsections in sections %}
<a class="sections" href="{% url 'sections:generate' section.slug %}">{{ section.title }}</a>
{% for subsection in subsections %}
<p>{{ subsection.title }}</p>
{% endfor %}
{% endfor %}
Firstly, I'm relatively new to Django, so forgive me if there's some fundamental misunderstanding on my part.
I'm looking to parse some Django template tags that are imported as a part of a Django render. The end issue I'm seeing is the HTML/JS portions are being correctly called and displayed, but the last Django tags are being rendered as plain text on the page. I realize it's because I'm rendering the view before one step too early, but I'm not sure how to go about this correctly.
Here's the basic call stack (simplified):
template1
{% extends base.html %}
{% block main_content %}
{{ block.super }}
<div id="col1" class="chart-col" style="float: left; width: 33%;">
{% block col1 %}
{% endblock col1%}
{% endblock main_content %}
template2:
{% extends template1.html %}
{% block main_content %}
{% for DATA in DATALIST %}
{{ DATA|safe }}
{% endfor %}
{% endblock main_content %}
python code that generates desired Django / HTML for the above DATALIST in template2:
def chartdisplay(block_var, othervars):
text = "{% block block_var %} \n {{ block.super }} \n"
text += "<html to generate data display goes here>"
text += "{% endblock block_var %}
return text
the above python is passed to the Django View.py:
def dataview(request):
datapull = model.object.filter(**kwargs) #generic data pull
#date-time handler
dthandler = lambda obj: (
obj.isoformat()
if isinstance(obj, datetime.datetime)
or isinstance(obj, datetime.date)
else obj)
data = {'data': datapull}
chart_html = [
chartdisplay(block_var="col1", other_unique_vars),
chartdisplay(block_var="col2", other_unique_vars),
chartdisplay(block_var="col2", other_unique_vars),
chartdisplay(block_var="col3", other_unique_vars)
]
context = {'data', jsondump(data, default=dthanlder), 'charts': charts_html)
return render(request, 'path/to/template2.html', context)
Now, the main issue is the resulting HTML that is displayed displays the HTML and JS fine, but has the Django template tags exposed as text and does not direct the chartdisplay into the desired divs. E.g. template2.html is viewed as:
{% block col1 %} {{ block.super }}
<html/D3 chart is displayed fine>
{% endblock col1 %}
{% block col2 %} {{ block.super }}
<html/D3 chart is displayed fine>
{% endblock col2 %}
{% block col2 %} {{ block.super }}
<html/D3 chart is displayed fine>
{% endblock col2 %}
{% block col3 %} {{ block.super }}
<html/D3 chart is displayed fine>
{% endblock col3 %}
I've tried a few various band-aid fixes but I'd rather just understand how to do this properly. The one thing I've tried but am probably misunderstanding is, before the return render step:
def dataview(request):
...
rendered = render_to_string('path/to/template12.html', context, context_instance=RequestContext(request))
return render(request, 'path/to/template2.html'/, rendered)
Thanks for you help. Please let me know if any clarification is needed!
You don't have to use render, it's just a shortcut that takes a request, template name and context, and returns an http response with the rendered context.
That's not what you want in this case. You have a template string rendered that you wish to render. The low level API to render a template is explained here in the docs.
from django.template import Context, Template
from django.http import HttpResponse
def dataview(request):
...
rendered = render_to_string('path/to/template2.html', context)
template = Template(rendered)
return HttpResponse(template.render(Context(context)))
You may have to use the verbatim tag so that your blocks and extends tags are not evaluated when you call render_to_string on the first template. For example
{% verbatim %}
{% extends template1.html %}
{% block main_content %}
{% endverbatim %}
{% for DATA in DATALIST %}
{{ DATA|safe }}
{% endfor %}
{% verbatim %}
{% endblock main_content %}
{% endverbatim %}
This seems like it should be pretty straightforward, but for some reason I am unable to solve this problem. I'm using Django 1.4. I am trying to do a basic check to see if a list QuerySet is empty or not during template rendering, but the if statement I'm using seems always to evaluate to true.
I have a Django template that reads:
{% extends 'includes/base.html' %}
{% if object_list %}
...
{% block data %}
{% for object in object_list %}
...
{{ object.create_date }}
...
{% endfor %}
{% endblock data %}
...
{% endif %}
'base.html' has the block:
<body>
{% block content %}
...
<div class="row-fluid">
<div class="span12">
{% block data %}
<div align="center"><i>No data.</i></div>
{% endblock data %}
</div><!-- span12 -->
</div><!-- row -->
{% endblock content %}
...
</body>
The view function generating the QuerySet is here:
def barcode_track(request, model):
query = request.GET.get('barcode_search', '')
object_list = model.objects.all()
if query:
object_list = model.objects.filter(barcode__icontains=query)
return render_to_response('barcode_track/barcode_list.html',
{'object_list': object_list, 'query': query},
context_instance=RequestContext(request))
Which is called via this form:
<form id="barcode_search_form" method="get" action="" class="form">
<input type="text" name="barcode_search" value="{{ query }}" />
<button type="submit" class="btn">Search</button>
</form>
And the urls.py line:
urlpatterns = patterns('barcode_track.views',
url(r'^$', 'barcode_track', {'model': Barcode},
name="barcode_track"),)
The idea is that results will only be presented if they exist in object_list, and otherwise the parent block will remain unaltered. I have tried changing the name of object_list, and I have printed {{ dicts }} to the page to ensure that object_list is, in fact, empty (which it is). I am not using a generic view, although I realize that the name suggests as much. I have actually had this trouble in a different app I wrote using similar logic, so I must be doing something systematically incorrectly.
What am I missing here?
You can't wrap control flow tags like if around a block. Your problem is that the child template's definition for block data is being used simply because it's there.
You can fix it by placing the if tag inside block data. If you want to inherit the parent's contents when the list is empty, add an else case that expands to {{ block.super }}.
Given is a model called "comment" with a foreign key relationship to a model called "task".
{% for task in tasks %}
{% for comment in task.comment_set.all %}
{{ comment }}
{% endfor %}
...
What is the best way to limit this to 5 comments like:
Entry.objects.all()[:5]
{% for task in tasks %}
{% for comment in task.comment_set.all|slice:"5" %}
{{ comment }}
{% endfor %}
{% endfor %}
You don't. You should not do "real work" in a template, this breaks the MVC pattern.
Do the real work in the view, and pass the data to the template (using the context dictionary).
def handle_comments(request):
tasks = Task.objects.all()
comments = {}
for task in tasks:
comments[task] = task.comment_set.all()[:5]
return render_to_response('commenting.html', {'comments': comments})
You can then iterate over the comments in your template:
{% for task, task_comments in comments.items %}{{ task }}{% endfor %}