I'm working on a simple blog app in Django, and i'm having trouble figuring out how to dynamically generate the five most recent posts in a side bar. Each of my views are class based and they extend a generic template, each view maps to one template which I believe is the correct way to do it. I've looked for a way to do this using template tags, but it seems Django doesn't like you to put any logic inside of your templates.
The problem I believe is that I want this to exist within my base.html because I want the recent posts to be displayed site-wide, is a view even supposed to map to your base.html or does that cause problems, i'm pretty new with this. I don't know how to approach this, whether i'm supposed to create a new view for base.html or if I should use my template tags, or if I should extend an existing view(but if I do that it won't be site wide?).
I essentially want the following(they're ordered in reverse chronological order)
{% for post in post_list[:4] %}
{{ post.title }}
{% endfor %}
You can use a template tag. More specifically, an inclusion tag is what you need. This allows you to insert a rendered snippet anywhere inside your template via a small view-like piece of code.
For example, create a templatetags/blog_tags.py file (it's important that you create the templatetags folder within your app; Django searches for them here by default) in your blog app and add the following:
from django import template
register = template.Library()
#register.inclusion_tag('blog/snippets/recent_posts.html')
def render_recent_blogposts():
return {
# This is just an example query, your actual models may vary
'post_list': BlogPost.objects.all().order_by("published_on")[:4]
}
now create a blog/snippets/recent_posts.html template (it can be anywhere as long as it mathecs the #register.inclusion_tag(...) above.):
<ul>
{% for post in post_list %}
<li> {{ post.title }}</li>
...
{% endfor %}
</ul>
finally, in your original template, you can now render your template tags:
<aside>
{% load blog_tags %}
{% render_recent_blogposts %}
</aside>
Related
I'm struggling to finding a quick and easy solution to show HTML content based on a conditional that checks if the wagtail page is at root, aka '/'.
According to the Wagtail docs, I can also make this work using original request object from Django:
Additionally request. is available and contains Django’s request
object.
This is essentially what I want to do. I use the page object here from wagtail but it might be better if I used the request object from Django instead. How can I
{% if page.request.path == '/' %}
<div> show something </div>
{% else %}
#show nothing
{% endif %}
How can I structure a conditional to solve what I'm trying to do?
Access request
The request object can be accessed via request and not page.request.
A helpful tip is to add {% debug %} to see ALL the context that is available to the current template while working locally and debugging.
{% if request.path == '/' %}
<div> show something </div>
{% else %}
#show nothing
{% endif %}
More info
Within Wagtail the request object should be available to all templates, however you may need to enable this by following the instructions about Using RequestContext in the Django docs.
Alternatively this Django request in template answer provides a clear example of what to update in your settings file.
I'm building a site where users can view their posts Like this. After building the quizzes portion, I tabbed to "blogs" where I realized I needed to import the blogs template to use it.
I'm using the quiz template already like this
{% extends '../main/base.html' %} {% block title %}View Quizzes{% endblock %} {% block content %}
but I need to access the blog template as well. How can I do this? Thanks!
You can include the blog template into the main one like:
{% include "path/to/blogs.html" %}
It's best to have a main template which includes blogs, quizzes... (instead of inheritance)
You don't need django to do this.
I'm trying to figure out what is the best way to have mixes member and guest templates.
The main difference would be the menu of the page. In some languages i've worked with you can add prefixes to templates to get it to switch out the whole templates for the other version.
ex:
base.guest.html
base.member.html
In Django the only way i've seen any thing related to this is this code i found in the documents:
if request.user.is_authenticated():
# Do something for authenticated users.
else:
# Do something for anonymous users.
Is this the base way to do this in Django? or is there something else that i'm missing.
For most of my pages this would work out ok but wasn't sure if there was a better way to switch content based on authenticated state.
You don't extend in this case, instead you include. The base.html should authentication-agnostic.
{% if user.is_authenticated %}
{% include 'member.html' %}
{% else %}
{% include 'guest.html' %}
{% endif %}
If you want to, you can do have the if-statement also in your view, and pass the name of the template-to-be-included to the main template. For more info see https://docs.djangoproject.com/en/dev/ref/templates/builtins/#include
So the title is a bit obtuse, I know, but I couldn't think of a more succinct way to state it. Here's the issue:
I've created two proxy models for "user types", both inheriting from django.contrib.auth.User. Each has a custom manager limiting the queryset to items belonging to a particular Group. Specifically, there's a PressUser which is any user belonging to the "Press" group and StaffUser which is any user in any other group than "Press".
The issue is that when I add 'groups' to list_filters on my StaffUsers modeladmin, the resulting filter options are every group available, including "Press", and not just groups available to StaffUsers.
I've research a bit online and came up with a custom filterspec that should produce the behavior I want, but the problem is that the User model's 'groups' attribute is actually a related_name applied from the Group model. As a result, I can't attach my filterspec to 'groups' in my proxy model.
Is there any other way to apply the filterspec? Alternatively, is there a better approach to filtering the items returned by the default filterspec?
So, I was able to solve my own problem. For those that might run into a similar situation, here are the steps:
The approach I took is to modify the change_list.html template and manually filter out the items I didn't want to be included. There's quite a number of changes to make, though.
First, add a changelist_view method to your ModelAdmin:
# myproject/account/admin.py
class StaffUserAdmin(models.ModelAdmin):
...
def changelist_view(self, request, extra_context=None):
groups = Group.objects.exclude(name__in=['Press',]).values_list('name')
extra_context = {
'groups': [x[0] for x in groups],
}
return super(StaffUserAdmin, self).changelist_view(request,
extra_context=extra_context)
Basically, all we're doing here is passing in the filtered list of Groups we want to use into the context for the template.
Second, create a change_list.html template for your app.
# myproject/templates/admin/auth/staffuser/change_list.html
{% extends "admin/change_list.html" %}
{% load admin_list %}
{% load i18n %}
{% load account_admin %}
{% block filters %}
{% if cl.has_filters %}
<div id="changelist-filter">
<h2>{% trans 'Filter' %}</h2>
{% for spec in cl.filter_specs %}
{% ifequal spec.title 'group' %}
{% admin_list_group_filter cl spec groups %}
{% else %}
{% admin_list_filter cl spec %}
{% endifequal %}
{% endfor %}
</div>
{% endif %}
{% endblock filters %}
This one deserves a little explanation. First, the template tag loads: admin_list is used for the default Django template tag responsible for rendering the filters, admin_list_filter, i18n is used for trans, and account_admin is for my custom template tag (discussed in a sec), admin_list_group_filter.
The variable spec.title holds the title of the field that's being filtered on. Since I'm trying to alter how the Groups filter is displayed, I'm checking if it equals 'groups'. If it does, then I use my custom template tag, otherwise, it falls back to the default Django template tag.
Third, we create the template tag. I basically just copied the default Django template tag and made the necessary modifications.
# myproject/account/templatetags/account_admin.py
from django.template import Library
register = Library()
def admin_list_group_filter(cl, spec, groups):
return {'title': spec.title, 'choices' : list(spec.choices(cl)), 'groups': groups }
admin_list_group_filter = register.inclusion_tag('admin/auth/group_filter.html')(admin_list_group_filter)
The only things that I've changed here are adding a new argument to the method called 'groups' so I can pass in my filtered list of groups from before, as well as adding a new key to the dictionary to pass that list into the context for the template tag. I've also changed the template the tag uses to a new one that we're about to create now.
Fourth, create the template for the template tag.
# myproject/templates/admin/auth/group_filter.html
{% load i18n %}
<h3>{% blocktrans with title as filter_title %} By {{ filter_title }} {% endblocktrans %}</h3>
<ul>
{% for choice in choices %}
{% if choice.display in groups %}
<li{% if choice.selected %} class="selected"{% endif %}>
{{ choice.display }}</li>
{% endif %}
{% endfor %}
</ul>
No big surprises here. All we're doing is putting all the pieces together. Each choice is a dictionary with all the values needed to construct the filter link. Specifically, choice.display holds the actual name of the instance that will be filtered by. Obviously enough, I've set up a check to see if this value is in my filtered list of groups I want to show, and only render the link if it is.
So, it's a bit involved but works remarkably well. Just like that, you have a list of filters that is exactly what you want instead of the default ones generated by Django.
I'm going to tell you off the bat that I've never done this before myself, so take it with a grain of salt.
What I'd suggest would be to override get_changelist on your ModelAdmin, to return a custom ChangeList class, which you can define somewhere in your admin module.
Your custom ChangeList class would simply override get_filters, so you can map your custom FilterSpec for the group field.
Another thing that might interest you are patches from the feature request ticket for specifying custom filter specs. The latest patch doesn't work for Django 1.3rc1 yet, although #bendavis78 recently posted that he's working on a new one, but depending on your version of Django it may apply cleanly.
It looks like it barely missed the cut to get included into the 1.3 milestone, so I figure it's going to make it into the trunk as soon as work beings on Django 1.4.
I'm writing a simple blog-like application for Django and am trying to get the effect of having a front page with posts limited to 5, with a comprehensive archive that lists something like 100 posts at a time. (100 is not realistic, just throwing a number out there)
Since the blog post blocks will look exactly the same between the two pages minus the number being shown, I'd like to put the corresponding HTML in a separate template that I can include or link to from the actual templates being rendered. I've looked over the documentation, and the include tag looked promising, but it apparently renders outside of the current context, which is not helpful to my cause, since it wouldn't get the objects to loop through. Outside of that, I can't see any other way to do what I want. Is this possible or am I just out of luck and going to have to violate DRY? Code is below to give you an idea of what I want.
Thanks
#######################
# news/frontpage.html #
#######################
{% extends "news/base.html" %}
{% block site_title %} - Front Page{% endblock %}
{% block center_col %}
{{ block.super }}
View Older Blog Posts
{% endblock %}
{% block blog_rows %}
{% for object in object_list %}
# Blog post content would go here, however it is to be included.
{% endfor %}
{% endblock %}
You're looking for an inclusion tag.
Why don't you filter for the blog posts you want to show in your view? That way you can keep the template the same:
{% for object in blogposts %}
# ...
{% endfor %}
You define blogposts in your view, which either includes 5 or 100 posts.
Ignacio is right that you want an inclusion tag, but you should know that the include tag does not render outside the current context - it very definitely uses the same context as the block it's in.
Your problem is probably that you're trying to call blogpost_set on the object_list - but the relationship is not with the list of objects, it's with each individual object in the list. You'd need to iterate through object_list and then through blogpost_set.all on each one.