How to loop through urls in a template - django

I have a load of URLs I want to declare as constants so I can use them in for and if statements in a template. At the moment I do this manually e.g.:
{% url 'inbox' as inbox %}
{% url 'sent' as sent %}
{% url 'drafts_tasks' as drafts_tasks %}
However this feels kinda clunky and when the urls increase in numbers it is a load of extra code repeated on every template.
Is there a better way I can loop through urls and declare them?
Here's an example of how I want to use them:
{% url 'XXX' as XXX %}
{% for ....
<li class="nav-item {% if request.path == XXX %}active{% endif %}">
<a class="nav-link" href="{{ XXX.url }}">{{ XXX.name }}
</a>
</li>
endfor %}

The easiest option would be to pass the urls as a list.
class URLView(TemplateView):
template_name = 'urls.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['urls'] = get_my_list_of_urls()
return context
As for the repeated code you could make use of the include template tag.
Create a file with the repeating template fragment templates/includes/url_link_list.html:
<li class="nav-item {% if request.path == xxx %}active{% endif %}">
<a class="nav-link" href="{{ xxx.url }}">{{ xxx.name }}</a>
</li>
Then in your urls.html file that was defined in your view you will include that fragment:
<ul>
{% for url in urls %}
{% with url as xxx %}
{% include 'includes/url_link_list.html' %}
{% endwith %}
{% endfor %}
</ul>

Related

How to use get_elided_page_range in Django paginator?

There is new option to create pagination range - get_elided_page_range
https://docs.djangoproject.com/en/3.2/ref/paginator/#django.core.paginator.Paginator.get_elided_page_range
How should I use it? How can I set args? I am using CBV ListView.
I tried https://nemecek.be/blog/105/how-to-use-elided-pagination-in-django-and-solve-too-many-pages-problem but it didn't work for me.
I have 81 pages and current page is 10. Problem is I am always have range 1 2 3 4 ... 80 81
What am I doing wrong?
#views.py
class TrailersListView(ListView):
queryset = Trailer.objects.all()
paginate_by = 10
#template.html
{% for i in paginator.get_elided_page_range %}
{% if page_obj.number == i %}
<li class="active page-item">
<span class="page-link">{{ i }}</span>
</li>
{% else %}
{% if i == paginator.ELLIPSIS %}
<li class="page-item">
<span class="page-link">{{ paginator.ELLIPSIS }}</span>
</li>
{% else %}
<li class="page-item">
<a class="page-link" href="?page={{ i }}">{{ i }}</a>
</li>
{% endif %}
{% endif %}
{% endfor %}
The page number is not passed to the "get_elided_page_range" method and it uses the default value (1) all the time. So I created a custom tag to pass it a number.
appname/templatetags/paginator_tags.py
from django import template
from django.core.paginator import Paginator
register = template.Library()
#register.simple_tag
def get_proper_elided_page_range(p, number, on_each_side=3, on_ends=2):
paginator = Paginator(p.object_list, p.per_page)
return paginator.get_elided_page_range(number=number,
on_each_side=on_each_side,
on_ends=on_ends)
template.html
{% load paginator_tags %}
{% get_proper_elided_page_range paginator page_obj.number as page_range %} <!-- here -->
{% for page_num in page_range %}
{% if page_obj.number == i %}
<li class="active page-item">
<span class="page-link">{{ i }}</span>
</li>
{% else %}
{% if i == paginator.ELLIPSIS %}
<li class="page-item">
<span class="page-link">{{ paginator.ELLIPSIS }}</span>
</li>
{% else %}
<li class="page-item">
<a class="page-link" href="?page={{ i }}">{{ i }}</a>
</li>
{% endif %}
{% endif %}
{% endfor %}
You can add additional arguments, e.g:
{% get_proper_elided_page_range paginator page_obj.number 1 1 as page_range %}
If you are using a class-based view such as ListView in the question's example, then if you trigger pagination by setting the paginate_by member, the page_obj object is available in the get_context_data method. From this method, you can invoke get_elided_page_range on the Paginator to set extra context for the template being rendered.
Example:
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(**kwargs)
page: Page = context['page_obj']
context['paginator_range'] = page.paginator.get_elided_page_range(page.number)
return context
You can then access paginator_range from within your template.
Usage note: this method returns a generator which yields either an int (which is a valid page number) or a str (…), which is not.

Active nav-item highlighting based on pk passed in url

I have 4 nav-items being generating dynamically based on the Django Model entries.
My template code is as below:
{% for stockbroker in stockbrokers %}
<li class="nav-item">
<a class="nav-link" href="{% url 'broker:display_stocks' stockbroker.id %}" id="nav">{{ stockbroker.broker_name }}
</a>
</li>
{% endfor %}
I want to highlight the currently active nav based on the id I am passing in the url section of the a tag href. Is it possible to achieve this?
I am generating these nav links in the base.html using context_processors.py
from .models import StockBroker
def stockbrokers(request):
return {'stockbrokers': StockBroker.objects.all()}
An if condition should work for this:
{% for stockbroker in stockbrokers %}
<li class="nav-item">
<a class="nav-link {% if stockbroker.id == current_id %}active{% endif %}" href="{% url 'broker:display_stocks' stockbroker.id %}" id="nav">{{ stockbroker.broker_name }}
</a>
</li>
{% endfor %}
Note: current_id (or variable name of your preference) should be passed in the context.
The context is the dictionary that is passed while rendering the template, example:
def my_view(request):
# View Code
return render(request, 'template_name.html', {'current_id': current_id})

Pagination using ListView and a dynamic/filtered queryset

I have a class based view that I am using to display a queryset in a table. I am also using a couple formsets to filter this queryset. I am using the get_queryset() method provided as part of the generic.ListView class to filter the diplayed results. Here is basically what my class looks like:
from django.views import generic
class UnifiedSingleSearch(generic.ListView):
template_name = 'app/foo.html'
model = MyModel
paginate_by = 30
def get_queryset(self):
if self.request.POST: # If we got here because of a search submission, filter
return MyModel.objects.filter('Some stuff base on the POST data')
return MyModel.objects.all() # Otherwise, just show everything
Because I am using a formset to submit multiple search criteria, I have to use a POST request. Upon initial submission of the form, the page reloads with a correctly filtered querset. However when I try to use my pagination controls, the POST request is thrown away and the page acts as if I am going to page#2 of MyModel.objects.all() instead of my filtered down subset.
How can I retain my filtered queryset when using pagination controls?
Here is the HTML for the pagination controls:
{% if is_paginated %}
<nav aria-label="Pagination nav">
<ul class="pagination">
{# Back a page #}
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}">❮</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">❮</span>
</li>
{% endif %}
{# Page numbers #}
{% for i in paginator.page_range %}
{% if page_obj.number == i %}
<li class="page-item active">
<span class="page-link">{{ i }}
<span class="sr-only">(current)</span>
</span>
</li>
{% else %}
<li class="page-item">
<a class="page-link" href="?page={{ i }}">{{ i }}</a>
</li>
{% endif %}
{% endfor %}
{# Next page #}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}">❯</a>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">❯</span>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% else %}
<p>No MyModel objects found</p>
{% endif %}
Switching to a GET request was the answer. Pagination controls also had to be edited a bit to retain the passed querystring. They should all include {{ request.GET.urlencode }} and then you tack on the page logic to the end like usual. Ends up looking something like this:
<li class="page-item">
<a class="page-link" href="?{{ request.GET.urlencode }}&page={{ page_obj.next_page_number }}">❯</a>
</li>

Flask: request.view_args always returns an empty dicitonary [duplicate]

This question already has answers here:
Get the data received in a Flask request
(23 answers)
How do you access the query string in Flask routes?
(13 answers)
Closed 4 years ago.
I am trying to paginate my products catalog using paginate() function that comes with Flask .
Inside catalog i am using a search filter to sort products depends on the filter itself, in the url i have more than 5 to 10 arguments, these arguments are always changing and i want the paginate path to contain them .
Inside template i am doing this:
{% if pagination.has_next or pagination.has_prev %}
{{
macros.pagination_widget(
pagination,
request.endpoint,
args=request.view_args
)
}}
{% endif %}
The issue is, request.view_args always returns an empty dictionary , in fact the arguments are in url .
If i tried to hardcode all the arguments by using just some logic like {% if %} and {% else %} which i don't want to, the pagination works just fine .
I solved the problem, by using request.query_string and creating a macro helper everything now is working just fine.
Here is the solution:
_macros_pagination.html
{% macro pagination_widget(pagination, endpoint, extra='') %}
<ul class="uk-pagination uk-flex-center" uk-margin="">
<li{% if not pagination.has_prev %} class="disabled"{% endif %}>
<a href="{% if pagination.has_prev %}{{ url_for(endpoint, page=pagination.prev_num, **kwargs) }}&{{extra}}{% else %}#{% endif %}">
<span uk-pagination-previous=""></span>
</a>
</li>
{% for p in pagination.iter_pages() %}
{% if p %}
{% if p == pagination.page %}
<li class="uk-active">
{{ p }}
</li>
{% else %}
<li>
{{ p }}
</li>
{% endif %}
{% else %}
<li class="uk-disabled"><span>...</span></li>
{% endif %}
{% endfor %}
<li{% if not pagination.has_next %} class="disabled"{% endif %}>
<a href="{% if pagination.has_next %}{{ url_for(endpoint, page=pagination.next_num, **kwargs) }}&{{extra}}{% else %}#{% endif %}">
<span uk-pagination-next=""></span>
</a>
</li>
</ul>
{% endmacro %}
template.html
{% import "_macros_pagination.html" as macros %}
{% if pagination.has_next or pagination.has_prev %}
{{
macros.pagination_widget(
pagination,
request.endpoint,
extra=request.query_string
)
}}
{% endif %}

Custom tag for pagination

I've been trying to construct my own tag for pagination - pagetag.
But when I render this:
{% if people.has_previous %}
<li><a href={% pagetag people.previous_page_number %}>«</a></li>
{% else %}
<li class="disabled">«</li>
{% endif %}
{% for page in people.paginator.page_range %}
{% if page == people.number %}
<li class="disabled">{{page}}</li>
{% else %}
<li><a href={% pagetag page %}>{{page}}</a></li>
{% endif %}
{% endfor %}
{% if people.has_next %}
<li><a href={% pagetag people.next_page_number %}>»</a></li>
{% else %}
<li class="disabled">»</li>
{% endif %}
I get folowing links on first page:
<li class="disabled">«</li>
<li class="disabled">1</li>
<li><a href=/?page=page>2</a></li>
<li><a href=/?page=page>3</a></li>
<li><a href=/?page=people.next_page_number>»</a></li>
Not the desired ?page=2 and so on.
def pagetag(parser, token):
tokens = token.split_contents()
print tokens
if len(tokens) < 1:
raise TemplateSyntaxError, "pagetag takes at least 1 arguments"
return PageNode(tokens[1].strip())
How to pass the arguments correctly?
Use simple tags. For example:
#register.simple_tag
def pagetag(page):
return '/?page=' + page
Then, in your view, make sure to use {{ }} instead of {% %}:
<li>{{page}}</li>
Note: It's not recommended to use a different tag just for prepending a simple string. Just do it like in the documentation.