I have a simple Django application for a magazine website, with a sidebar that shows links for recent issues. The sidebar is its own template, which I then include into other pages. I want the Queryset for recent issues to be part of the sidebar inclusion, rather the in the view of the page that the sidebar appears on. (This is both to avoid repetition and, more importantly, to make the sidebar functional on the flatpages, which don't have views.)
This seems like iti should be a simple/common thing to do, but I can't find a good way to do it. I looked into custom template tags, context managers, etc., but all the options I saw want to return a string or a dictionary, rather than a queryset. So I ended up doing an inclusion template tag (code below), and basically building a dictionary for the issues I want displayed. It works fine, but it seems really clunky and brittle to me, and it mixes logic and display pretty badly.
Is there a better way to do this?
<!-- app_name/templates/sidebar.html -->
...
{% load pr_extras %}
...
<ul class="nav nav-sidebar">
<li>Home</li>
<li>Previous Issues</li>
<li>{% show_recent %}</li>
</ul>
...
# app_name/templatetags/pr_extras.py
from django import template
from mag.models import Issue
register = template.Library()
#register.inclusion_tag('recent.html')
def show_recent():
issues = Issue.objects.order_by('-issue_num')[1:6]
iss_dict = {}
for i, iss in enumerate(issues):
k = "recent_%d" % i
iss_dict[k] = "%s %s %d" % (iss.issue_num, iss.get_season_display(), iss.year)
u = "url_%d" % i
iss_dict[u] = iss.issue_pdf
return iss_dict
<!-- app_name/templates/recent.html -->
<ul class="nav nav-sidebar">
<li>Issue {{ recent_0 }}</li>
<li>Issue {{ recent_1 }}</li>
<li>Issue {{ recent_2 }}</li>
<li>Issue {{ recent_3 }}</li>
<li>Issue {{ recent_4 }}</li>
</ul>
A context processor would be the typical way to do this.
def recent_issues(request):
{'recent_issues': return Issue.objects.order_by('-issue_num')[1:6]}
Once you add the context processor to your TEMPLATES settings, you can access recent_issues in the template.
Alternatively, if you don't want a context processor to run for every view, you can create a template tag using the simple_tag decorator (in Django < 1.9 use an assignment tag).
#register.simple_tag
def recent_issues():
return Issue.objects.order_by('-issue_num')[1:6]
In your template, use the tag to assign the queryset to a variable
{% recent_issues as recent_issues %}
You can now loop through recent_issues in the template.
Maybe I don't understand your question very well, but are you looking for something like this:
{% include "some_include_template.html" with some_list=some_list some_var=some_var %}
?
Related
I am new to coding and new to Django. I searched stackoverflow for my question but didn't find what I was looking for:
What I am trying to do is check if certain values are in my database and if yes, pass it as a variable to the template. The values will be items of a dropdown menu.
If I have for example a database with bicycle1 to bicycleN I'd like to check if the value of the attribute "handlebar" of each database-object matches a certain manufacturer. If yes, pass it to the template so it can appear in the dropdown menu to later filter the results.
First I thought I should check in the template itself and thought about something like this:
bicyle_list.html
<ul id='dropdown1' class='dropdown-content'>
{% for bicyle in bicycles %}
{% with manufacturerA=False %}
{% if manufacturerA == False and bicycle.handlebar == "manufacturerA" %}
<li>ManufacturerA</li>
{% manufacturerA=True %}
{% endif %}
{% endwith %}
{% endfor %}
But as I understand the template should only contain rendering logic. (Besides, I`d have to use boolean variables in the for-loop, because a manufacturer should only appear once in the dropdown menu even if there are several bicycles with that certain handlebar. Changing the value of variables in a template seems quite complicated to me.)
In the views.py I thought about this but don't know how to work with conditions and the rendering-method:
views.py
bicycles = Bicycle.objects.all()
for bicycle in bicyles:
if bicycle.handlebar == "manufacturerA":
manufacturerA= "manufacturerA"
if bicycle.handlebar == "manufacturerB":
manufacturerB= "manufacturerB"
#if variable manufacturerA exists, pass it to the template – if not, not! Same with manufacturerB
#But how to include in return render(..)?
return render(request, 'shop/bicycle_list.html', {'bicycles': bicycles})
Do you have any idea how to pass optional variables?
You can obtain a list of distinct manufacturers with:
def some_view(request):
manufacturers = Bicycle.objects.values_list('handlebar', flat=True).distinct()
return render(request, 'shop/bicycle_list.html', {'manufacturers': manufacturers})
<ul id='dropdown1' class='dropdown-content'>
{% for manufacturer in manufacturers %}
<li>{{ manufacturer }}</li>
{% endfor %}
</ul>
You however might want to make a model for the manufacturer, and work with a many-to-one relationship [Django-doc].
I have a model called Project in an app called projects that I registered with the admin site so the instances can be added/edited/etc. This works as expected. Now I want to add a button for each project in the change list view on the admin site, that links to a custom form that requires a Project instance to do things. I followed a bunch of different tutorials to customize the admin site and managed to add another field to the table of the change list view. However the entries show up outside the table (see image).
I added the custom field by overwriting the admin/change_list.html template and calling a custom template tag custom_result_list within it. This tag adds a table field to the change list and then calls the admin/change_list_results.html template to render it. I have confirmed with a debugger that the item is added to the entries of the change list before the template is rendered (see image).
I cannot explain why the table is not rendered correctly even though the additional field has the same structure as the auto-generated ones. I have to admit I have resorted to Cargo Cult Programming, because I do not understand how this is supposed to work, despite spending too many hours trying to solve this simple problem.
Here's the relevant code.
In file /projects/templatetags/custom_admin_tags.py:
from django import template
from django.contrib.admin.templatetags.admin_list import result_list as admin_result_list
def custom_result_list(chl):
extended_cl = {}
extended_cl.update(admin_result_list(chl))
extended_cl["result_headers"].append({
'class_attrib': r' class="column-__str__"',
'sortable': False,
'text': 'Configure Project'
})
idx = 0
snippet = '<td class="action-button">{}</td>'
for project in chl.result_list:
extended_cl["results"][idx].append(snippet.format(project.id, project.unmod_name))
idx += 1
return extended_cl
register = template.Library()
register.inclusion_tag('admin/change_list_results.html')(custom_result_list)
In file templates/admin/projects/project/change_list.html:
{% extends "admin/change_list.html" %}
{% load i18n admin_urls static admin_list %}
{% load custom_admin_tags %}
{% block result_list %}
{% if action_form and actions_on_top and cl.show_admin_actions %}{% admin_actions %}{% endif %}
{% custom_result_list cl %}
{% if action_form and actions_on_bottom and cl.show_admin_actions %}{% admin_actions %}{% endif %}
{% endblock %}
To fix your issue:
from django.utils.html import format_html
replace your snippet.format(...) with format_html(snippet,...)
Explanation:
in django, all strings you pass from python are automatically HTML escaped. which here means, all your tags will not be considered as HTML. Such limitation is added to avoid any potential exploits by hackers. In your case, use of a template to render html is highly recommended. However, you can also send raw html from python using format_html helper function.
I couldn't place my question into one sentence.
I was following a tutorial to make a blog from scratch. But the tutorial predicted having all the categories, tags, months and years listings in separate templates.
I want to add a list of categories, a list of months and years on the main blog page.
So here is what I got. With this code, the list of categories is shown in the main page but only if you go to the blog/category url, and not in just blog/ where I want it to be.
**(r'^$', list),**
(r'^archive/(\d{1,2})/$', list),
(r'^\d{2}/d{4}/$', month),
(r'^([0-9]{4}/\d{1,2})/(?P<slug>.*)/$', detail),
(r'^(?P<year>\d{4})/$', year),
**(r'^category/$', category),**
I also tried:
(r'^$', category),
but no luck.
This is the same code from the template in category.html and in list.html:
{% if categories %}
{% for category in categories %}
<li class="cat-item"><a href="category/{{ category.name.lower }}/"
title="{{ category.name.capitalize }}">
{{ category.name.capitalize }}</a>
</li>
{% endfor %}
{% endif %}
Views.py:
def category(request):
return render_to_response('blog/list.html', {'categories':Category.objects.all(),},)
It was like this. I tried this, but no luck in def list:
return render_to_response('blog/list.html',{'posts':posts,
'next':next,
'previous':previous,
'categories':Category.objects.all(),
},)
How can I get what shows on blog/category to show on blog/ also?
Thanks.
When you type an url in your browser a request is sent to your server. Django then takes the url and matches it against it url patterns to determine the proper view. As soon as a view is found django stops matching and executes this view which in turn returns a response.
If you want to use your categories in different views you either have to to make sure that in every view the same categories context variable is provided to the template or, which is usually much better, write a simple custom template tag. For your categories this could look like this:
#register.inclusion_tag('blog/category_list.html')
def categories_list():
return { 'categories': Category.objects.all() }
In the file 'blog/categoy_list.html' would then be the code that is currently both in 'categories.html' and 'list.html'. In these files replace it with.
{% load your_blog_tags %}
{% categories_list %}
You can use that then wherever you need a category list. Of course the same applies for years and month lists.
I would like to display some content located in my models in some of my template pages.
I am using django-page cms
In the documentation views are not used to display content. Instead ready made template tags are used.
http://packages.python.org/django-page-cms/display-content.html
I do not understand a word of this. Please Bear with me I am new.
All I want to do is display some info located in my models inside a template this manner..
{% if latest_news_list %}
{% for news in latest_news_list %}
<li><h3>{{ news.title }}</h3></li>
<li><p>{{ news.body }}</p></li>
{% endfor %}
Since views are not used I cannot use if latest_news_list.
I need to somehow get my models to display in the templates using django-page cms and NOT regular views. The documentation states to use some kind of template tag for this.
Could somebody please explain to me how to do this.
And a clear concise explanation of the following ready-made template tags would also be appreciated...
* get_content
* show_content
* get_page
* show_absolute_url
taken from.http://packages.python.org/django-page-cms/display-content.html
I need to display info contained using the following models in the manner I have highlighted above. Thank you very much for your help. my models are as follows.
class Body(models.Model):
type = models.ForeignKey(Content)
title = models.CharField(max_length=100)
published = models.DateTimeField(default=datetime.now)
body = tinymce_models.HTMLField("Main content")
As I have stated I am very new to this please make explanations as simple as possible.
The template tags you mentioned are supposed to display content coming from the cms. If you want to include data coming from your app, you should see this sectionlink text.
def extra_context():
from myapp.models import Body
items = Body.object.all()
return {'items': items}
PAGE_EXTRA_CONTEXT = extra_context
{% if items %}
<ul>
{% for item in items %}
<li>{{ item.title }} </li>
{% endfor %}
<ul>
{% endif %}
Or, if you want to use your app's view, see this.
I'm sure I've seen this question on Stack Overflow before, but I couldn't find it by my life, so here goes nothing.
I have a normal Django menu which uses the {% url %} tag and static names for the menu items. Now I want to have a different style for the menu item which has been selected. But the menu is being rendered in the base template, so how do I figure out which menu item it is?
You could surely do this with some ugly template code, but a better more globally known way is to use a CSS selector. This lets CSS do all of the work automatically for you.
Here's how it works:
You simply put an id in your body depending on which page you are on.
Then in css you do something like this:
#section-aboutme #nav-aboutme,
#section-contact #nav-contact
/* ... put one of these per body/menu item ... */
{
font-color: red;
}
You put the nav-aboutme, and nav-contact ids on each of your menu items.
The style will automatically be selected by CSS depending on which body id they are inside of.
I normally do it the way Brian suggested, but to accommodate for a template which a designer gave me which used the more common class="selected" method, I wrote a {% nav %} template tag.
Your HTML navigation template will look something like:
{% block nav %}
<ul class="nav">
<li{% if nav.home %} class="selected"{% endif %}>Home</li>
<li{% if nav.about %} class="selected"{% endif %}>About</li>
</ul>
{% endblock %}
To set the navigation in a child template, do:
{% include "base.html" %}
{% load nav %}
{% block nav %}
{% nav "about" %}
{{ block.super }}
{% endblock %}
How about a custom tag which you use to generate your nav item?
The following takes the name of the url for which a nav item should be generated and the text it should display. It generates a li tag with a class of "selected" if the named url's path is the same as the current url (requires 'django.core.context_processors.request' in your TEMPLATE_CONTEXT_PROCESSORS). Within the li, it generates an a tag with the path of the url specified by the url_name. It has the contents specified by contents.
Obviously, this could be tweaked to generate different markup for the nav item, as required.
The rest can be done using CSS.
Advantages:
Easy to use
Little code required
DRY
Could be made to be more flexible
Disadvantages:
Requires 'django.core.context_processors.request'
Requires urls to be named e.g. urlpatterns = patterns('django.views.generic.simple',
...
(r'^$', 'direct_to_template', {'template': 'index.html'}, 'index'),
...
). This could potentially be done differently (e.g. pass in url).
Doesn't cope with pages not exactly equal to the specified and therefore will not apply the selected class to the li when on a page lower in the url heirarchy. For example, if I'm on /products/, it will highlight the nav item directing to /products/. If I'm on /products/myProduct/, it will not highlight the /products/ link. This could be coded around, but it would force people to use sensible urls. For example, change the additionalAttrs assignment to additionalAttrs = ' class=selected' if (context['request'].path.startswith(path) and path != '/') or (context['request'].path == path) else ''.
Code:
from django import template
from django.core.urlresolvers import reverse
register = template.Library()
class NavNode(template.Node):
def __init__(self, url_name, contents):
self.url_name = url_name
self.contents = contents
def render(self, context):
path = reverse(self.url_name)
additionalAttrs = ' class=selected' if path == context['request'].path else ''
return '<li'+additionalAttrs+'>'+self.contents+'</li>'
#register.tag
def nav_link(parser, token):
bits = token.split_contents()
if len(bits) == 3:
contents = bits.pop()
url_name = bits.pop()
else:
raise template.TemplateSyntaxError, "%r tag requires a single argument" % bits[0]
if contents[0] == contents[-1] and contents[0] in ('"', "'"):
contents = contents[1:-1]
return NavNode(url_name, contents)
You can pass request.path to your template
from django.shortcuts import render_to_response
from django.template import RequestContext
return render_to_response('templ.html', {'page':request.path}, context_instance=RequestContext(request))
then use an ugly if template tag to add a CSS class to your menu item
Let's say one has an app named "stackoverflow" and inside of that app folder one has a templates folder with a stackoverflow.html file that extends from the base template.
One way to achieve it is by defining a variable in the views.py of ones stackoverflow app, like
def stackoverflow(request):
return render(request,
'stackoverflow/stackoverflow.html',
{'section': 'stackoverflow'})
Then, in the base template
<li {% if section == "stackoverflow" %} class="selected" {% endif %}>
StackOverflow
</li>
Essentially having the variable section allows to figure the section. Note that one needs a space between section and ==... if one doesn't respect that, then one will get a Django Template Error saying
Could not parse the remainder.