RoutablePageMixin and breadcrumbs - django

A standard Wagtail breadcrumb system like this works perfectly if all of your pages are in a tree (parents/children):
{% block main %}
{% if self.get_ancestors|length > 1 %}
<ul class="breadcrumb">
{% for page in self.get_ancestors %}
{% if page.is_root == False and page.url != '/' %}
<li>{{ page.title }}</li>
{% endif %}
{% endfor %}
<li class="active">{{ self.title }}</li>
</ul>
{% endif %}
{% endblock main %}
But it falls down if some of your sub-pages are not actual children, but instead use RoutablePageMixin. Because the routable pages are really different instances of the parent, the breadcrumb trail stops short of making it down to the routable page.
I thought I could add some extra info to the context to detect the situation and special-case it, but all of the WT URL methods return the URL of the "parent" page (i.e. the actual instance), and besides there is no programmatic "title" that could be used in the breadcrumb.
What's the best way to have a breadcrumb system that works equally well for child pages and routable pages?

Answering my own question (Thanks Robert for the hint). In the route definition in the model, add something like:
ctx['routed_title'] = 'Staff'
Then modify the breadcrumb example above like this (check for existence of the new element on context and append to breadcrumbs):
{% block main %}
{% if self.get_ancestors|length > 1 %}
<ul class="breadcrumb">
{% for page in self.get_ancestors %}
{% if page.is_root == False and page.url != '/' %}
<li>{{ page.title }}</li>
{% endif %}
{% endfor %}
{# If this is a routable, add non-parent/child link from context #}
{% if routed_title %}
<li>{{ page.title }}</li>
<li class="active">{{ routed_title }}</li>
{% else %}
<li class="active">{{ self.title }}</li>
{% endif %}
</ul>
{% endif %}
{% endblock main %}

Maybe this can be of any help.
#route(_(r'^detail/(?P<activity_slug>[-\w]+)/$'))
def show_activity(self, request, activity_slug):
activity_model_class = self.activity_model_class
if not activity_model_class:
raise Http404('No activity model.')
else:
queryset = self.get_activity(activity_slug)
try:
activity = queryset.get()
except activity_model_class.DoesNotExist:
raise Http404('activity not found')
else:
self.current_url = self.get_url(
'show_activity',
kwargs = {'activity_slug': activity_slug}
)
Now the routable page has a current_url
def get_context(self, request, *args, **kwargs):
context = super().get_context(request)
context['current_url']= self.current_url
return context
And now it’s in the context.

Related

How to pass a date (year) parameter to my url?

This is a huge rookie mistake but I can't figure it out.
This is what I wanna do:
I have a page displaying a list a years where an objects is available.
I want that, when I click on a year, it takes me to the corresponding YearArchiveView. I just don't succeed in passing the right parameter to the URL. Passing a template tag obviously doesnt work so what is the right way to do it ?
I get this error:
TemplateSyntaxError at /receipts/
Could not parse some characters: |{{y||date:"Y"}}
My template:
<ul>
{% for y in years_available %}
<li>{{y|date:"Y"}}</li>
{% empty %}
<p>No Receipt Yet</p>
{% endfor %}
</ul>
My view:
class ReceiptListView(LoginRequiredMixin, ListView):
model = Receipt
template_name = 'receipts.html'
def get_queryset(self):
queryset = Receipt.objects.dates('date_created','year',order="DESC")
return queryset
def get_context_data(self, *args, **kwargs):
context = super(ReceiptListView, self).get_context_data(**kwargs)
context['years_available'] = Receipt.objects.dates('date_created',
'year', order="DESC")
return context
My urls.py:
url(r'receipts/(?P<year>[0-9]{4}/$)',views.ReceiptYearArchiveView.as_view(),
name='receipt_year_archive'),
you dont need year=
just use this
<ul>
{% for y in years_available %}
{% with y|date:"Y" as current_year %}
<li>{{y|date:"Y"}}</li>
{% endwith %}
{% empty %}
<p>No Receipt Yet</p>
{% endfor %}
</ul>
You can't add another {{ and }} inside {%. It should call with direct variable.
<ul>
{% for y in years_available %}
<li>{{ y|date:"Y" }}</li>
{% empty %}
<p>No Receipt Yet</p>
{% endfor %}
</ul>
But, I think your case similiar with this docs examples:
<ul>
{% for yearvar in year_list %}
<li>{{ yearvar }} Archive</li>
{% endfor %}
</ul>
If the output of years_available is a list of integer years.
eg: [1992, 2001, 2005, 2011, 2014]
It should be:
<ul>
{% for y in years_available %}
<li>{{ y }}</li>
{% empty %}
<p>No Receipt Yet</p>
{% endfor %}
</ul>

Django: get profiles having auth.Group as foreign key

I have an model which uses auth.models.Group as foreign key called Dashboard:
class Dashboard(models.Model):
d_name = models.CharField(max_length=200)
d_description = models.CharField(max_length=200)
d_url = models.CharField(max_length=200)
d_status = models.CharField(max_length=200)
owner = models.ForeignKey(Group)
def __str__(self):return self.d_name
my views.py is:
def custom_login(request):
if request.user.is_authenticated():
return HttpResponseRedirect('dashboards')
return login(request, 'login.html', authentication_form=LoginForm)
def custom_logout(request):
return logout(request, next_page='/')
def user(request):
context = {'user': user, 'groups': request.user.groups.all()}
return render_to_response('registration/dashboards.html', context,
context_instance=RequestContext(request))
and here using this dashboards.html I want to display the dashboards by using the Group_name which i will get as a result of group.name:
{% extends "base.html" %}
{% block content %}
{% if user.is_authenticated %}
<p>Welcome, {{ request.user.get_username }}. <br/>
{% else %}
<p>Welcome, new user. Please log in.</p>
{% endif %}
<ul>
{% for group in groups %}
<li>
<strong>{{ group.name }}<strong> -
{{ dashboards.d_name }}{% if not forloop.last %},{% endif %}
</li>
{% endfor %}
</ul>
{% endblock %}
here I have mentioned all the supporting information for my problem, please let me know if there are any solution.
To access the list of Dashboards for the Group use the group.dashboard_set queryset:
{% for group in groups %}
<li>
<strong>{{ group.name }}</strong> -
{% for dashboard in group.dashboard_set.all %}
{{ dashboard.d_name }}{% if not forloop.last %},{% endif %}
{% endfor %}
</li>
{% endfor %}
This queryset is called "backward relationship".
views.py
def user(request):
user= request.user
groups = request.user.groups.all()
dashboards = Dashboard.objects.filter(owner=groups)
context = {
'user': user,
'groups': groups,
'dashboards': dashboards,
}
return render_to_response('registration/dashboards.html', context, context_instance=RequestContext(request))
and dashboards.html
{% extends "base.html" %}
{% block content %}
{% if user.is_authenticated %}
<p>Welcome, {{ request.user.get_username }}. <br/>
{% else %}
<p>Welcome, new user. Please log in.</p>
{% endif %}
<ul>
{% for group in groups %}
<li>
<strong>you belongs to::{{ group.name }}</strong> </li>
{% endfor %}
</ul>
<strong>#Dashboards available are::</strong>
{% for Dashboard in dashboards %}
<ol>
<li>{{ Dashboard.d_name }}-{{ Dashboard.owner }}-{{Dashboard.d_description}}</li> </ol>
{% endfor %}
{% endblock %}
this works good and neat,,,,

Using custom object manager on related set

Im trying to print out 4 entries. It works, as long I don't have any entries not published.
How can I get a queryset that only contains objects from my "published" manager?
Now I use: {% if benefit.status == "p" %} to not print those entries not published, but then the unpublished effects the slice count.
#views.py:
class PackageListFrontpage(ListView):
context_object_name = "package_frontpage_list"
template_name = "frontpage.html"
queryset = Package.published.all().order_by('order')[:5]
#frontpage.html
{% for package in package_frontpage_list %}
<div>
<h3>{{ package.name }} >></h3>
<ul>
{% for benefit in package.benefit_set.all|slice:":4" %}
{% if benefit.status == "p" %}
<li>{{ benefit.name }}</li>
{% endif %}
{% empty %}
<li>There are no published benefits in this package</li>
{% endfor %}
</ul>
</div>
{% endfor %}
I guess there is a better way of doing this?
You could define a method on your Package model that returns the queryset of related benefits which are published.
class Package(object):
...
def benefit_set_published(self):
"""
Return the related benefits which are published
"""
return self.benefit_set.filter(status="p")
Then change your template to:
{% for benefit in package.benefit_set_published.all|slice:":4" %}
<li>{{ benefit.name }}</li>
{% empty %}
<li>There are no published benefits in this package</li>
{% endfor %}

Object not iterable with django's pagination

I have a template showing a list of events. To prepare list of events I'm using generic views, and set 'paginate_by' parameter. Strangely when I load my page I see :
TemplateSyntaxError at /event/latest/
Caught an exception while rendering: 'int' object is not iterable
in 9th line of pagination.html template :
{% if is_paginated %}
{% load i18n %}
<div class="pagination">
{% if page_obj.has_previous %}
<< Prev
{% else %}
<span class="disabled prev"><< Prev</span>
{% endif %}
#here {% for page in pages %}
{% if page %}
{% ifequal page page_obj.number %}
<span class="current page">{{ page }}</span>
{% else %}
{{ page }}
{% endifequal %}
{% else %}
...
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
Next >>
{% else %}
<span class="disabled next">Next >></span>
{% endif %}
</div>
{% endif %}
Here is my view :
def events_nearest(request):
events = Event.nearest.all()
return object_list(request,
queryset = events,
extra_context = {'title': 'Nearest events'},
paginate_by = 12,
template_name = 'event/events_date.html')
And model :
class NearestManager(models.Manager):
def get_query_set(self):
return super(NearestManager, self).get_query_set().order_by('-data')
class Event(models.Model):
category = models.ForeignKey(Category)
title = models.CharField(max_length=120)
slug = models.SlugField(max_length=255, unique=True, verbose_name='Slug')
about = models.TextField()
city = models.ForeignKey(City)
objects = models.Manager()
nearest = NearestManager()
Any ideas what can cause this ?
pages variable is the number of pages, which is int and hence the error: 'int' object is not iterable
you should be looping over page_range
{% for page in page_range %}
I met the same error. There is a note at
https://docs.djangoproject.com/en/dev/topics/pagination/
Changed in Django Development version: Previously, you would need to use {% for contact in contacts.object_list %}, since the Page object was not iterable.
So {% for page in pages.object_list %} could probably solve your problem.
For anybody stumbled upon this post:
As with Django 1.4 and later (as far as I know), the iterable object for pagination is now paginator.page_range , i.e. the for loop should be
{% for page_num in paginator.page_range %}
In your error line #9 {% for page in pages %} what exactly is pages
Can't see it in your code anywhere.

How can I get a variable passed into an included template in django

I am a Django newbie and am unable to achieve something trivial. Please help me with this.
I am setting a variable pgurl in my views.py
Am able to access the variable {{pgurl}} in my with_tag.html template. This template includes a pagination.html template into itself
In pagination.html I am unable to use the variable {{pgurl}} and nothing is printed
How can I get this variable passed into the included template?
views.py
def with_tag(request, tag, template_name='main/with_tag.html', current_page=1, pgurl=''):
if request.method == 'GET':
query_tag = Tag.objects.get(name=tag)
primes = TaggedItem.objects.get_by_model(Prime, query_tag)
primes = primes.order_by('-date')
request.page = current_page
tcm_pp = TCM_ITEMS_PER_PAGE
pgurl = request.path
else:
return HttpResponseRedirect(request.path)
return direct_to_template(request, template_name, { 'primes' : primes, 'prime_total' : Prime.objects.count(), 'now': datetime.now(), 'page' : current_page, 'tcm_pp' : tcm_pp, 'tag' : tag, 'pgurl' : pgurl })
with_tag.html
{% extends "base.html" %}
{% load comments %}
{% load pagination_tags %}
...
{% include "pagination.html" %}
{% paginate %}
pagination.html
{% if is_paginated %}
{% load i18n %}
<div class="pagination">
{% if page_obj.has_previous %}
‹‹ {% trans "previous" %}
{% else %}
<span class="disabled prev">‹‹ {% trans "previous" %}</span>
{% endif %}
{% for page in pages %}
{% if page %}
{% ifequal page page_obj.number %}
<span class="current page">{{ page }}</span>
{% else %}
{{ page }}
{% endifequal %}
{% else %}
...
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
{% trans "next" %} ››
{% else %}
<span class="disabled next">{% trans "next" %} ››</span>
{% endif %}
</div>
{% endif %}
It will be helpful if you post the output of the rendered page. The context should get passed, might be your template tags instead. Try to do assert and check to see if the variables were passed correctly.