syntax in wagtail HTML template file - wagtail

In below HTML template of Wagtail,
<h1>{{ page.title }}</h1>
<div>{{ page.intro }}</div>
{% for post in page.get_children %}
<h2>{{ post.title }}</h2>
<p>{{ post.first_published_at }}</p>
<p>{{ post.owner }}</p>
{% if post.specific.rncategory.all.count %}
{% for ctgy in post.specific.rncategory.all %}
{{ctgy.category.name}}
{% endfor %}
{% endif %}
{% for tg in post.specific.rntags.all %}
<button type="button">{{tg.tag.name}}</button>
{% endfor %}
{% endfor %}
Q1: Why we use page.get_children & post.specific.rncategory.all.count instead of page.get_children() and post.specific.rncategory.all().count() which are expected to be corrected ?
And It is the other way around (page.get_children is invalid) in the PDB debug interaction session.
(Pdb++) obj.get_children()[0]
<Page: Detail>
(Pdb++) obj.get_children()[0].specific.rncategory.all.count
*** AttributeError: 'function' object has no attribute 'count'
(Pdb++) obj.get_children()[0].specific.rncategory.all
<bound method BaseManager.all of <modelcluster.fields.create_deferring_foreign_related_manager.<locals>.DeferringRelatedManager object at 0x7f7076003790>>
(Pdb++) obj.get_children()[0].specific.rncategory.all()
<QuerySet []>
(Pdb++) obj.get_children()[1].specific.rncategory.all()
<QuerySet [<Link_PostDetail_Category: Link_PostDetail_Category object (2)>]>
(Pdb++) obj.get_children()[1].specific.rncategory.all().count()
1
(Pdb++) obj.get_children()[0].specific.rncategory.all().count()
0
(Pdb++)

This is the normal behaviour of the Django template language - by design, Django template code is not equivalent to Python.
Django template syntax never uses () for function calls. Instead, whenever a variable lookup results in a callable, it will automatically be called with no arguments. See "Behind the scenes" under https://docs.djangoproject.com/en/stable/ref/templates/language/#variables.

Related

How can you loop through all of the children of a model?

In Django I am trying to loop through all of the children of my Todo model. But whenever I try to run it, it gives me an AttributeError that says "Manager isn't accessible via Todo instances". My code looks like this:
{% extends 'base.html' %}
{% block content %}
<h3>Tasks</h3>
{% for t in model.objects.all %} <!-- Error -->
<p>{{t.name}}</p>
{% endfor %}
{% endblock %}
{% block options %}
<li class="bg-light py-3 w-100 px-4 rounded text-nowrap fs-4">
<button class="text-decoration-none text-dark">Save</button>
</li>
{% endblock %}
I tried to just put the model in the context, and then I got an error in the HTML, so I figured out that it happened when I tried to reference 'model.objects.all'.
Inside of your view you need to specify what django should pass to the template. It does not serve the entire Database; therefore queries like in model.objects.all inside of your templates are not allowed.
Specify the queryset inside your views.py:
def todo_view(request):
context = {}
context['my_todos'] = my_todo_model.objects.all()
context['most_important_todo'] = my_todo_model.objects.get(pk=1)
# put your own logic inside the `.get` method above
return render(request, 'my_template.html' context)
And then access it inside of your template like so:
{% for t in my_todos %}
<p>{{ t.name }}</p>
{% endfor %}
{{ most_important_todo.name }}
Because we put a queryset inside of my_todos we can loop over it in the template. most_important_todo ist just a single object passed to the template, so we can access its properties (e.g. the name) directly.
Let me know how it goes

Jekyll / liquid for loop being mysterious

I am trying to use the for loop in Jekyll to render sample data. I can reference the markdown objects or whatever they are called directly, but when I use a for loop it doesn’t work. Here’s an example of the problem I am having:
markdown file with data:
tests:
test1:
number: 1 .
test2:
number: 2 .
test3:
number: 3 .
sessions.html:
<p>{{ page.tests.test1.number }}</p>
<p>{{ page.tests.test2.number }}</p>
<p>{{ page.tests.test3.number }}</p>
This outputs to:
<p>1</p>
<p>2</p>
<p>3</p>
If I do this instead:
{% for test in page.tests %}
<p>{{ test.number }}</p>
{% endfor %}
I get:
<p></p>
<p></p>
<p></p>
I can even do this:
{% for test in page.tests %}
<p>{{ test }}</p>
{% endfor %}
And get:
<p>test1{"number"=>1}</p>
<p>test2{"number"=>2}</p>
<p>test3{"number"=>3}</p>
Any idea what I am doing wrong?
Take a look at this:
{% for test in page.tests %}
<p>{{ test.number }}</p>
{% endfor %}
You are iterating over page.tests. Since page.tests is a hash and not a list, you are iterating over the key-value pairs of that object. (See this topic).
The keys are test1, test2 and test3, you can access them with test[0].
But you need the value, in this case you can use test[1], and to get the number, you can call test[1].number:
{% for test in page.tests %}
<p>{{ test[1].number }}</p>
{% endfor %}
And you will get the same output as in your first example.
Or, instead of test1, test2 and test3, you may try to define a list:
tests:
- number: 1
- number: 2
- number: 3
This way, your code will work too:
{% for test in page.tests %}
<p>{{ test.number }}</p>
{% endfor %}

Wagtail Show latest blog posts on Homepage through a streamfield

I have 3 mains sections in my site, homepage, blog index, and blog specific. I am using the streamfield function in wagtail to order various sections in the homepage. One of those sections is for the latest three blog posts.
I have done this for the blog index page, but can't grab the latest blog posts in the streamfield.
My model looks like this
class CaseStudiesIndex(Page):
def casestudies(pages):
casestudies = CaseStudyPage.objects.all().order_by('-first_published_at')
return casestudies
intro = RichTextField(blank=True)
content_panels = Page.content_panels + [
FieldPanel('intro', classname="full")
]
class LatestPosts(blocks.StructBlock):
static = blocks.StaticBlock(admin_text='Latest posts: no configuration needed.',)
def casestudies(pages):
casestudies = CaseStudyPage.objects.all().order_by('-first_published_at')[:3]
return casestudies
class Meta:
icon = 'doc-full'
label = 'Latest Posts'
template = 'blocks/_latestPosts.html'
class HomePage(Page):
blocksbody = StreamField([
('lead_banner', LeadBanner()),
('latest_posts', LatestPosts()),
('team', Team())
],null=True,blank=True)
content_panels = Page.content_panels + [
StreamFieldPanel('blocksbody'),
]
In my block folder I am calling the file fine and it renders the wrapper fine but I can't grab any of the data, I have tried a bunch of ways but nothing returns.
{% load wagtailcore_tags wagtailimages_tags %}
{% load static %}
<section>
<div class="wrapper__inner">
<ul>
{% for case in self.casestudies %}
{{case.title}}
{% endfor %}
{% for case in self.case_studies %}
{{case.title}}
{% endfor %}
{% for case in self.latest_posts %}
{{case.title}}
{% endfor %}
{% for case in page.casestudies %}
{{case.title}}
{% endfor %}
{% for case in page.case_studies %}
{{case.title}}
{% endfor %}
{% for case in page.latest_posts %}
{{case.title}}
{% endfor %}
</ul>
</div>
</section>
For the Blog Index page that does work I do the following.
{% extends "inner_base.html" %}
{% load wagtailcore_tags %}
{% block body_class %}template-case-studies{% endblock %}
{% block content %}
<section>
<div class="wrapper__inner">
<h1>{{self.title}}</h1>
<ul>
{% include "blocks/CaseStudiesLatestBlock.html" %}
</ul>
</div>
</section>
{% endblock %}
And the CaseStudiesLatestBlock.html which works fine looks like
{% load wagtailcore_tags wagtailimages_tags %}
{% load static %}
{% for case in self.casestudies %}
<li>
<strong>{{ case.title }}</strong>
</li>
{% endfor %}
Defining your own methods on a StructBlock won't work - the self (or value) variable you receive on the template is just a plain dict, not the StructBlock object itself. (This might seem counter-intuitive, but it's consistent with how blocks work in general: just as a CharBlock gives you a string value to work with and not a CharBlock instance, StructBlock gives you a dict rather than a StructBlock instance.)
Instead, you can define a get_context method (as documented here) to provide additional variables to the template:
class LatestPosts(blocks.StructBlock):
static = blocks.StaticBlock(admin_text='Latest posts: no configuration needed.',)
def get_context(self, value, parent_context=None):
context = super(LatestPosts, self).get_context(value, parent_context=parent_context)
context['casestudies'] = CaseStudyPage.objects.all().order_by('-first_published_at')[:3]
return context
You can then access the casestudies variable in the template, e.g. {% for case in casestudies %}.

looping over jekyll collections

I have the following code
<div>
{% for note in site.regnotes %}
{% if note.regulationno == page.regulationno %}
<p>
{{ note.regulationno }} - {{ note.url }}
</p>
{% endif %}
{% endfor %}
</div>
This code loops over the regnotes collection in a jekyll site, checks if the current note regulationno is the same as the page regulationno and if so displays the regulationno and url - that is the url of the current page. How do I change this code to include the url of the previous page, the current page and the next page. I'm looking for three urls - previous, current and next? - The "page.previous.url" variable within jekyll does not appear to work in collections.
This is what it might look like in other code
for i=1 to number of items in the regnotes collection
if current note == page note
print page[i].url //current page url
print page[i-1].url //previous page url
print page[i+1].url //next page url
end if
end for
I suppose what I'm trying todo is reference the items in the collection by their array index. just can't seem to get the syntax correct.
Since you are a programmer, you just need to know that you need to use forloop.index0 to know where you are in the for loop (https://docs.shopify.com/themes/liquid-documentation/objects/for-loops#index0).
The code will be something like:
<div>
{% for note in site.regnotes %}
{% assign current_index = forloop.index0 }}
{% assign next_index = current_index | plus: 1 %}
{% assign prev_index = current_index | minus: 1 %}
{% if note.regulationno == page.regulationno %}
<p>
{{ note.regulationno }} - {{ note.url }}
</p>
{% if site.regnotes[prev_index] %}prev{% endif %}
{% if site.regnotes[next_index] %}next{% endif %}
{% endif %}
{% endfor %}
</div>

Jinja2 in google appengine extended template repeates base templete

Ok I have a base.html and I try to use that for my header menu and footer. In my other template I loop over items and display them on the page. My problem is the the other template is repeating my base.html like it's in the loop. I hope someone can show me the error in My ways.
Here is my base.html code:
<div class="menu">
<ul class="nav">
<li>Home</li>
<li>New Entry</li>
<li>Sign-up</li>
{% if user %}
<li>{{user.name}}</li>
<li>Log-Out</li>
{% else %}
<li>Log-In</li>
{% endif %}
</ul>
​
This is in the base.html also but didn't paste correctly.
<div id="content">
{% block content %}
{% endblock %}
</div>
And here is the sub template code:
{% extends "base.html" %}
{% block content %}
{% for p in posts %}
{{ p.render() | safe }}
<br><br>
{% endfor %}
<div>
{{text}}
</div>
{% endblock %}
Please help
Edit:
edit2: removed link and found my problem I was calling the wrong html file in render()
Be kind Newbie here
Looks ok. Are you sure you don't have a loop in the python code that renders the template?

Resources