Django simple pagination in detail view between items - django

I want to add simple back/next pagination in my item detail view.
How can I do that ?
I have something like that in my template:
{% if is_paginated %}
<div class="pagination">
<span class="page-links">
{% if page_obj.has_previous %}
Back
{% endif %}
{% if page_obj.has_next %}
Next: {{ item.title }}
{% endif %}
</span>
</div>
{% endif %}
The main thing is that I want to do this in ItemDetailView, not in ItemListView, because in list view I have all items, and I just want to go between items in detail view.
Thanks a lot for help.

Related

how to properly implement Django (Wagtail CMS) pagination in modal window using Select html tag?

I have a list view in a modal window, once the user chooses the option of the select element, the pagination changes the url queryset (?p=<page_number>), although, in a normal list view there is no issue,as it changes the url, in a modal, it changes the entire page's location (URL address), which of course causes the lost of changes in the main page (document form), I just need the modal list to be changed by the number of pagination-page.
I have searched and read almost all documentations pages related to pagination of a list view using Django, however I was unable to find the solution.
here is the modal pagination template code (see the comments that indicates not working parts):
{% load wagtailadmin_tags %}{# Changed by HH #}
{% if linkurl %}{% url linkurl as url_to_use %}{% endif %}
{% if items.has_previous or items.has_next %}
<div class='pagination center' aria-label="Pagination">
<!-- this is working -->
{% if items.has_previous %}<div class="l_arrow previous">Previous</div>{% endif %}
<p>{% with pr=items.paginator.page_range page_num=items.number total_pages=items.paginator.num_pages %}
Page {{ page_num }} of {{ total_pages }}
<!-- this is NOT working -->
<select onchange="gotopage(this)" name="pg_selector" title="pg_selector" id="pg_selector" disabled>
{% for i in pr %}
<option value="{{ url_to_use }}{% querystring p=i %}" data-page="{{ i }}" {% if i == page_num %} selected {% endif %}>{{i}}</option>
{% endfor %}
</select>
{% endwith %}
</p>
<!-- this is working -->
{% if items.has_next %}<div class="r_arrow next">Next</div>{% endif %}
</div>
{% endif %}
Edit: Temporary Workaround by using text input (please do NOT Consider this as an answer)
{% load wagtailadmin_tags %}{# Changed by HH #}
{% if linkurl %}{% url linkurl as url_to_use %}{% endif %}
<script>
function goto_pg(){
let pg_selector_el = document.getElementById("pg_selector");
pg_selector_el.href = `{{ url_to_use }}?p=${document.getElementById("pg").value}`;
pg_selector_el.click();
}
</script>
{% if items.has_previous or items.has_next %}
<div class='pagination center' aria-label="Pagination">
<!-- this is working -->
{% if items.has_previous %}<div class="l_arrow previous">Previous</div>{% endif %}
<p>
{% with pr=items.paginator.page_range page_num=items.number total_pages=items.paginator.num_pages %}
Page <input id="pg" type="text" value="{{page_num}}" onkeyup="goto_pg()" > of {{ total_pages }}
{% endwith %}
</p>
<!-- this is working -->
{% if items.has_next %}<div class="r_arrow next">Next</div>{% endif %}
</div>
{% endif %}

How to display django-treebeard MP in template as dropdown menu?

Following the treebeard docs api example, I created an annotated_list of my tree using get_annotated_list(parent=None, max_depth=None) with parent=<my root node>. I pass this to my template and using the example they attribute in the docs to Alexey Kinyov, I am able to successfully display my tree using
{% for item, info in annotated_list %}
{% if info.open %}
<ul><li>
{% else %}
</li><li>
{% endif %}
{{ item }}
{% for close in info.close %}
</li></ul>
{% endfor %}
{% endfor %}
What I would like though is to be able to give these nested lists dropdown features. Borrowing from this standard example on w3schools, I modified it to work with my annotated_list template variable and ended up with this:
<ul id="myUL">
<li><span class="caret">{{ annotated_list.0.0 }}</span>
{% for item, info in annotated_list|slice:"1:" %}
{% if info.open %}
<ul class="nested"><li>
{% else %}
</li><li>{% if item.get_children_count > 0 %}<span class="caret">
{% endif %}
{% endif %}
{{ item }}
{% if item.get_children_count > 0 %}</span>
{% endif %}
{% for close in info.close %}
</li></ul>
{% endfor %}
{% endfor %}
</li>
</ul>
My code almost works, but does not seem to display node children for left-most nodes and I can't figure out why.
Note: CSS & JS not included in question but needed to make the dropdown menu work (I'm just using the out-of-the-box CSS/JS used in that w3schools example)
The most likely reason is that there is a problem with the tree.
Run Model.find_problems() to confirm.
Model.fix_tree() can fix most of the common problems.
Note that the get_annotated_list_qs() function doesn't really work at all.
You can try this Code:
<ul id="myUL">
{% for item, info in annotated_list %}
{% if item.get_children_count > 0 %}
<li><span class="box">{{item}}</span><ul class="nested">
{% else %}
<li>{{item}}</li>
{% endif %}
{% for close in info.close %}
</ul>
{% endfor %}
{% endfor %}
</ul>
It worked for me.
To add some missing context:
views.py:
def tree(request):
annotated_list = Category.get_annotated_list()
# template_name = 'JsonDefine/tree.html'
context = {
'annotated_list': annotated_list
}
return render(request, 'JsonDefine/tree3.html',context)
urls.py
from django.urls import path
from . import views
app_name = 'MyApp'
urlpatterns = [
path('tree/', views.tree, name='tree'),
]

Custom menu in Mezzanine

I have following mezzanine tree.html
{% if page_branch_in_menu %}
<ul class="nav nav-list navlist-menu-level-{{ branch_level }}">
{% for page in page_branch %}
{% if page.in_menu %}
{% if page.is_current_or_ascendant or not page.is_primary %}
<li class="
{% if page.is_current %} active{% endif %}
{% if page.is_current_or_ascendant %} active-branch{% endif %}
" id="tree-menu-{{ page.html_id }}">
{{ page.title }}
{% if page.has_children_in_menu %}{% page_menu page %}{% endif %}
</li>
{% endif %}
{% endif %}
{% endfor %}
</ul>
{% endif %}
In the base.html I have following block:
<div class="col-md-2 left">
{% block left_panel %}
<div class="panel panel-default tree">{% page_menu "pages/menus/tree.html" %}</div>
{% endblock %}
</div>
The problem is that on some pages where the menu is empty the css styles are applied to the divs with empty menu lists and users can observe empty containers. In html it looks like:
<div class="col-md-2 left">
<div class="panel panel-default tree">
<ul class="nav nav-list navlist-menu-level-0"></ul>
</div>
</div>
I can hide the child ul with something like .nav:empty { display:none;} but the parent will still be visible. Here is the discussion about similar question: :empty selector for parent element
Is it possible to solve this problem with Mezzanine template tags?
{% if page_branch %} doesn't help because it's full of pages which are all not in_menu.
So better filter them before context.
menu_pages = page_branch.filter(in_menu=True)
Also you should put if block on top of div.tree
{% if menu_pages %}
<div class="panel panel-default tree">{% page_menu "pages/menus/tree.html" %}</div>
{% endif %}
Another way is to write custom filter
{% with menu_pages=page_branch|filter_in_menu %}
{% if menu_pages %}
...
{% endif %}
{% endif %}
But there is no way to apply extra filter to queryset with built-in syntax or Mezzanine tags.
You could wrap the code with an if tag.
{% if page_branch %}
<ul>
{% for page in page_branch %}
{% if page.in_menu %}
{% if page.is_current_or_ascendant or not page.is_primary %}
<li>
{% if not page.is_primary %}
{{ page.title }}
{% endif %}
{% page_menu page %}
</li>
{% endif %}
{% endif %}
{% endfor %}
</ul>
{% endif %}

Django template skip line every two iteration

I have the following html structure:
<div class="row>
<div class="box"></div>
<div class="box"></div>
</div>
I am using pagination feature on Django to pass on 6 items per page.
How would I go about iterating over the paginator generated object list while wrapper each two box divs with row div?
You can use the forloop.counter in the template
{% for obj in obj_list %}
{% if forloop.counter0|divisibleby:2 %}
<div class="row">
{% endif %}
<div class="box"></div>
<div class="box"></div>
{% if forloop.counter|divisibleby:2 %}
</div>
{% endif %}
{% else %}
Nothing to show
{% endfor %}
and if there are odd number of elements in the list, then it would not have a trailing div. I will let you figure out that scenario by yourself. (it is pretty simple)
Documentation for the forloop.counter0 can be found here
Documentation for divisibleby can be found here
I agree with karthikr's solution, but it doesn't print the </div> if you have 3, 5 items...
You have to add a forloop.last to handle that case:
{% for obj in obj_list %}
{% if forloop.counter0|divisibleby:2 %}
<div class="row">
{% endif %}
<div class="box"></div>
<div class="box"></div>
{% if forloop.counter|divisibleby:2 or forloop.last %}
</div>
{% endif %}
{% else %}
Nothing to show
{% endfor %}

Django template and splitting the variable list

In my django view, I pass a list that contains several items.
My html design is like this:
<ul>
<li>
<div>1st item</div>
<div>2nd item</div>
</li>
<li>
<div>3th item</div>
<div>4th item</div>
</li>
<li>
<div>5th item</div>
<div>6th item</div>
</li>
</ul>
You see the pattern, every two items, I need to split them and put into a new <li></li>. How can I loop and divide these variables by every two using {% for item in my_list %}?
You could try something like this which isn't particularly elegant and isn't tested in any way!
{% if list_of_items %}
<ul>
<li>
{% for item in list_of_items %}
<div>{{ item }}</div>
{% cycle '' '</li><li>' %}
{% endfor %}
</li>
</ul>
{% endif %}
You could re-arrange the list in your view first to make it a list of lists, or alternatively you could write your own batch filter.
As far as I know there isn't a really easy django template way of doing what you want - Django templates are deliberately restrictive to encourage you to write python code instead.
If the items have some property that groups them naturally into pairs, you can use regroup:
{% if list_of_items %}
<ul>
{% regroup list_of_items by property as item_chunks %}
{% for chunk in item_chunks %}
<li>
{% for item in chunk.list %}
{{ item }}
{% endfor %}
</li>
{% endfor %}
</ul>
{% endif %}
It's a couple more lines than Nick's solution, but if those elements do have a property that joins them naturally, I would say that this is a more elegant solution.
you might want to enumerate your total list of 6 elements in batches of size 2 which gives you 3 batches and then loop through each batch.
To do this, you can add a custom batch filter in your templatetags directory. Here is the documentation for adding custom tag/filter.
https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/
Your custom filter can be something like this.
#register.filter
def batch(iterable, n=1):
l = len(iterable)
for ndx in range(0, l, n):
yield iterable[ndx:min(ndx + n, l)]
In your html
{% for batch in yourlist|batch:2%}
<li>
{% for element in batch %}
<div>{{element}}</div>
{% endfor %}
</li>
{% endfor %}
Updating this answer for Django 3.1
{% block content %}
<div class="row">
{% for product in products %}
<div class="col-md-auto">
<h5>{{ product.name }}</h5>
</div>
{% if forloop.counter|divisibleby:"6" %}
</div>
<div class="row">
{% endif %}
{% endfor %}
</div>
{% endblock content %}