Passing arguments into partials automatically - django

The problem is the header.html partial always contains categories dictionary that is kept on database. Including this partial with arguments
{% include "_partials/header.html" with categories %}
Every time on rendering partials I need to pass categories dictionary
render("index.html", {"flowers":flowers, "categories":categories})
render("details.html", {"flower":flower, "categories":categories})
...
Is there any solution, that header.html partials always contains categories dictionary.

Solved it using inclusion tags.
Created custom tag in the templatetags/tags.py file
from django import template
from flowers.models import Category
register = template.Library()
#register.inclusion_tag('_partials/nav.html')
def show_categories():
categories = Category.objects.all()
print categories
return {'categories':categories}
Created template for it in the _partials/nav.html file
<nav>
<ul>
{% for category in categories %}
<li>{{ category.name }}</li>
{% endfor %}
</ul>
</nav>
At the end, used that tag
{% load tags %}
{% show_categories %}

You should use a custom inclusion tag for this.

Related

Generating a unique list of Django-Taggit Tags in Wagtail;

I am trying to add a list of category tags in the sidebar of my Wagtail blog index page. The code below does work, but unfortunately it iterates through the posts and lists all the tags as individual tags, which I ultimately end up with duplicate tags. I built my blog from the Wagtail demo and since it doesn't use Views like I am used to, I am not sure where to add .distinct('tags').
Template
{% for b in blogs %}
{% for tag in b.tags.all %}
<li> <i class="glyphicon glyphicon-tag"></i> {{ tag }}<span>{{ tag }}</span>
{% if not forloop.last %} {% endif %}
</li>
{% endfor %}
{% endfor %}
Any logic that would normally go in a view function, can go in the page model's get_context method:
from django.contrib.contenttypes.models import ContentType
from taggit.models import Tag
class BlogIndex(Page):
# ...
def get_context(self, request):
context = super(BlogIndex, self).get_context(request)
blog_content_type = ContentType.objects.get_for_model(BlogPage)
context['tags'] = Tag.objects.filter(
taggit_taggeditem_items__content_type=blog_content_type
)
return context
(the tag-fetching code here is adapted from some internal Wagtail code.)

Custom Template Tag Queryset Not Returning Anything

I'm trying to implement a feature that displays the 5 most recently created events. I decided to implement this with Django custom template tags (if this is not the best way, let me know). What I have so far is:
In event_search.html (among other things):
{% extends 'base.html' %}
{% load eventSearch_extras %}
<p>count: {{ recents.count }}</p>
<ul>
{% for e in recents %}
<li> {{e.title}} </li>
{% empty %}
<li> No recent events </li>
{% endfor %}
</ul>
In eventSearch_extra.py:
from django import template
from eventSearch.models import Event
register = template.Library()
#register.inclusion_tag('eventSearch/event_search.html')
def mostrecentevents():
"""Returns most 5 most recent events"""
recents = Event.objects.order_by('-created_time')[:5]
return {'recents': recents}
My issue here is that the queryset 'recents' appears to return empty to the template. 'count:' shows nothing & the for-loop defaults to 'No recent events'.
You've loaded the inclusion tag function, but not the individual tag, so the code to populate that information is never called; it's also laid out slightly oddly, so you're calling from the wrong place.
The main template calls the inclusion tag by using:
{% load eventSearch_extras %}
And you include the actual tag by calling
{{mostrecentevents}}
mostrecentevents goes off and runs the code, parses the html of event_search.html and puts it in the main template. The way your code is set out just now, you'd be calling an inclusion tag from its own HTML.
Main template > {% load inclusion_tags %} {{ actual_tag }}
As an example, I have a restaurant template. In that template is this code:
{% load restaurant_menu %} <!--main inclusion tag .py file) -->
{% menu %} <!-- the actual tag code you want to run -->
in restaurant_menu.py I have the following (additional irrelevant stuff removed):
#register.inclusion_tag('core/_menu.html', takes_context=True)
def menu(context):
filtered = context['filtered']
from core.models import MenuItem, FoodProfile, Ingredient, Recipe
if filtered:
restaurant = context['restaurant'].id
filtered_menu = #stuff here
restaurant_menu = filtered_menu
else:
restaurant_menu = MenuItem.objects.filter(restaurant__pk=context['restaurant'].id)
return {"restaurant_menu": restaurant_menu,
"number_of_menu_items": restaurant_menu.count(),
"filtered": filtered}
and the _menu.html page (underscored so I know it's a fragment) :
<ul>
{% for item in course.list %}
<li>
{{ item.number|floatformat:0 }} {{ item.name }} {{ item.description }} {{ item.price }} </li>
</li>{% endfor %}
{% endfor %}
</ul>
An inclusion tag is used to render another template. It doesn't make sense to create an inclusion tag that renders event_search.html, then call that template tag inside event_search.html itself. Note that you haven't actually used the template tag (with {% mostrecentevents %}), all you have done is load the template tag library.
It would be easier to use a simple tag instead.
#register.simple_tag
def mostrecentevents():
"""Returns most 5 most recent events"""
recents = Event.objects.order_by('-created_time')[:5]
return recents
Then in your template you can do:
{% load eventSearch_extras %}
{% mostrecentevents as recents %}
This loads the result of the template tag into the variable recents, and you can now do:
<p>count: {{ recents.count }}</p>
<ul>
{% for e in recents %}
<li> {{e.title}} </li>
{% empty %}
<li> No recent events </li>
{% endfor %}
</ul>
Note you can only use the as recents syntax with simple tags with Django 1.9+. For earlier versions, you can use an assignment tag instead.

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!

Can you make a custom template tag that returns a queryset? If yes, how? - Django

Let's make this very easy for my fellow SOians(?).
This is how normally the custom template tags work -
Template ->
{% block content %}
blah blah blah
{% custom_tag_load %}
{% endblock %}
The custom_tag_load is called and it returns a string. What I want to return is a queryset which I could possibly use like this ->
{% block content %}
blah blah blah
{% for x in custom_tag_load %}
{{ x.datetime }}
{% endfor %}
{% endblock %}
Note -> What I'm basically trying to do is to avoid passing the queryset through the view, and I'm not sure if I should be comfortable storing querysets in my global context.
You can return anything you like from a tag, including a queryset. However, you can't use a tag inside the for tag - you can only use a variable there (or a variable passed through a filter). What you could do is get your tag to put the queryset into a variable in the context, and use that variable in the for loop. See the docs on how to set a variable from a tag - although note that the development version has an easier method for doing this.
However, you shouldn't be concerned about putting a queryset into a context processor, either. Don't forget that querysets are lazy, so no database hit will be made unless the queryset is evaluated or iterated in the template.
A template tag can do whatever you want. From your pseudo code, you could accomplish what you need with an inclusion tag:
#my_tags.py
from django import template
from my_app.models import MyModel
register = template.Library()
#register.inclusion_tag('my_template.html')
def my_custom_tag():
things = MyModel.objects.all()
return {'things' : things}
#my_template.html
{% if things %}
<ul>
{% for thing in things %}
<li>{{ thing }}</li>
{% empty %}
<li>Sorry, no things yet.</li>
{% endfor %}
</ul>
{% endif %}
#the_view.html
{% load my_tags %}
{% my_custom_tag %}
Alternatively, you could write a custom tag that adds a queryset to the context. Hope that helps you out.
I had these same problems recently and most of the answers here were kind of outdated, but a little digging through Django's documentation and I was able to sort it out.
Like most of the answers above, you can return basically anything using a template tag, but it all depends on how you register the template tags. So say you want to use a template tag to return a queryset to be available for any template you wish, you could register the template tag as a simple tag just like this
from django import template
from blog.models import Post
from django.template.defaulttags import register
register = template.Library()
#register.simple_tag
def get_posts():
return Post.objects.all()
Then to be able to access this in your template, you first need to load this file in your template like
{% load templatetagfile %}
And then to loop through, you need to first assign it to a variable before looping through
{% get_posts as posts %}
{% for post in posts %}
{{ post.whatever }}
{% endfor %}
The first line there makes the queryset from the get_posts function available as a variable named posts which you can then loop through.

Django - Simple custom template tag example

I have users, videos, topics, criterias and ratings
A video has a topic
A topic has criterias
A user can create a video for a given topic
A user can rate a video on each criterias given for the concerned topic.
You can see my original post Django - Rating Model Example DetailView Template to get details on the model used
I have extended a DetailView template based on the video model to put the list of ratings for the selected video for a given user as extra context.
class VideoFileDetailView(DetailView):
model = VideoFile
def get_context_data(self, **kwargs):
context = super(VideoFileDetailView, self).get_context_data(**kwargs)
context['rates'] = VideoRate.objects.filter(video=self.object, user=self.request.user)
return context
In the template pointed by the DetailView, I'd like to list the criterias of the video, and for each criteria display the current rating value form the user.
<div id="rating">
<ul>
{% for crit in videofile.topic.crits.all %}
<li>
{% for rate in rates %}
{% if rate.crit.id == crit.id %}
{{ rate.rate }}
{% endif %}
{% endfor %}
<div class="rateit"
data-rateit-value="{# The rating value #}"
data-rateit-ispreset="true"
crit-id="{{ crit.id }}"></div>
{{ crit }}
</li>
{% endfor %}
</ul>
</div>
(rateit is a jquery plugin that I use to draw pretty stars rating controls)
Actually I get my rating values here within the 2nd for but I'm sure there is a better way to do that. In fact, I'm still not sure about my model correctness.
Finally I'd like to replace {# The rating value #} by the rating value from rate for the current crit (in the loop). How can I do that ?
Here is my solution (based on a custom tag):
Firstly create the file structure. Go into the app directory where the tag is needed, and add these files:
templatetags
templatetags/__init__.py
templatetags/video_tags.py
The templatetags/video_tags.py file:
from django import template
register = template.Library()
#register.simple_tag
def get_rate(crit, rates):
return rates.get(crit=crit).rate
The template part, with our tag call:
{% load video_tags %}
<div id="rating">
<ul>
{% for crit in videofile.topic.crits.all %}
<li>
<div class="rateit"
data-rateit-value="{% get_rate crit rates %}"
data-rateit-ispreset="true"
crit-id="{{ crit.id }}"></div>
{{ crit }}
</li>
{% endfor %}
</ul>
</div>
Inline HTML in tag
If the HTML is small, this method is more convenient than creating a separate file.
This example factors out links to user profiles. The file templatetags/somemodule.py contains:
from django import template
from django.template import Template
register = template.Library()
#register.simple_tag(takes_context=True)
def user_link(context):
return Template('<a href="{% url \'user_detail\' ' +
'user.id %}">{{ user.username }}</a>').render(context)
Template#render already returns a safe string which is not XSS escaped. E.g. if we had done just:
return '<br>'
it would be escaped. You might also want to play with mark_safe.
You can make that tag available on all views with:
TEMPLATES = [
{
'OPTIONS': {
'builtins': [
'myprojectname.templatetags.somemodule',
in settings.py.
See also:
https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/
Rendering a template variable as HTML