Django - Simple custom template tag example
DjangoDjango ModelsDjango TemplatesDjango Problem Overview
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 https://stackoverflow.com/questions/6388259/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 ?
Django Solutions
Solution 1 - Django
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>
Solution 2 - Django
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: