So we're writing a super simple game-app (answer questions, get points) and we want to render a high-score in the side-bar of every screen on the page.
So I understand that Django automatically (kinda) passes the authenticated user as argument (if we use render() ) but I'm not sure how to pass what basically will be:
top_list = User.objects.filter().order_by('-score')[:10]
so that we can fix a nice little top list in our base template :)
We've looked around at different questions here, like this one for example and thought about whether the solution would be to write our own context processor?
EDIT:
Might be a duplicate of this so should I perhaps write a custom template tag instead?
You can use django template tags to do it..
Under myapp/templatetags/ create a myapp_tags.py and __init__.py
In myapp_tags.py do the following:
from django import template
register = template.Library()
#register.assignment_tag
def get_top_list():
top_list = User.objects.filter().order_by('-score')[:10]
return top_list
And then use it in your templates like this:
{% load myapp_tags %}
{% get_top_list as top_list %}
{% for top_user in top_list %}
...
Related
In a Django project, I have a mini navbar that is common in ~30% of my templates. Instead of including it in my global base.html, I decided to take a different route.
I first wrote a separate view for this:
from django.template.loader import render_to_string
def navbar(origin=None):
if origin == '1':
locations = get_approved_loc(withscores=True)
else:
locations = get_approved_loc()
obj_count = get_all_obj_count()
return render_to_string("mini_navbar.html",{'top_3_locs':locations[:3],\
'other_cities':len(locations[3:]),'obj_count':obj_count})
I next added it in the templates it needed to be in via:
{% include "mini_navbar.html" with origin='1' %}
When I run this code, I get a NoReverseMatch error. It seems the view function navbar never runs. So the context variables it was sending in (e.g. top_3_locs or other_cities etc) are never populated. Hence NoReverseMatch.
What's wrong with this pattern, and what's the fix for it? An illustrative example would do the trick.
Rather than including a template directly, you should write a custom template tag - specifically, an inclusion tag that renders the template with the custom context. The code that you have put in that separate view goes in that template tag instead.
Here's an illustrative example of Daniel's suggestion:
I created an 'inclusion' template tag like so:
from django import template
from redis_modules import get_approved_loc, get_all_obj_coun
register = template.Library()
#register.inclusion_tag(file_name='mini_navbar.html')
def mini_navbar(origin=None):
locations = get_approved_loc(withscores=True)
obj_count = get_all_obj_count()
return {'top_3_locs':locations[:3],'origin':origin,'other_cities':len(locations[3:]),'obj_count':obj_count}
Next I included this in the relevants templates like so:
{% load get_mini_navbar %} <!-- name of the module -->
And finally, I called it within the template like so:
{% mini_navbar origin %}
Where origin is the parameter passed into the tag.
So I often have to use something like the following in my Django templates:
{% include "conname.html" %}
I'd like to be able to instead just have my own tag and only have to type something like
{% conname %}
Is there an easy way to setup some sort of alias so that when ever the template engine sees the conname tag it knows that should actually be a specific include tag?
Pretty simple, in a module just add the following code (mine are usually called custom_tags.py:
from django import template
register = template.Library()
#register.simple_tag
def conname():
return '''{% include "conname.html" %}'''
#to make it available everywhere (i generally add this to my urls.py
from django.template.loader import add_to_builtins
#this will be aded to all templates
add_to_builtins('custom_tags')
In your template you would just use {% conname %}
Or you could make it more complicated and programatically call IncludeNode but that is overkill.
I am trying to put a login form in every page in my web that uses django.contrib.auth.views.login. I created a templatetag in templatetags/mytags.py, where I define a function called get_login wich looks like this:
#register.inclusion_tag('registration/login.html', takes_context=True)
def get_login(context):
...
return {'formLogin': mark_safe(AuthenticationForm())}
...and in base.html:
{% load mytags %}{% get_login %}
The problem now is that the template (registration/login.html) doesnt recognize {{ formLogin.username }},{{ formLogin.password }}... and so on.
What am I missing?
mark_safe returns an instance of django.utils.safestring.SafeString, not a form, so those lookups will fail. I don't think there's anything wrong with directly returning the form (that's what all the generic views in django.contrib.auth do when populating templates, for instance). Just change your return statement to
return {'formLogin': AuthenticationForm()}
and it should work.
A (not so) quick question about extending django admin templates.
I'm trying to change the result list (change list in django lingo) of a specific model by adding an intermediary row between the result rows (row1 and row2 classes) that contains some objects related to that object.
I searched the code but haven't found a way to do this. Any pointers are very much appreciated. Code will also help too.
PS: I know I should be designing my own interface, but this is an internal project and I don't have that much time to spare. Also, the django interface is really nice.
Thank you in advance.
To expand on Yuji's answer, here are some specifics on overriding change_list_results.html ...
Override changelist_view as described above in step 1, and also described here at djangoproject. Or auto-override by placing in the appropriate directory as in step 2 above. (Note that the step 2 path shown above is model-specific. App-specific would be /admin/<MyAppName>/change_list.html under any directory defined in the TEMPLATE_DIRS tuple.)
Or (perhaps easier) simply specify ModelAdmin.change_list_template as explained here with any discoverable template filename. (Although, if you retain the name change_list.html, be sure not to deposit directly into the /admin folder, else the extends tag will cause a recursion.)
class MyModelAdmin(admin.ModelAdmin):
change_list_template = 'change_list.html' # definitely not 'admin/change_list.html'
# ...
In your change_list.html template, have at a minimum
{% extends "admin/change_list.html" %}
{% load i18n admin_static admin_list %}
{% load myapptags %}
{% block result_list %}
{% if action_form and actions_on_top and cl.full_result_count %}{% admin_actions %}{% endif %}
{% result_list cl %}
{% if action_form and actions_on_bottom and cl.full_result_count %}{% admin_actions %}{% endif %}
{% endblock %}
Create a /<MyAppName>/templatetags package (a directory containing __init__.py) with a file corresponding to the load tag above
# MyAppName/templatetags/myapptags.py
from django import template
from django.contrib.admin.templatetags.admin_list import result_list
register = template.Library()
register.inclusion_tag('my_change_list_results.html')(result_list)
Copy and edit Django's change_list_results.html (as e.g. my_change_list_results.html above) to use your added functionality.
Note that these steps do not include extra context for the template, but can easily be expanded as such. (My reason for doing this was to add classes for CSS and a leading <tbody> that was not sorted with the results list.)
ADDITIONAL:
To include extra context, change your templatetags module as follows:
# MyAppName/templatetags/myapptags.py
from django import template
from django.contrib.admin.templatetags.admin_list import result_list as admin_list_result_list
def result_list(cl):
mycl = {'myextracontext': 'something extra'}
mycl.update(foo_extra())
mycl.update(admin_list_result_list(cl))
return mycl
register = template.Library()
register.inclusion_tag('my_change_list_results.html')(result_list)
Then, the value of myextracontext or whatever foo_extra returns can be included in your results template (as e.g. {{ myextracontext }})
Step 1: Overriding changelist view:
You'll have to override a template as opposed to specifying one like you can with add_view / change_view.
First things first, override
def changelist_view(self, request, extra_context=None): in your ModelAdmin. Remember to call super(foo, self).changelist_view(request, extra_context) and to return that.
Step 2: Overriding templates:
Next, override the app-specific changelist template at templates/admin/my_app/my_model/change_list.html (or not.. you can use a global changelist override too if you'd like).
Step 3: Copy result list functionality
I think you can either copy result_list functionality (define a new template tag) or fake it by copying and pasting the result_list function and template include into your view.
# django.contrib.admin.templatetags.admin_list
def result_list(cl):
"""
Displays the headers and data list together
"""
return {'cl': cl,
'result_hidden_fields': list(result_hidden_fields(cl)),
'result_headers': list(result_headers(cl)),
'results': list(results(cl))}
result_list = register.inclusion_tag("admin/change_list_results.html")(result_list)
You can see the admin uses this admin/change_list_results.html template to render individual columns so you'll need to use one of the methods to replace this template tag.
Since it's looking for a global template, I wouldn't override it.
Either define a new tag w/ new template specifically for your view, or send result_list(cl) to your template directly and adopt the admin/change_list_results.html for use directly in your change_list.html template.
If you want a complete override of the admin_list template tag, here is my post in another thread. https://stackoverflow.com/a/55597294/11335685
I know it is not directly related to the change_list template, but the logic for it lays on the functions inside the admin_list template tag.
A disadvantage of that approach is i didn't find a way of overriding only specific function and get use of the other ones.
Still that was the only approach that was allowing me to not mess with the html templates and only alter the behavior of result_list function.
Hope that helps anyone.
Short version:
Is there a simple, built-in way to identify the calling view in a Django template, without passing extra context variables?
Long (original) version:
One of my Django apps has several different views, each with its own named URL pattern, that all render the same template. There's a very small amount of template code that needs to change depending on the called view, too small to be worth the overhead of setting up separate templates for each view, so ideally I need to find a way to identify the calling view in the template.
I've tried setting up the views to pass in extra context variables (e.g. "view_name") to identify the calling view, and I've also tried using {% ifequal request.path "/some/path/" %} comparisons, but neither of these solutions seems particularly elegant. Is there a better way to identify the calling view from the template? Is there a way to access to the view's name, or the name of the URL pattern?
Update 1: Regarding the comment that this is simply a case of me misunderstanding MVC, I understand MVC, but Django's not really an MVC framework. I believe the way my app is set up is consistent with Django's take on MVC: the views describe which data is presented, and the templates describe how the data is presented. It just happens that I have a number of views that prepare different data, but that all use the same template because the data is presented the same way for all the views. I'm just looking for a simple way to identify the calling view from the template, if this exists.
Update 2: Thanks for all the answers. I think the question is being overthought -- as mentioned in my original question, I've already considered and tried all of the suggested solutions -- so I've distilled it down to a "short version" now at the top of the question. And right now it seems that if someone were to simply post "No", it'd be the most correct answer :)
Update 3: Carl Meyer posted "No" :) Thanks again, everyone.
Since Django 1.5, the url_name is accessible using:
request.resolver_match.url_name
Before that, you can use a Middleware for that :
from django.core.urlresolvers import resolve
class ViewNameMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
url_name = resolve(request.path).url_name
request.url_name = url_name
Then adding this in MIDDLEWARE_CLASSES, and in templates I have this:
{% if request.url_name == "url_name" %} ... {% endif %}
considering a RequestContext(request) is always passed to the render function. I prefer using url_name for urls, but one can use resolve().app_name and resolve().func.name, but this doesn't work with decorators - the decorator function name is returned instead.
No, and it would be a bad idea. To directly refer to a view function name from the template introduces overly tight coupling between the view layer and the template layer.
A much better solution here is Django's template inheritance system. Define a common parent template, with a block for the (small) area that needs to change in each view's version. Then define each view's template to extend from the parent and define that block appropriately.
If your naming is consistent in your urls.py and views.py, which it should be, then this will return the view name:
{{ request.resolver_match.url_name }}
Be sure to apply some context to it when you call it in the template. For example, I use it here to remove the delete button from my detail view, but in my update view the delete button will still appear!
{% if request.resolver_match.url_name != 'employee_detail' %}
Since Django 1.5 you can access an instance of ResolverMatch through request.resolver_match.
The ResolverMatch gives you the resolved url name, namespace, etc.
one simple solution is :
def view1(req):
viewname = "view1"
and pass this viewname to the template context
def view2(req):
viewname = "view2"
and pass this viewname to the template context
in template access the viewname as
{{viewname}}
and also you can use this in comparisons.
I'm working on this for a help-page system where I wanted each view to correspond to a help-page in my cms with a default page shown if no help page was defined for that view. I stumbled upon this blog where they use a template context processor and some python inspect magic to deduce the view name and populate the context with it.
This sounds like the perfect example of a generic view that you can set up.
See the following resources:
Django Book - Chapter 11: Generic Views
Django Docs -Tutorial: Chapter 4
Django Docs - Generic Views
These links should help you simplify your views and your templates accordingly.
If you're using Class Based Views, you most likely have a view variable you can access.
You can use several methods from that to determine which view has been called or which template is being rendered.
e.g.
{% if view.template_name == 'foo.html' %}
# do something
{% else %}
# other thing
{% endif %}
Another option is to take out the piece of the template where you need something to change and make it into a snippet and then use {% include 'my_snippet.html' with button_type = 'bold' %} in your templates, sending arbitrary values to the snippet so it can determine what to show / how to style itself.
Most generic views — if not all — inherits the ContextMixin which adds a view context variable that points to the View instance.
In your template, you can access the current view instance like this:
{{ view }}
Define class_name method in your view
class ExampleView(FormView):
...
def class_name(self):
return self.__class__.__name__
You can get the class name of the current view in a template like this:
{{ view.class_name }}
{% if view.class_name == "ExampleView" %} ... {% endif %}
Why not trying setting up a session cookie, then read the cookie from your template.
on your views set cookies
def view1(request):
...
#set cookie
request.session["param"]="view1"
def view2(request):
request.session["param"]="view2"
then in your ONE template check something like..
{% ifequal request.session.param "view1" %}
... do stuff related to view1
{% endifequal %}
{% ifequal request.session.param "view2" %}
... do stuff related to "view2"
{% endifequal %}
Gath