How to display {% django block %} dynamically with for loop? - django

Hello Awesome People!
I'm building a dashboard for my website, in the left section, I have a menu containing multiple items : Home, Messenger, Job Offers ....
When creating an account, Users are able to choose their items, re-order or remove.
I have Menu & Item models,
class Menu(models.Model):
items = models.ManyToManyField('Item',blank=True)
class Item(models.Model):
name = models.CharField(max_length=100)
unique_key = models.CharField(max_length=100,unique=True)
url = ......
For the menu of the user, in my base_template I display the items of the current users with for loop. With a CSS class I want to highlight the current view, the current item.
When the user visits his messenger, I want to highlight that item messenger with a CSS class.
<ul>
{% for item in user.menu.items.all %}
<li class='{% block item.unique_key %}{% endblock %}'>
{{item.name}}
</li>
{% endfor %}
</ul>
Now, when being in messenger.html for example, knowing that I have a unique_key for messenger called item_messenger, I do
{% block 'item_messenger' %}active{% endfor%}
It's not working, the item Messenger or whatever I choose doesn't become highlighted, I wonder why?
Is there another way to achieve that?
Any hint will be helpful, thanks in advance!

You are trying to have the block tag interpret a variable as it's name. That doesn't work.
You will need to pass along the active item's key in the template context and then in your base_template you can just do a simple if statement to add the active class to the correct entry.
<ul>
{% for item in user.menu.items.all %}
<li class='{% if item.unique_key == active_key %}active{% endif %}'>
{{item.name}}
</li>
{% endfor %}
</ul>

Related

Wagtail / Django inbuilt main menu

Started a project working with Wagtail CMS and what I understood of their principal of pages is that it is hierarchical leading me to the conclusion that pages are either parent/child related or siblings.
So how I have done it is have my main page landing and all other pages would be children of this page.
Here is my structure
Home page
/ | \
News About us Events
/ | \ / | \
n_item n_item n_item e_item e_item e_item
So they are 6 types of pages
Home page * (unique)
News * (unique)
n_item (News Item)
About Us * (unique)
Events * (unique)
e_item (Event Item)
I then ticked the "Show in menus:" in the promote tab for the starred pages (*) in the above list.
The n_item and e_item will allow the editor (non technical) person to add as many of these pages as they wanted as they will be listed as part of the contents in the "News" and "Events" pages respectively.
Ideally the editor should not create siblings to Home Page,News, About us and events. Is it also possible to limit wagtails functionality of where editors can add pages if they are not super users?
But there doesn't seem to be a clear simple way (maybe a tag) to generate my menu. This is what I have found in my research.
page url and slug url which i cant get to seem to work in every template that is not being referenced directly by that pages template. It would also mean its not that dynamic
django-simple-menu doesn't seem dynamic to me as i have to manual link pages in code. Plus if this is the way wagtail intended to do it, it seems like a waste of maintaining page hierarchy internally.
List item seems to have been deprecated but even if it wasn't it seems cumbersome for the same reasons as 1 and 2
I wanted to have automatically generated menu too but only first level with home. I might did something naive but it works. I am using Wagtail 2.5.1 and Django 2.2.2.
My Wagtail page structure look like this:
Home page
/ \
Blog About
/ | \
n_item n_item n_item
I am using bootstrap4 so this is my nav in base.html template:
{% with root_p=request.site.root_page %}
{# this is static item for home #}
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link {% if '/' == request.path %} active {% endif %}" href="{{root_p.url}}">{{root_p.slug}}</a>
</li>
{# Here I am looping home's childrens #}
{% for nav in root_p.get_children.live %}
{% if nav.show_in_menus %}
<li class="nav-item">
<a class="nav-link {% if nav.slug in request.path %} active {% endif %}" href="{{nav.url}}">{{nav.slug}}</a>
</li>
{% endif %}
{% endfor %}
</ul>
{% endwith %}
This is result:
Now I am able to change slug, tick/untick "show in menus" and my bootstrap menu reflect these changes.
EDIT:
My last answer does not solve custom order of menu items. We can order items by standard fields in Page model "first_published_at" or alphabetically by "title" name. But I needed make it custom. So I added custom integer field in each my page model. Then wagtail users can change order of menu items using "promote tab".
My current page structure look like this:
Home
/ | \
Case studies Services Contact
My page model: (example of services only)
class ServicesPage(Page):
template = "home/services.html"
max_count = 1
subpage_types = []
menu_order = models.IntegerField(default = 0, help_text = "Setup custom menu order")
promote_panels = Page.promote_panels + [
FieldPanel('menu_order'),
]
class Meta:
verbose_name = "Services"
verbose_name_plural = "Services"
This is how it looks in promote tab of each page model
Last step is edit navbar loop in base template:
{% with root_p=request.site.root_page %}
{% for nav in root_p.get_children.specific.live|dictsort:"menu_order" %}
{% if nav.show_in_menus %}
<li class="nav-item {% if nav.slug in request.path %} active {% endif %}">
<a class="nav-link" href="{{nav.url}}">{{nav.title}}</a>
</li>
{% endif %}
{% endfor %}
{% endwith %}
For ordering I am using default django template filter dictsort.
I guess it is not best way how to do that, but it works.
Have you seen https://github.com/rkhleics/wagtailmenus? It's designed to "manage and render multi-level navigation and simple flat menus in a consistent, flexible way".

Creating Django Wagtail Sidebar with Index Page Child Links

I'm building a site using Django Wagtail and am having trouble figuring out how to add a sidebar menu that will list all the child pages of the parent index page. For example, I have a standard_index_page.html that I created a parent page with in Admin, then I added child pages of that using standard_page.html template.
In my standard_index_page.html template, I have the following code
{% standard_index_listing calling_page=self %}
and it displays all the child pages with links, but I would also like to display a list of all the child links on the child pages as well.
I hope this is making sense and someone can lend a hand. Thank you.
In essence you traverse the tree structure of your page hierarchy that is provided to Wagtail by Django-Treebeard.
Many front-end frameworks do not allow for multiple levels of menus as some consider it outside of best practices. However, with a library such as SmartMenus you can display this structure with a little elbow grease.
For my needs, there was no easy solution to this. So, while I want to share an example of how I went about this, it may be missing explanation. If you have any questions, I'd be happy to answer them.
I struggled with this for awhile and while there may be easier methods to traverse the tree, I built the following method as my needs expanded. It allows us to traverse all live pages in our site, check when the current page is being rendered in the menu, and allows for fine-grained control over rendering.
Here's what we're going to do:
Create a few template tags that will get the site root of the
current site, loop through direct children of the site root, and loop through any lower levels of children, while looping through children of the current menuitem when discovered at each level.
In your base template, this means we need to:
{% load demo_tags %} to import our custom template tags
Call {% top_menu calling_page=self %} to get and render all
direct children of the site root. These are items that would be shown across a standard menu bar.
Call {% top_menu_children parent=menuitem %} within the template
rendered by {% top_menu %} to get and render all second- and
lower-level children pages. This encompasses all menu items to be shown when hovering on the parents menu item.
Here's the custom demo_tags.py file I created to traverse all levels of the page hierarchy. The beauty of this is that it does not require any custom context data to be supplied; it works out of the box with Wagtail!
#register.assignment_tag(takes_context=True)
def get_site_root(context):
'''
Returns a core.Page, not the implementation-specific model used
so object-comparison to self will return false as objects would differ
'''
return context['request'].site.root_page
def has_menu_children(page):
'''
Returns boolean of whether children pages exist to the page supplied
'''
return page.get_children().live().in_menu().exists()
#register.inclusion_tag('info_site/tags/top_menu.html', takes_context=True)
def top_menu(context, parent, calling_page=None):
'''
Retrieves the top menu items - the immediate children of the parent page
The has_menu_children method is necessary in many cases. For example, a bootstrap menu requires
a dropdown class to be applied to a parent
'''
root = get_site_root(context)
try:
is_root_page = (root.id == calling_page.id)
except:
is_root_page = False
menuitems = parent.get_children().filter(
live=True,
show_in_menus=True
).order_by('title')
for menuitem in menuitems:
menuitem.show_dropdown = has_menu_children(menuitem)
return {
'calling_page': calling_page,
'menuitems': menuitems,
'is_root_page':is_root_page,
# required by the pageurl tag that we want to use within this template
'request': context['request'],
}
#register.inclusion_tag('my_site/tags/top_menu_children.html', takes_context=True)
def top_menu_children(context, parent, sub=False, level=0):
''' Retrieves the children of the top menu items for the drop downs '''
menuitems_children = parent.get_children().order_by('title')
menuitems_children = menuitems_children.live().in_menu()
for menuitem in menuitems_children:
menuitem.show_dropdown = has_menu_children(menuitem)
levelstr= "".join('a' for i in range(level)) # for indentation
level += 1
return {
'parent': parent,
'menuitems_children': menuitems_children,
'sub': sub,
'level':level,
'levelstr':levelstr,
# required by the pageurl tag that we want to use within this template
'request': context['request'],
}
In essence, there are three levels of pages rendered:
The site root is called by {% get_site_root %}
First-level children are called by {% top_menu %}
Second- and lower-level children are called by {% top_menu_children %}, which is called any time a page shown in the menu has children while rendering this tag.
In order to do this, we need to create the templates to be rendered by our top_menu and top_menu_children template tags.
Please note - these all are built for Bootstrap 3's navbar class and customized for my needs. Just customize these for your needs. The whole menu building process is called by {% top_menu_children %}, so place this tag in your base template where you want the menus rendered. Change top_menu.html to reflect the overall structure of the menu and how to render each menuitem. Change children_items.html to reflect how you want children of all top-menu items, at any depth, rendered.
my_site/tags/top_menu.html
{% load demo_tags wagtailcore_tags static %}
{% get_site_root as site_root %}
{# FOR TOP-LEVEL CHILDREN OF SITE ROOT; In a nav or sidebar, these are the menu items we'd generally show before hovering. #}
<div class="container">
<div class="collapse navbar-collapse" id="navbar-collapse-3">
<ul class="nav navbar-nav navbar-left">
{% for menuitem in menuitems %}
<li class="{% if menuitem.active %}active{% endif %}">
{% if menuitem.show_dropdown %}
<a href="{{ menuitem.url }}">{{ menuitem.title }}
<span class="hidden-lg hidden-md hidden-sm visible-xs-inline">
<span class="glyphicon glyphicon-chevron-right"></span>
</span>
</a>
{% top_menu_children parent=menuitem %}
{% else %}
{{ menuitem.title }}
{% endif %}
</li>
{% endfor %}
</ul>
</div>
</div>
my_site/tags/children_items.html
{% load demo_tags wagtailcore_tags %}
{# For second- and lower-level decendents of site root; These are items not shown prior to hovering on their parent menuitem, hence the separate templates (and template tags) #}
<ul class="dropdown-menu">
{% for child in menuitems_children %}
{% if child.show_dropdown %}
<li>
<a href="{% pageurl child %}">
{% for i in levelstr %}&nbsp&nbsp{% endfor %}
{{ child.title }}
<span class="glyphicon glyphicon-chevron-right"></span>
</a>
{# On the next line, we're calling the same template tag we're rendering. We only do this when there are child pages of the menu item being rendered. #}
{% top_menu_children parent=child sub=True level=level %}
{# ^^^^ SmartMenus is made to render menus with as many levels as we like. Bootstrap considers this outside of best practices and, with version 3, has deprecated the ability to do so. Best practices are made to be broken, right :] #}
</li>
{% else %}
<li>
<a href="{% pageurl child %}">
<!-- Allows for indentation based on depth of page in the site structure -->
{% for i in levelstr %}&nbsp&nbsp{% endfor %}
{{ child.title }}
</a>
</li>
{% endif %}
{% endfor %}
</ul>
Now, in your base level template (let's assume you are using one; if not, get to it :) ) you can traverse the menu while keeping clutter cleared away to the templates used by your inclusion_tags.
my_site/base.html
<ul class="nav navbar-nav navbar-left">
{% for menuitem in menuitems %}
<li class="{% if menuitem.active %}active{% endif %}">
{% if menuitem.show_dropdown %}
<a href="{{ menuitem.url }}">{{ menuitem.title }}
<span class="hidden-lg hidden-md hidden-sm visible-xs-inline">
<span class="glyphicon glyphicon-chevron-right"></span>
</span>
</a>
{% top_menu_children parent=menuitem %}
{% else %}
{{ menuitem.title }}
{% endif %}
</li>
{% endfor %}
</ul>
I wrote a blog post about this - check it out for more details. Or, head over to Thermaline.com to see it in action, though I think there's not multiple levels of depth right now. IF THERE WERE, they'd be rendered automatically :)
Now, this example is for a navbar, but it could easily be adapted for a sidebar.
All you need to do is:
Include demo_tags in your base template
Call {% top_menu %} where you wish to render your menus.
Customize top_menu.html and children_items.html to render the
first and then all subsequent levels of pages.
Shout out to Tivix for their post on two-level menus that was a great starting point for me!

Django: How can I access previous results via navigation bar?

I implemented a Django website to search my database. My navigation bar has the following permanent tags: Search, Results, Article, About, Contact. After I submit the term on the Search-site,
the Result-site opens and shows a list of all possible hits from the database. From there, I can click on a link to navigate to the Article-site to read the article. However, when I leave the Result-site to another site and want to go back later by clicking on the Result-tag, I want to see the previous results again. But it won’t reopen my previous results because I neither send a request to the corresponding search methods nor can I tell the tag which dynamic url to open (/search/term).
How can I revisit my previous results? Can I somehow keep the result stored on this side until I start another search via the Search-site? I saved the search term as session variable. Can I send a request containing a session variable by clicking on a tag in my navigation bar?
I do not want to use a back-button because it should be possible to go back to the Result-site independent from where you are right now on the website.
Can you please help me?
views.py
class IndexView(ListView):
model = S
template_name = 'app/index.html'
class ResultsView(DetailView):
model = S
template_name = 'app/results.html'
context_object_name = 'results'
def search(request):
term = request.GET['term'] #search term
request.session['query'] = term
results = S.objects.filter(name=term)
list_of_results = [model_to_dict(aresult) for aresult in results]
return render(request, 'app/results.html', {'list_of_results':list_of_results})
base.html
<header class="naviBar">
<nav>
<ul>
{% block index %}
<li>
Search
</li>
{% endblock %}
{% block results %}
<li>
Result
</li>
{% endblock %}
{% block contact %}
<li>
Contact
</li>
{% endblock %}
</ul>
</nav>
</header>

Factorizing a header menu in Django template

I'm building a website using django with a header on top of every page, which basically is a menu with a few links, constant throughout the pages.
However, depending on the page you're on I'd like to highlight the corresponding link on the menu by adding the class "active". To do so, I am currently doing as follow: each page has a full menu block that integrates within a general layout, which does NOT contain the menu. For exemple, page2 would look like this:
{% extends "layout.html" %}
{% block menu %}
<li>Home</li>
<li>page1</li>
<li class="active">page2</li>
<li>page3</li>
{% endblock %}
The problem is that, beside from that solution being not so pretty, every time I want to add a link to the header menu I have to modify each and every page I have. Since this is far from optimal, I was wondering if any of you would know about a better way of doing so.
Thanks in advance!
You can create a custom templatetag:
from django import template
from django.core.urlresolvers import reverse, NoReverseMatch, resolve
register = template.Library()
#register.simple_tag
def active(request, view_name):
url = resolve(request.path)
if url.view_name == view_name:
return 'active'
try:
uri = reverse(view_name)
except NoReverseMatch:
uri = view_name
if request.path.startswith(uri):
return 'active'
return ''
And use it in the template to recognize which page is loaded by URL
<li class="{% active request 'car_edit' %}">Edit</li>
If you have a "page" object at every view, you could compare a navigation item's slug to the object's slug
navigation.html
<ul>
{% for page in navigation %}
<li{% ifequal object.slug page.slug %} class="active"{% endifequal %}>
{{ page.title }}
</li>
{% endfor %}
</ul>
base.html
<html>
<head />
<body>
{% include "navigation.html" %}
{% block content %}
Welcome Earthling.
{% endblock %}
</body>
</html>
page.html
{% extends "base.html" %}
{% block content %}
{{ object }}
{% endblock %}
Where navigation is perhaps a context_processor variable holding all the pages, and object is the current PageDetailView object variable
Disclaimer
There are many solutions for your problem as noted by Paulo. Of course this solution assumes that every view holds a page object, a concept usually implemented by a CMS. If you have views that do not derive from the Page app you would have to inject page pretenders within the navigation (atleast holding a get_absolute_url and title attribute).
This might be a very nice learning experience, but you'll probably save loads time installing feinCMS or django-cms which both define an ApplicationContent principle also.
You may use the include tag and pass it a value which is the current page.
For example, this may be a separate file for declaring the menu template only:
menu.html
{% if active = "a" %}
<li>Home</li>
{% if active = "b" %}
<li>page1</li>
{% if active = "c" %}
<li class="active">page2</li>
{% if active = "d" %}
<li>page3</li>
And call this from within your template like this:
{% include 'path/to/menu.html' with active="b"%} # or a or c or d.
Hope it helps!

Django, Pulling a Value from a Model based on an ID

I'm relatively new to Django and as a test of my knowledge I'm trying to set up a project where it displays a list of games between anchor tags, when one of the game tags is clicked on, it pulls information about that game from a model using the ID (primary key) relative to that name. For instance the first games ID would be 1 and so on.
However, I am uncertain as to how to approach building a view for this. The only way I was able to get information from a template before was from user input (input tag) and then using request.GET to take the information from the input.
So far in this project, anchor tags are linking to a different URL which has the view which gets the information based on the id, then it should refresh the page and the information should display. Everything should be easy, but I'm just having trouble thinking of a way to get the id of the game based on which link is clicked. Is there a way I can simply set the value of this ID somewhere and reference it in the view, or rather pull the id of the game based on which link is clicked?
Code:
{% extends "base.html" %} <!-- Extends the base models html design-->
{% block title %}Arcade{% endblock %}
{% block content %}
{% if games %}
<p>Found {{ games|length }} game{{ games|pluralize }}</p>
{% for game in games %}
<li>{{ game.game_name }}</li><!--Game Link->
{% endfor %}
{% else %}
<p>There are currently no games in the database.</p>
{% endif %}
{% if results %}
{% endif %}
{% endblock %}
I hope I did an adequate job explaining this problem.
To simplify it further: How do I pull any kind of value from a template?
Thank You
Keith
This is simple. In each iteration of your for loop, you have an object called game, which is presumably a model instance. So you can just do:
<li>{{ game.game_name }}</li>
Or, even better, use the reverse URL functionality:
<li>{{ game.game_name }}</li>
You should look at using AJAX for this.
When the user clicks a tag, have the page asynchronously GET or POST to your django backend. If you want to GET, write a url to the effect of
^/data/game/(\d+)
then grab the id, get your model instance, and return some json or xml.