Unable to render Listblock inside StreamBlock in template Wagtail - python

I have StructBlock inside a Listblock, all of which sit insde a class BannerBlock as follows:
class BannerBlock(blocks.StreamBlock):
"""
Blocks for displaying individual banners
"""
banners = blocks.ListBlock(
blocks.StructBlock(
[
('image', ImageChooserBlock(required=True)),
('title', blocks.CharBlock(required=True, max_length=128)),
('description', blocks.CharBlock(required=True, max_length=1024)),
('link', blocks.URLBlock(required=False)),
]
)
)
class Meta:
template = "home/banner_block.html"
icon = "placeholder"
label = "Banners"
The template Banner_block is as follows:
{% load wagtailimages_tags %}
<!-- Banner -->
<div id="banner">
{% for banner in self.banners %}
<article data-position="bottom right">
<div class="inner">
{% image banner.image original as image %}
<img src="{{ image.url }}" alt="">
<div class="features">
<a href="{{ banner.link.url }}" class="c-accent alt no-bg">
<h2>{{ banner.title }}</h2>
<p>{{ banner.description }}</p>
</a>
</div>
</div>
</article>
{% endfor %}
</div>
Finally, I am trying to render each Banner (Listblock) as follows:
{% for block in page.banners_collection %}
{% include_block block %}
{% endfor %}
However, Each Listblock is rendered as plain text as list items.
How do I render each item correctly?
Thank you.

I'm assuming the banners_collection field on your page model is defined as:
banners_collection = StreamField(BannerBlock())
where you're specifying a StreamBlock to use at the top level of the StreamField, rather than the more basic setup of
some_stream = StreamField([
('first_block_type', some_block_definition),
('another_block_type', some_block_definition),
])
where you pass a list of blocks for StreamField to make a stream from. This means that when you access page.banners_collection, it will give you a single instance of a BannerBlock value. When you loop over this value with for block in page.banners_collection, you're actually getting back the ListBlock values in your stream, and rendering them individually with include_block - bypassing the template you set up for BannerBlock. Your ListBlock values don't have a template of their own, so they end up using the default rendering.
If you call include_block on the BannerBlock value as a whole:
{% include_block page.banners_collection %}
then it will use your template. However, there's a secondary problem - in the line
{% for banner in self.banners %}
self.banners is not valid, because a stream value behaves as a sequence, not a dictionary. You can't look up the banners children by name - you need to loop over the list of children finding the ones that are of type banners. (Except, in your case, you can skip that check because banners is the only available block type within the stream). The correct template code would look like:
<div id="banner">
{% for block in self %}
{# the 'block' object here has properties block_type (which is always 'banners' here) and value #}
{% for banner in block.value %}
{# banner is a StructBlock value, with image, title, description, link properties #}
<article data-position="bottom right">
<div class="inner">
{% image banner.image original as image %}
...
</div>
</article>
{% endfor %}
{% endfor %}
</div>
However, are you sure you need the ListBlock in addition to the StreamBlock? A StreamBlock is already a sequence of blocks, so a ListBlock inside a StreamBlock gives you a list of lists of banners. If you simplified the BannerBlock value to
class BannerBlock(blocks.StreamBlock):
"""
Blocks for displaying individual banners
"""
banner = blocks.StructBlock(
[
('image', ImageChooserBlock(required=True)),
('title', blocks.CharBlock(required=True, max_length=128)),
('description', blocks.CharBlock(required=True, max_length=1024)),
('link', blocks.URLBlock(required=False)),
]
)
class Meta:
template = "home/banner_block.html"
icon = "placeholder"
label = "Banners"
then BannerBlock as a whole would be a list of banners, and the template would become
<div id="banner">
{% for block in self %}
{# block.value is now a StructBlock value #}
<article data-position="bottom right">
<div class="inner">
{% image block.value.image original as image %}
...
</div>
</article>
{% endfor %}
</div>

Related

How to get dictionary value of a key inside a loop in Django Template?

views.py
def get(self, request, *args, **kwargs):
domains = Domain.objects.all()
context['domains'] = domains
domain_dict = {}
# ..........
# ..........some codes here for domain_dict dictionary
print(domain_dict)
context['domain_dict'] = domain_dict
return render(request, self.response_template, context)
Output after printing the domain_dict
{4: '', 3: '', 1: '', 5: '', 7: '', 2: '', 6: 'Are you a candidate for the engineering admission '}
Now the domain_dict is sent to template through context.
templates.html
<div class="col-md-8">
<div class="tab-content">
{% for domain in domains %}
{% with domain_id=domain.id %}
<div class="tab-pane container p-0 {% if forloop.first %} active {% endif %}" id="services{{domain.id}}">
<div class="img" style="background-image: url('static/counsellor/images/service-1.png');">
</div>
<h3>Name: {{domain.name}} ID: {{domain_id}}</h3>
<p>{{domain_dict.6}}</p>
</div>
{% endwith %}
{% endfor %}
</div>
</div>
In the above template I use <p>{{domain_dict.6}}</p>. domain_dict.6 to find the value of key 6. It returns perfectly.
Outputs: Are you a candidate for the engineering admission.
But in the below
<div class="col-md-8">
<div class="tab-content">
{% for domain in domains %}
{% with domain_id=domain.id %}
<div class="tab-pane container p-0 {% if forloop.first %} active {% endif %}" id="services{{domain.id}}">
<div class="img" style="background-image: url('static/counsellor/images/service-1.png');">
</div>
<h3>Name: {{domain.name}} ID: {{domain_id}}</h3>
<p>{{domain_dict.domain_id}}</p>
</div>
{% endwith %}
{% endfor %}
</div>
</div>
In the above template I use <p>{{domain_dict.domain_id}}</p>. domain_dict.domain_id to find the value of key domain_id. It returns null. Here domain_id = 4,3,1,6,5,7,2
Outputs: null
How can I return the value of a key of the dictionary here?
I've recently run into the same problem. Here is example of the solution.
Say, we have tuple
people = ('Vasya', 'Petya', 'Masha', 'Glasha')
and dictionary with their status:
st = {
'Vasya': 'married',
'Petya': 'divorced',
'Masha': 'married',
'Glasha': 'unmarried'
}
We transfer these to our template as context of corresponding view:
context['people'] = people
context['st'] = st
If we write in template
{% for person in people %}
{{ person }} is {{ st.person }}
{% endfor %}
then it won't work. Well, 'person' will be displayed, but data from dictionary will not. Because :
Note that bar in a template expression like {{ foo.bar }} will be
interpreted as a literal string and not using the value of the
variable bar, if one exists in the template context.
The solution of the problem is either to use another data structure or to use custom filter in the template. The idea of the latter is to add a filter to your dictionary in the template, which takes current value of person and returns corresponding value of the dictionary.
To do this,
create in the folder with your app new folder templatetags,
write the path to this new folder in settings.py,
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
...,
os.path.join(BASE_DIR, 'your_app/templatetags'),
],
...
}
inside the templatetags create new file, say, filter.py with our new filter called dict_value:
from django import template
register = template.Library()
#register.filter
def dict_value(d, key):
return d[key]
go back to your template and load newly created filter somewhere in first lines:
{% load filter %}
rewrite you template code like this:
{% for person in people %}
{{ person }} is {{ st|dict_value:person }}
{% endfor %}
Now it works as needed.

I can't display posts only with a boolean field set to true in my template

In short - I have a bootstrap carousel and it works nicely, however I can't get it to display only fields with 'featured' set to 'true'
I have tried doing for post in posts.objects.featured (the carousel literally does not show up at all then) and variations like posts.objects.filter(featured=True) (it says it can't parse the remainder).
Here's the code from the template where I am trying to display the carousel image only with items with featured=True
{% for post in posts.objects.featured %}
<div class="carousel-item {% if forloop.first %}active{% endif %} ">
{% image post.image fill-1920x500 %}
<div class="carousel-caption d-none d-md-block">
<h2 id="inner-carousel-title">{{post.title}}</h2>
<h4><a href="{% pageurl post %}" style="color:white;text-shadow:2px 2px 4px #000000" >something</a></h4>
</div>
</div>
{% endfor %}
Again, I just want the carousel to show up only with featured posts
As a side note- it'd be awesome if it only showed 3 posts.
EDIT - Here's my model.py for the page
class BlogPage(RoutablePageMixin, Page):
description = models.CharField(max_length=240, blank=True)
content_panels = Page.content_panels + \
[FieldPanel("description", classname="full")]
def get_context(self, request, *args, **kwargs):
context = super(BlogPage, self).get_context(request, *args, **kwargs)
context['posts'] = self.posts
context['blog_page'] = self
return context
If you really want to do this in the template do:
{% for post in posts %}
{% if post.featured %}
<div> ... <div/>
{% endif %}
{% endfor %}
But you can also pass only the featured posts to your template in your view. Just add:
...
featured_posts = Post.objects.filter(featured=True)[:4]
return render('post_list.html', {'featured_posts': featured_posts, ...})
If you’re using Django’s generic ListView and you’re only showing the featured posts, you can set the queryset property to filter only the featured posts. If you’re also showing the other posts in your ListView, add the featured_posts to your context by overriding get_context_data().
You can try this if the way I suggested in the comment doesn't work
{% for post in posts %}
{% if post.featured %}
// write down your stuff
{% endif %}
{% endfor %}

Django How can I cache properly to reuse data?(with using django-mptt package and redis module)

I am making kind of dynamic menu. when you click menu on the top, it show sub menu on the left side. I searched with keyword 'dynamic menu' from stackoverflow and google. I got idea to build that kind of menu. I made it like below.
1) render data(menu list) in context to template by custom context processor.
2) using custom template tag which is provided by django-mptt package.
3) show top menu in base template.
4) move to another template to show sub menu according to what top menu you click
I made custom context_processor to use menu in context in every template.
context_processor.py
from manager.models import Menu
def menu(request):
menu_list = list(Menu.objects.all())
return {'menu':menu_list}
template.py(example)
{% load mptt_tags %}
<nav id="{{ menu_id }}" class="tree-menu">
<ul>
{% recursetree menu %}
<li class="menu
{% if node.is_root_node %}root{% endif %}
{% if node.is_child_node %}child{% endif %}
{% if node.is_leaf_node %}leaf{% endif %}
{% if current_menu in node.get_descendants %}open{% else %}closed{% endif %}
">
{{ node.menu_name }}
{% if not node.is_leaf_node %}
<ul class="children">
{{ children }}
</ul>
{% endif %}
{% if node.items and node.items.exists %}
<ul class="items">
{% for item in node.items.all %}
{% if item_template %}
{% include item_template %}
{% else %}
{% include "menu/tree-item.html" %}
{% endif %}
{% endfor %}
</ul>
{% endif %}
</li>
{% endrecursetree %}
</ul>
</nav>
mptt_tags.py
#register.tag
def recursetree(parser, token):
"""
Iterates over the nodes in the tree, and renders the contained block for each node.
This tag will recursively render children into the template variable {{ children }}.
Only one database query is required (children are cached for the whole tree)
Usage:
<ul>
{% recursetree nodes %}
<li>
{{ node.name }}
{% if not node.is_leaf_node %}
<ul>
{{ children }}
</ul>
{% endif %}
</li>
{% endrecursetree %}
</ul>
"""
bits = token.contents.split()
if len(bits) != 2:
raise template.TemplateSyntaxError(_('%s tag requires a queryset') % bits[0])
queryset_var = template.Variable(bits[1])
template_nodes = parser.parse(('endrecursetree',))
parser.delete_first_token()
return RecurseTreeNode(template_nodes, queryset_var)
My Question
If you see django manual about QuerySet, it says that "Each QuerySet contains a cache to minimize database access". It is obvious that, if you query same data in certain rule, it doesn't seem hit database again but return result from cache. Then I am querying Menu.objects.all() in custom context processor. This result(menu_list = Menu.objects.all()) will be in context every time, you can use menu data on every template repeately. So does it reuse the result from cache without hitting database again?
If menu_list = Menu.objects.all() in custom context processor hit database every time whenever template load this menu list, Does it work in this way to reuse menu data from cache without hitting database everytime?
context_processors.py
from manager.models import Menu
from django.core.cache import cache
def menu(request):
menu_list = cache.get_or_set('menu_list', list(Menu.objects.all()))
return {'menu':menu_list, 'redis':"Food"}
Lastly, I don't know if there are many people using django-mptt package. I guess just a few people have experience using it in person. It says "Only one database query is required (children are cached for the whole tree)" so does it mean if I use django-mptt package and get menu from it on template, it automatically cache its data?
Well, I am not clear about django cache system.
It would be really appreciate if you can give me answer and insight for my questions. Thanks for reading!

webapp2, Jinja2: how to cut large html file into multiple html files

When I blog, I like to separate each blog-post into its own .html file (is that ok?)
This prevents the file getting too big, and makes it easy to go back and edit a previously written blog post if need be.
Occasionally the blog post will contain css/js/ajax/template variables.
But on my website, I like all the blog posts on one page (so I can scroll through them all, instead of going to a separate page for each post)
Here is an html file that contains two blog posts:
{% extends "base.html" %}
{% block blog_posts %}
<!-- links/targest for the side menu to jump to a post -->
<li>Post2 - April 2012</li>
<li>Post1 - Feb 2012</li>
{% endblock %}
{% block content %}
<div id="post1">
spam1 blah blah
</div>
<div id="post2">
spam2
</div>
{% endblock %}
and in base.html I have something like:
<div id="content-container">
<div id="section-navigation">
<ul>
{% block blog_posts %}
{% endblock %}
</ul>
</div>
<div id="content">
{% block content %}{% endblock %}
</div>
</div>
What is the best way for me to split these blog posts out into separate files using webapp2 and jinja2?
e.g. blog1.html might look like:
{% block blog_posts %}
<!-- links/targest for the side menu to jump to a post -->
<li>Post1 - Feb 2012</li>
{% endblock %}
{% block content %}
<div id="post1">
spam1 blah blah
</div>
{% endblock %}
(And I would want the links and the blogposts to be displayed in the right order on the website)
I could think of a way of doing it where post2 extends post1.html, post3 extends post2.html etc, but I would prefer to fan out more
"Henry and Kafura introduced Software Structure Metrics Based on Information Flow in 1981[2] which measures complexity as a function of fan in and fan out."
Thanks
#robert king, your design has data embedded directly in the template. Templates should only contain the blueprint to a view, and they should be rendered with new data generated from your main code every time. I simulate this process here (Edited to illustrate the use of a loop to extract post titles, and the display of a single post.):
import jinja2
# NOTE: in this template there is no data relating to specific posts.
# There are only references to data structures passed in from your main code
page_template = jinja2.Template('''
<!-- this is a navigation block that should probably be in base.html -->
{% block blog_posts %}
<!-- links/targets for the side menu to jump to a post -->
{% for post in posts %}
<li><a href="{{ post.url }}">{{ post.title }}
- {{ post.date }}</a></li>
{% endfor %}
{% endblock %}
<!-- this is a content block that should probably be in page.html -->
{% block content %}
<div id="post">
<h1>{{ current.title }}</h1>
<h2>{{ current.date }}</h2>
<p>{{ current.content }}</p>
</div>
{% endblock %}
''')
# NOTE your main code would create a data structure such as this
# list of dictionaries ready to pass in to your template
list_of_posts = [
{ 'url' : '#post1',
'title' : 'My first post',
'date' : 'Feb 2012',
'content' : 'My first post is about Hello World.'},
{ 'url' : '#post2',
'title' : 'My second post',
'date' : 'Apr 2012',
'content' : 'My second post is about Foo Bar.'}
]
# Pass in a full list of posts and a variable containing the last
# post in the list, assumed to be the most recent.
print page_template.render(posts = list_of_posts,
current = list_of_posts[-1])
Hope this helps.
EDIT See also my answer to a question on "Site fragments - composite views"
I just found another option in the jinja2 tutorial. I think it makes more sense for my handler to pass my template a list of filenames of blog posts, and then to include the blog posts.
include - returns the rendered contents of that file into the current namespace:
{% include 'header.html' %}
<div ...
{% include 'footer.html' %}
Included templates have access to the variables of the active context by default. For more details about context behavior of imports and includes see Import Context Behavior.
From Jinja 2.2 onwards you can mark an include with ignore missing in which case Jinja will ignore the statement if the template to be ignored does not exist. When combined with with or without context it has to be placed before the context visibility statement. Here some valid examples:
{% include "sidebar.html" ignore missing %}
{% include "sidebar.html" ignore missing with context %}
{% include "sidebar.html" ignore missing without context %}
New in version 2.2.
You can also provide a list of templates that are checked for existence before inclusion. The first template that exists will be included. If ignore missing is given, it will fall back to rendering nothing if none of the templates exist, otherwise it will raise an exception. Example:
{% include ['page_detailed.html', 'page.html'] %}
{% include ['special_sidebar.html', 'sidebar.html'] ignore missing %}
When I read the raw html file (file.read()) and passed the data to my template, it escaped all the html.
instead of {{data}} i had to use {{data|safe}} which allowed raw html.
something like:
class HomeHandler(BaseHandler):
def get(self):
file_names = sorted(os.listdir('blog_posts'))
html = [open('blog_posts/%s' % fn).read() for fn in file_names]
templates = {'html': enumerate(html)}
self.render_template('home.html', **templates)
{% block content %}
{% for num,data in html %}
<div id="post{{num}}">
{{data|safe}}
</div>
<br />
<img src="http://www.sadmuffin.net/screamcute/graphics/graphics-page-divider/page-divider-007.gif" border=0>
<br />
{% endfor %}
{% endblock %}
(make sure the directory isn't a static directory)

Highlighting link of currently opened category tab in css

I am trying to highlight a link of a currently opened category tab, here is what i have already done:
globs.py
def globs(request):
cats = Category.objects.all()
return {'cats': cats}
views.py
def news_by_category(request, slug):
c = Category.objects.get(slug=slug)
news = News.objects.filter(category=c, status='p').order_by('-id')
#news = c.news_set.all().order_by('-id')
return object_list(
request,
news,
paginate_by = 5,
extra_context = {'c':c},
template_name = 'news_by_category.html')
base.html #bodyclass
<body class="{% block bodyclass %}{% endblock %}">
news_by_category.html
{% block bodyclass %}{{c|cut:" "}}{% endblock %}
base.html
<li><h4>Categories:</h4></li>
{% for i in cats %}
<li class="{{i.name|safe|cut:" "}}_li">
{{ i.name }}
</li> {% endfor %}
What i need to do now is to create style for every category, in category list, I could achieve this easily by styling inside a html file, but I'm not sure wether that would be proper (Would it?). I came up with some css styling,
{% for i in cats %}
body.{{ i|safe|cut:" "}} li.{{i|safe|cut:" "}}_li {
color: red;
}
but as I can't use django template tags inside my .css file, this wont work.
My questions:
1) How could i make this css file work for me. Any chance for a little step by step?
2) If I failed step1, how improper would it be to style those few li elements inside html file?
EDIT: /trying another way
I tried using:
base.html
{% for i in cats %}
<li class="{% ifequal 'request.get_full_path' '/k/{{ i.slug }}/' %}active{% endifequal %}">
{{ i.name }}
</li> {% endfor %}
.css
.active {{color:red;}
When i compared {{ request.get_full_path }} and /k/{{i.slug}}/ both returned same thing... but if its inside ifequal it doesnt seem to work.
You can create a simple class named "active" or something along those lines and add it to the current tab. Then, in your CSS you apply the active styles to that class. So you just append the active class and it'll automatically take the active style.
If you have a url:
{% url app:home i.slug as home %}
<li {% ifequal request.get_full_path home %}class="active"{% endifequal %}>

Categories

Resources