Active nav-item highlighting based on pk passed in url - django

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})

Related

no reverse matching at / and crawler is not a registered namespace

I am trying to make a codeforces crawler and I am just adding user authentication in the somehow failed to implement. Reverse not match and crawler is not a registered namespace is the error I'm getting. I don't know what files exactly are needed to put here so please ask me I will post them if you need it. I'm just a beginner and I need help.
crawler/urls.py
app_name = 'crawler'
urlpatterns = [
path('',views.index,name='index'),
path('formpage/',views.search_form_view , name='searchform'),
path('formpage/<str:handle>',views.person, name= 'person'),
path('user_login/',views.user_login,name ="user_login"),
path('logout/',views.user_logout,name="logout"),
]
base.html
<body>
<nav class="navbar navbar-expand-sm bg-dark navbar-dark">
<!-- Brand -->
<a class="navbar-brand" href="{% url 'crawler:index'%}">Crawler</a>
<!-- Links -->
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{% url 'crawler:searchform'%}">Search</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link 2</a>
</li>
{% if user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="{%url 'crawler : logout'%}">Log Out</a>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{%url 'crawler :user_login'%}">Login</a>
{% endif %}
</li>
</li>
</ul>
</nav>
<br>
{% block body_block %}
{% endblock %}
</body>
views.py
#login_required
def user_logout(request):
logout(request)
return HttpResponse(reverse('index'))
webcrawler/urls.py
app_name = 'crawler'
urlpatterns = [
path('admin/', admin.site.urls),
path('',include('crawler.urls',namespace= "crawler")),
]
A two-part answer here.
No reverse match at /
The point is that, in both your main and app urls.py, you registered both URLs to be '', which means that there will be a match at (empty string) but won't be one at '/'. To fix this, simply add '/' to the main urls.py as it's a better practice.
Crawler is not a registered namespace
As you call an URL, it should be {% url 'crawler:index' %} or {% url 'crawler:searchform' %} or something because crawler is the main namespace but there are multiple URLs under it so you need to pass an additional parameter after your crawler namespace.

How to loop through urls in a template

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>

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>

Django - Change active navbar template based on webpage

I have a template that looks like this in my html. It uses the bootstrap classes.
<-- navbar-template.html>
<div class="collapse navbar-collapse" id="myNavbar">
<ul class="nav navbar-nav">
<li>Home</li>
<li class='active'>Members</li>
<li>Research</li>
<li>Publications</li>
<li>Links</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><span class="glyphicon glyphicon-log-in"></span> Login</li>
</ul>
</div>
I like the active class but I need the to change which list object it is based on which page from the navbar django has loaded.
I think you'd like to do something like this in the home.html file
<-- HOME -->
{% include "navbar_template.html" with page="home"} %}
###Do something with {{page}} variable in order to set the home list tag to active.
Do I have to write a crazy amount of if else statements or is there an easier way. Perhaps something with the views.py in django
You can do it like this (example solution I use on my page):
<ul class="sidebar-nav--primary">
{% url 'main:home' as home %}
{% url 'main:work' as work %}
{% url 'main:personal' as personal %}
<li><a href="{{ home }}" {% if request.path == home %}class="active"{% endif %}>Home</a></li>
<li><a href="{{ work }}" {% if request.path == work %}class="active"{% endif %}>Work</a></li>
<li><a href="{{ personal }}" {% if request.path == personal %}class="active"{% endif %}>Personal</a></li>
</ul>
A cleaner method would be creating a custom template tag. Something like is_active:
# Inside custom tag - is_active.py
from django.template import Library
from django.core.urlresolvers import reverse
register = Library()
#register.simple_tag
def is_active(request, url):
# Main idea is to check if the url and the current path is a match
if request.path in reverse(url):
return "active"
return ""
And use it in your templates like this:
# template.html
{% load is_active %}
<li>Home</li>

Share same pagination code for different views in Django

I'm building a simple blog app using Django.
This app has a main template blog.html which is shared between the following views:
blog (url: /blog/[page number])
Main page of the blog, displays the last articles
search (url: /search/<query>/[page number])
Will display the articles who match the query
category (url: /category/<category name>/[page number])
Will display the articles from the given category
Each a this views provides the template blog.html with an object page obtained using the function Paginator each time with a different objects list (depending on the view).
Here is my problem:
The template blog.html contains a pager
<ul class="pager">
{% if page.has_previous %}
<li class="previous">
<a href="{{ url_previous_page }}" class="btn btn-primary" >← Older</a>
</li>
{% endif %}
{% if page.has_next %}
<li class="next">
<a href="{{ url_next_page }}" class="btn btn-primary" >Newer →</a>
</li>
{% endif %}
</ul>
How can I define in an elegant way the values of url_next_page and url_previous_page in all the views?
You shouldn't really need to supply the links for the next and previous buttons like that. I would suggest changing your pagination code to be something like this:
<div class="pagination-wrap">
<span class="pagination-links">
{% if page_obj.has_previous %}
{% endif %}
<span class="current">
{{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
{% endif %}
</span>
<div>{{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ page_obj.paginator.count }}</div>
</div>
In your views, all you need to specify is the number of objects to paginate by with paginate_by = 15.
If you really want to, you could create a mixin for your list views to use which has a method that could return the url you want.