Wagtail Index page not displaying objects of children - django

I am at my wits-end and feel I am missing something simple but I've looked at it over and over and can't figure it out.
I have a simple person_index_page that I want to show the child person_page objects, but no matter what I try...nothing. I have several sites with a similar setup and they work. Can you please look at my code below and see if you notice something I am missing? Thank you.
home_tags.py
# Person feed for home page and staff page
#register.inclusion_tag(
'home/tags/person_listing_homepage.html',
takes_context=True
)
def person_listing_homepage(context, count=3):
people = PersonPage.objects.live().order_by('?')
return {
'people': people[:count].select_related('feed_image'),
'request': context['request'],
}
person_index_page.html
{% extends 'base.html' %}
{% load wagtailcore_tags wagtailimages_tags home_tags %}
{% block content %}
...
{% include "home/tags/person_listing_homepage.html" %}
...
{% endblock %}
person_listing_homepage.html probably should name this at some point
{% for person in people %}
{% include "home/includes/person_list_item.html" %}
{% endfor %}
person_list_item.html
{% load wagtailcore_tags wagtailimages_tags %}
{# Individual person item in a list - used on people index and home page #}
<a class="list-group-item" href="{% pageurl person %}">
<div class="media">
{% if person.feed_image %}
<div class="media-left">
{% image person.feed_image width-200 as img %} <img class="media-object" src="{{ img.url }}"/>
</div>
{% endif %}
<div class="media-body">
<h4 class="media-heading">{{ person.first_name }} {{ person.last_name }}</h4>
{% if person.search_description %}
<p>{{ person.search_description }}</p>
{% endif %}
</div>
</div>
</a>

This is more of a "how to debug" question than a Wagtail one. Rather than just giving you the answer directly, here's the process I would take:
You say that adding print(people) inside the person_listing_homepage function doesn't display anything. So, your next question should be: "is this function being run at all?" Change the print statement to print("GOT HERE"). You'll find that this doesn't display anything either - which tells you that the function is not being run.
The next step would be to add some debugging output around the place where the function should be called from - if that doesn't get displayed either, you know that code isn't being run either, and you'd have to keep going up a level until you find something that is being run. So let's look for that place...
And this is where you find the problem. You never call the person_listing_homepage function anywhere in your code. You include the person_listing_homepage.html template, but that's not the same thing. person_index_page.html should become:
{% extends 'base.html' %}
{% load wagtailcore_tags wagtailimages_tags home_tags %}
{% block content %}
...
{% person_listing_homepage %}
...
{% endblock %}

Related

Share variables to included templates in django

In views.py the thing object is being passed to index.html.
def index(request):
return render(request, "myapp/index.html", {
'things': Thing.objects.all()
})
index.html is setup like this:
{% block body %}
{% for thing in things %}
{{ thing.attribute }}
<button type="button" class="btn" data-toggle="modal" data-target="#exampleModal"></button>
{% endfor %}
{% endblock %}
I would like to separate the html that renders the modal referred to by the button into another html file to keep things organized. I was able to do this by referencing the modal html file with include as shown below:
{% block modal_code %}
{% include 'myapp/modal_code.html' with things=things %}
{% endblock %}
When I tried to share the things object it didn't get incorporated into modal_code.html when I used it like this:
{% for thing in things %}
{{ thing.attribute }}
{% endfor %}
from the documentation, you can pass context like this
{% include "name_snippet.html" with person="Jane" greeting="Hello" %}
so in your case:
{% block modal_code %}
{% include 'myapp/modal_code.html' with things=things %}
{% endblock %}

django tables how to detect if table is empty

I am new to django and web development and based on examples and help on SO, I have pieced together something which takes a model and renders it in a django-table. My template code is basically as follows:
{% block content %}
{% load static %}
{% load render_table from django_tables2 %}
<div class="function-page">
<div class="table-form">
<div class="function-container">
{% render_table reviews %}
</div>
</div>
</div>
{% endblock %}
The view is as follows:
#login_required(login_url="login/")
def review(request):
table = DummyTable(DummyModel.objects.all())
form = DummyForm()
RequestConfig(request, paginate={"per_page": 10}).configure(table)
return render(request, 'review.html', {'reviews': table, 'DummyForm': form})
This works fine. However, what I would like to do is show a message to the user saying that there are no records when the database table is empty. In the current setting, it shows an empty table with the columns which is probably not the best from a usability point of view.
There are two options. Either you set empty_text inside class Meta
class Reviews(tables.Table):
class Meta:
empty_text = _("There are no reviews yet")
Or you can check it inside the template and avoid rendering table this way
{% if reviews_table.data.list %}
{% render_table reviews_table %}
{% else %}
<h1>There are no reviews yet</h1>
{% endif %}
Probably the easiest way is in your template. Assuming your variable that's empty is called reviews:
{% block content %}
{% load static %}
{% if reviews %}
{% load render_table from django_tables2 %}
<div class="function-page">
<div class="table-form">
<div class="function-container">
{% render_table reviews %}
</div>
</div>
</div>
{% else %}
<span> Whatever holding response/error message you want. </span>
{% endif %}
{% endblock %}
Per this this answer, for example, using {% if variable %} against a valid but empty variable, it generally evaluates to False, letting you use the {% if reviews %}.
However, if you need a really bulletproof check, you can do {{ value|default:"nothing" }} - from here.
You could also do it in your views, and pass an error message back to the template using the standard Messages framework included in Django:
from django.contrib import messages
messages.add_message(request, messages.INFO, "No reviews found, I'm afraid!.")
You need to include something like this in your templates to use messages:
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
Or you could do both! :)
test against table.paginated_rows it will be empty and evaluated to False when the table have no data.
they use this in django_tables2/templates/django_tables2/bootstrap.html:~26 template:
{% for row in table.paginated_rows %}
...
{% empty %}
... {{ table.empty_text }}
{% endfor %}
Do this:
{% if reviews %}
<div class="function-page">
<div class="table-form">
<div class="function-container">
{% render_table reviews %}
</div>
</div>
</div>
{% else %}
<div>
<p> Message to use </p>
</div>
{% endif %}
Milano answer worked to me, but removing ".list" from the if conditional:
{% if reviews_table.data.list %}
{% render_table reviews_table %}
{% else %}
<h1>There are no reviews yet</h1>
{% endif %}

django - Invalid block tag: 'add_pinned_status', expected 'else' or 'endif'

I get the following error when serving my django application using Nginx+FastCGI
Invalid block tag: 'add_pinned_status', expected 'else' or 'endif'
Oddly, the site works just fine when I'm serving using the Django development server. It also works with Nginx most of the time, but the error randomly appears and reappears with refreshes. Any idea what the problem could be?
EDIT: Here's the code, just to clarify that there's NO hanging if statement.
{% extends 'master.html'%}
{% load thumbnail %}
{% load tags %}
{% block 'title' %}
{{ title }}
{% endblock %}
{% block 'content' %}
<div id="feed" class="content">
{% for book in books.object_list %}
<div class="book_preview">
<div class="thumbnail">
<a href="/book/{{ book.id }}/{{ book.get_slug }}/">
{% if book.cover_image %}
{% thumbnail book.cover_image "120" as im %}
<img src="{{ im.url }}" alt="Python for Software Design"/>
{% endthumbnail %}
{% else %}
<img src="{{ STATIC_URL }}default_thumb.jpg" alt="Python for Software Design"/>
{% endif %}
</a>
</div>
<div class="book_details">
<h2 class="book_title">
<a class="book_profile_link" href="/book/{{ book.id }}/{{ book.get_slug }}/">{{ book.title }}</a>
{% if user != book.uploader %}
<a class="shelf_adder {% add_pinned_status request book.pk %}" href="/shelf/{{ book.id }}/toggle/?next={{ request.get_full_path }}" title="Toggle shelf status"></a>
{% endif %}
</h2>
<h3 class="book_subtitle">
{% if book.subtitle %}
{{ book.subtitle }}
{% else %}
<a href='/book/{{book.id}}/edit/#subtitle'>Provide subtitle</a>
{% endif %}
</h3>
<h3 class="book_authors"> by {{ book.author.filter|join:", " }}</h3>
<div class="book_description">
{% if book.description %}
<p>
{{ book.description|truncatewords:25 }}
</p>
{% else %}
<p class="message">No description available. Create one.</p>
{% endif %}
</div>
<div class="book_links">
<a href="/book/{{ book.id }}/{{ book.get_slug }}/" class="book_profile_link" title="Book profile">
Book profile
</a>
<a href="http://{{ book.homepage }}" class="book_website_link" title="Book website" target="_blank">
Book website
</a>
</div>
<p>Points: {{ book.shelf_additions }}</p>
<div class="book_tags">
{% if book.topics.all %}
{% for topic in book.topics.filter %}
{{ topic }}
{% endfor %}
{% else %}
<a href="/book/{{ book.id }}/edit/#topics" title='Click to add'>no topics added☹</a>
{% endif %}
</div>
</div>
<div style="clear: both;"></div>
</div>
{% endfor %}
<div class="pagination">
{% if books.has_previous %}
previous
{% endif %}
<span class="current">
Page {{ books.number }} of {{ books.paginator.num_pages }}
</span>
{% if books.has_next %}
next
{% endif %}
</div>
</div>
{% endblock %}
The problem starts on the line after the if user != book.uploader statement, which as you can see is terminated with the appropriate endif. I suspect it may be some sort of timeout but I'm not entirely sure. Keep in mind, it works sometimes but randomly stops when using Nginx. It works flawlessly with the dev server.
Django gives that error when you have an unclosed templatetag. In this case an {% if ... %} templatetag.
As to why it only happens in certain scenarios, it might be inside a conditional tag itself, so it's not always processed, but I think Django processes the whole template despite what's going on conditionally or not. It might also be possible that there was some mistake in updating your production site and it's using a different/older version than your development site.
Regardless, the error is the error. Find the unclosed templatetag, and you'll solve it across the board.
UPDATE: The alternative is that the add_pinned_sites templatetag is undefined. Assuming it is in fact loaded in {% load tags %}, make sure that that templatetag library is available in all running environments, i.e. it literally exists on the server. If it is in fact there, make sure you completely reload your Nginx+FastCGI environment, or just reboot the server to be completely sure.
Is "tags" the actual name of the tag library that holds add_pinned_sites? Might be worth changing it to a clearer name-- just wondering if it's possible you're seeing import collisions between that and another tag library (like Django's built-in tags).

Unable to paginate object_list usng django-pagination

I am using django-pagination to paginate the a list of objects in my temlate. I have installed the app, added it in my project and added pagination.middleware.PaginationMiddleware in my settings.py file. But when I try to use it in my template the object_list is not being paginated. Here is my template code
{% extends "base.html" %}
{% load pagination_tags %}
{% autopaginate Questions %}
{% block title %}
Questions
{% endblock %}
{% block content %}
<div id="contentDiv">
{% for question in Questions %}
<div style="padding:5px 20px 5px 30px;">
<p class='question'><span id='style2'>Q
</span> {{ question.questiontext|safe }}
<span style= 'float:right;'><span style='font-size:12px; color:#099;'><a href="/question/type={{question.type}}"
style='font-size:12px; color:#099;'>{{question.type}}</a></span> <span style='color:#99C; font-size:12px;'>Level: </span><span style='color:#099;font-size:12px;'>{{question.level}}</a></span></span>
</p>
<h2 class='trigger1' ><a href='#'>Answer</a></h2>
<div class='toggle_container' >
<div class='block' style='background-color:#fff; '>
<p class='ans'> {{ question.answer|safe }} </p>
</div>
</div>
</div>
{% endfor %}
<div class="pagination" style="width:1000px; margin:auto; margin-bottom:20px;">
{% paginate %}
</div>
</div>
{% endblock %}
The list of objects is in the context_variable called Questions. Am I doing something wrong?
After a very long time I have been able to find out the error I was having with django-pagination. I had the canonical base template which I was extending on all pages.
In the documentation it is written that we require to put {% paginate %} after {% autopaginate object_list %} but no where it was written about the placement of {% autopaginate object_list %} itself.
I had title and body blocks in my template, and I was putting {% autopaginate object_list %} just below the {% extends "base.html" %} and as a result it was not working. I found that I had to put this statement inside the body block and now it is working absolutely fine.
Can you see the content of your pagination div if you write "Hello, I want a burger" or anything else in there?
Are you sure you have enough Questions to paginate? You could try something like:
{% autopaginate Questions 2 %}
to make sure that you'll be paginating at 2 questions/page.
Solved as Sachin told above:
I just moved {% load pagination_tags %}{% autopaginate list_objs 10 %}
inside {% block content %} statement (previously it was outside of it, so pagination was invisible. If no errors, but now pages - try to play with it (moving pagination block).

Use django-pagination to generate link refl=next/prev

I'm using django-pagination to paginate my pages. It works great, but I would like to set up
<link rel="prev" href="http://www.example.com/foo/?page=1" />
<link rel="next" href="http://www.example.com/foo/?page=3" />
to the <head>, like it is recommended by google .
However I found no way ho to do this (without extra queries at least). First I tried to edit the pagination/templates/pagination.html with something like this
{% block extra_head %}
<link rel=... ... />
{% endblock %}
Which of course did not work (pagination.html is included by the {% paginate %} tag, it does not extend my layout.html). Next, I tried to modify my template for /foo/ view to something like this (adding the {% block extra_head %}):
{# foo.html #}
{% extends "layout.html" %}
{% block content %}
{% load pagination_tags %}
{% autopaginate object_list %}
{% paginate %}
{% for obj in object_list %}
{{ obj }}
{% endfor %}
{% paginate %}
{% endblock %}
{% block extra_head %}
<link rel="prev" href="?page={{ page_obj.previous_page_number }}"/>
{% endblock %}
But this won't work either, as the page_obj variable is only available in scope of {% block content %}. A could call
{% autopaginate object_list %}
in the extra_head block, but that will mean an extra hit to the db (and possibly other side effects that I'm not aware of). Is there an elegant way to solve this, ideally as DRY as possible?
Edit: I'm using django 1.2.
You can do {% autopaginate %} in higher-level block, then paginated objects will be available in sub-blocks. If you don't have higher level block it is possible to do this in base template:
{% block root %}
...
{% endblock %}
And in extended template:
{% extends "base.html" %}
{% load pagination_tags %}
{% block root %}
{% autopaginate objects 10 %}
{{ block.super }}
{% endblock %}
<!-- the rest of your code -->
Now, to get a different rendering of paginator in head, you can make use of the with tag:
{% with we_are_in_head=1 %}
{% paginate %}
{% endwith %}
And override templates/pagination/pagination.html with something like this:
{% if we_are_in_head %}
# Your links to next and prev only
{% else %}
# original code
{% endif %}
A moral
This is not elegant and the reason is that pagination should be implemented in the view. Templates are for rendering only, template-tags too. Pagination makes extra sql queries, it also parses arguments from request, template is totally wrong place for this code, so workarounds has to be invented. These workarounds might break on next release of django, they are also subtle and can be accidentally broken by other developer.
We can call autopaginate in a view and then use {% paginate %} as usual. Here is a recipe if somebody still face the described problem:
from pagination.templatetags.pagination_tags import AutoPaginateNode
def autopaginate(request, context, queryset_var, paginate_by):
""" It allows us to use paginated objects in different template blocks """
autopagination = AutoPaginateNode(queryset_var, paginate_by)
# Inject a request - it's required by `autopagination` function
context['request'] = request
autopagination.render(context)