Django: identify app in template - python

I have three apps in my Django project that correspond to separate parts of the website. All three parts are accessible from the navigation menu, defined in common base.html template.
I want to be able to identify the app that called the template to add an 'active' css class to the menu option corresponding to the active part of the site.
What's the best way to do it, short of modifying each view to pass an additional variable to the template?

The most non-invasive way would be:
Write a template tag that generates your menu.
Update your context with the application name.
Pass this to the template tag and modify the css accordingly.
I'll explain the second bit, as writing custom template tags is covered in detail in the django documentation.
To update the context; we need some middleware. This should do what you need:
class SetApplicationName(object):
def process_view(self, request, view_func, view_args, view_kwargs):
request.current_app = view_func.__module__.split('.')[0]
Place this somewhere django can find it (in any directory in PYTHONPATH), and add it to your middleware classes. Also, make sure you have django.core.context_processors.request in your template context processors.
In your templates, now you have {{ request.current_app }} which should point the app name.
Your template tag should be something like {% navigation_menu request.current_app %}, and you can then modify your menu css accordingly.

I would try "overriding" the {%block%} tag.
In your base.html template put something like:
{%block navigation_bar%}
<div class="regular">First app</div>
<div class="regular">Second app</div>
<div class="regular">Third app</div>
{%endblock%}
In your descendant templates, change that navitation_bar block with other. first_app_base.html looks like:
{%extends "base.html"%}
{%block navigation_bar%}
<div class="active">First app</div>
<div class="regular">Second app</div>
<div class="regular">Third app</div>
{%endblock%}

If you define navigation menu in separate template, you could include it with additional context.
base.html:
{% block navigation %}Here will be navigation{% endblock %}
template_from_some_app.html:
{% extends "base.html" %}
{% block navigation %}
{% include "navigation.html" with active_app='second_app' %}
{% endblock %}
navigation.html:
<ul class="nav">
<li {% if active_app == 'first_app' %} class="active">{% endif %}>
First app
</li>
<li {% if active_app == 'second_app' %} class="active">{% endif %}>
Second app
</li>
<li {% if active_app == 'third_app' %} class="active">{% endif %}>
Third app
</li>
</ul>

Related

Pass additional context variable data into allauth views using class based views

Not sure how to pass additional context data into various allauth forms which includes my own templates. For my own views I'm using get_context_data() which is working fine. I'm including small templates into a master template such as a header, footer, side bar etc. Everything is working except when allauth kicks in such as login, logout, email confirmation window etc my context variables are not passed so images in my left side bar are not showing up but allauth works fine.
I've tried a few things but I believe the ideal option is to inherit from allauth views for that function such as login, password reset, confirm email etc, supply my own context variable data.
In my accounts.views.py, I'm expecting this to fail as the template doesn't exist but the form still shows up and the UserProfile image isn't being shown in the left side bar.
from allauth.account.views import ConfirmEmailView
class EmailViewExt(ConfirmEmailView):
template_name = "account/signup_alternate1.html"
def get_context_data(self, **kwargs):
context = super(ConfirmEmailView).get_context_data(**kwargs)
context['userprofile'] = UserProfile.objects.get(user=self.request.user)
return context
In my template left_bar_base.html which is included from my overridden allauth template.
{% if userprofile.avatar_picture %}
<img src="{{ userprofile.avatar_picture.url }}" class="card-img-top" alt="...">
{% else %}
<img src="{% static 'placeholder.png' %}" class="card-img-top" alt="...">
{% endif %}
In my email_confirmation.html I have this at the top.
{% extends "ui/base.html" %}
{% load i18n %}
{% load account %}
{% block header %}
{% include "ui/loggedin_header.html" %}
{% endblock %}
{% block left %}
<div class="row no-gutters justify-content-start">
{% include 'ui/left_bar_base.html' %}
{% endblock %}
{% block content %}
... allauth template code...
Came across the solution.
My EmailViewExt(ConfirmEmailView) was never being called.
So instead of using allauths.urls I put this right above the allauths.urls.
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/email/', EmailViewCustom.as_view(), name="email"),
path('accounts/', include('allauth.urls')),
Now the context variable I've added is being passed into my templates. So I guess I have to add all of the urls from allauth for the ones I want to replace.

How to add new created page url to modified django-oscar

I'm a beginner in django-oscar and I try to manage a new view on the page.
I've already created two pages with django-oscar dashboard,
https://ibb.co/cM9r0v
and made new buttons in the templates:
Lib/site-packages/oscar/templates/oscar/partials/nav_primary.html
https://gist.github.com/Kalinar/076fc8144869c3b50fc0bc9e52f825e4
I have no idea how to make a good a href="???" to new pages in buttons ... can someone help?
Maybe there is better way to do it, can you explain it to me?
Oscar uses Django's flatpages app, so you can use their template tags to dynamically add in navigation links to the pages you create.
{% load flatpages %}
{% get_flatpages as flatpages %}
{% for page in flatpages %}
<li class="dropdown active">
<a href="{{ page.url }}" class="dropdown-toggle" {% if not expand_dropdown %} data-toggle="dropdown"{% endif %}>
{% trans page.title %}
</a>
</li>
{% endfor %}
You can find more information on the flatpages app in the Django flatpage documentation.
You already know the title and of course the id of the page, and each page is an instance of the model PagePromotion, just query the model for such name/id and use the attribute page_url of the returned instance to send it within a variable in the context of the view that renders the menu.
Then in the template:
href="{{ variable_containing_url }}"

Is it possible to load a custom template tag in base and use it in extented templates

I loaded a custom template tag note_extras.py in base.html.
base.html
<div id="wrap">
{% load note_extras %}
{% block content %}
{% endblock %}
</div><!--wrap-->
but it is not accessible at templates which is an extend of base.html ie::
home.html
{% extends "base.html" %}
{% block content %}
<div class="container">
{% create_tagmenu request.user.pk %}
</div>
{% endblock %}
it is working fine if i load note_extras in home.html ie:
{% extends "base.html" %}
{% load note_extras %}
....
In Django template language, you must load all needed template libraries in each of the templates.
I personally think it is a good idea because it makes templates more explicit (which is better than implicit). Ill give an example. Prior to Django 1.5, the default behavior for a url tag was to provide the view name in plaintext as well as all the needed parameters:
{% url path.to.view ... %}
There however was no way to provide the path to the view via a context variable:
{% with var='path.to.view' %}
{% url var ... %}
{% endwith %}
To solve that, starting with 1.3, you could import the future version of the url tag (which became the default in 1.5) by doing:
{% load url from future %}
{% url var ... %}
or
{% url 'path.to.view' ... %}
Now imagine that you would need to create a template which would extend from a base template which you did not create (e.g. one of django admin base templates). Then imagine that within the base template it would have {% load url from future %}. As a result, {% url path.to.view ... %} within your template would become invalid without any explicit explanation.
Of course this example does not matter anymore (starting with 1.5) however hopefully it illustrates a point that being explicit in templates is better than implicit which is why the currently implementation is the way it is.
If you want that a template tag is loaded in every template you want to do it in the init file of your app:
from django.template.loader import add_to_builtins
add_to_builtins('my_app.templatetags.note_extras')
In case anyone was wondering, add_to_builtins has been deprecated but one could still load a tag for all of the templates in the project via settings.TEMPLATES - supported for Django 1.9 onwards as described here:
https://stackoverflow.com/a/59719364/2447803

Adding to, Not Overriding a Template Block

I have a block of code in a Django template for a simple blog engine:
{% block mainLeft %}
<section class="container" id="main">
<section class="offset1 span8" id="mainLeft">
</section>
{% endblock %}
What would the correct way to add content within the #mainleft section for different templates be? For instance, if I wanted to dynamically generate divs within the section tag based on info passed in from a context.
Use {{ block.super }} in the block. See the docs.

Jinja - Is there any built-in variable to get current HTML page name?

i'm very new to Jinja and Flask
I want to set different background color in the navigation bar to indicate the current page.
Is there any built-in Jinja variable or method that returns current HTML pages? If possible, I want the code that doesn't need to communicate with the Python file.
So if i'm currently in index.html, it will return "index" or "index.html"
Here's my navigation code in my template:
<ul>
{% for item in navigation %}
<a href="{{url_for(item.route)}}">
<li>
{{item.text}}
</li>
</a>
{% endfor %}
</ul>
I want to add if statement so the current page will get <li> that has class
{% if ??? %}
<li class="current">
...
</li>
{% else %}
...
{% endif %}
Thank You
There is a trick in jinja2 document for your problem: http://jinja.pocoo.org/docs/tricks/
If your list is simple enough, just using request object, something like that:
<li {% if request.endpoint == item.endpoint %} class='active' {% endif %}>
{{item.text}}
</li>
Normally, I write this snippet to a macro with an explicit argument to set active:
{% macro render_sitem(endpoint, display, cls='', icon-cls='', active='') %}
<li {% if request.endpoint == endpoint or active == endpoint %} class='active' {% endif %}>
<a class='{{cls}}' href="{{url_for(endpoint)}}"><i class="{{icon-cls}}"></i> {{display}}</a>
</li>
{% endmacro %}
The list will be something like:
<ul class="nav nav-list">
{{render_sitem('page.index', _('Pages'), icon-cls='icon-sitemap', active=active_page)}}
{{render_sitem('post.index', _('Posts'), icon-cls='icon-file', active=active_page)}}
{{render_sitem('user.index', _('Users'), icon-cls='icon-group', active=active_page)}}
</ul>
So if you have a child page which extends or includes your list, you can set active item like:
{% set active_page = 'page.index' %}
in the top of your child page.
In pyramid 1.5 there are no method like request.endpoint in Flask.
We use custom filter get_endpoint
request.path|get_endpoint
jinja2_custom_filters.py:
from pyramid_jinja2 import Environment
def get_endpoint(str):
"""
:param str:
:return:
"""
return str.split('/')[-1]
env = Environment()
env.filters['get_endpoint'] = get_endpoint
and in development.ini:
jinja2.filters =
model_url = pyramid_jinja2.filters:model_url_filter
route_url = pyramid_jinja2.filters:route_url_filter
static_url = pyramid_jinja2.filters:static_url_filter
get_endpoint = path to ... jinja2_custom_filters.get_endpoint
Maybe it will be useful to someone :)
In Flask 2.0.1, the request object is available in the template. With this you can easily use it to check the page using the request.path attribute.
An example of a check would be like this:
{% if request.path == "/" %}
<h1>You are at the root</h1>
{% endif %}

Categories