Django functions issue and urlconf confusion - django

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.

Related

How do I call my dict parameters from the view.py render in my template?

I have been working on this for two day, and have read almost every example on stackoverflow and consulted the django documentation. I am trying to pass my dict from the views.py to my template, but I keep getting the stupid "Could not parse the remainder" error. I'm not doing anything fancy. Just Href buttons passing in a parameter to represent what that button is. Then a template page opens using that parameter as a string to make the new page and url unique.
pass in with:
Call
urls.py
urlpatterns = [
url(r'^call=(\d+)/$', views.call, name='call')
]
views.py
def call(request, callID):
call_id = { 'id':callID }
return render(request, 'site/call.html', call_id)
Call template
{% extends 'site/layout.html' %}
{% block content %}
{% with call_id.get('id') as view_id %}
<h3 class="center-align blue lighten-3">Site # Room {{ view_id }}</h3>
Cancel
{% endwith %}
{% endblock %}
I have tried request.GET.get('id') and a bizillion other things. Can someone show me how I can actually parse those dict values I passed in?
You're not actually passing a dictionary at all. You're passing a single value, id, so you should just use that. And there is no need for any with block.
Site # Room {{ id }}

Check and clear filters with django-filter

I am using django-filter to filter a ListView and would like to display a "Clear all filters" link if any filters are applied.
Due to the generic nature of the filtering system I haven't yet found a straightforward way to achieve this.
The only thing I came up with so far is to return the regular queryset in the get_queryset method of the view if a "clear" flag is present in the request, however this doesn't actually clear the filters - it just returns all the data.
Does anyone have a solution/idea for this?
Update: Solution
After Jerin's comment I decided to solve this problem in 2 separate parts:
has filter:
I check if any of the fields I defined in my filter class are in the request. My solution looks a bit different as I'm using class based views so I abstracted it away in a mixin but if you're using simple views like here, you could just do:
def product_list(request):
f = ProductFilter(request.GET, queryset=Product.objects.all())
has_filter = any(field in request.GET for field in
set(f.get_fields()))
return render(request, 'my_app/template.html', {
'filter': f,
'has_filter': has_filter
})
clear all filters:
A simple redirect to your list view:
{% if has_filter %}
{% trans 'Clear all filters' %}
{% endif %}
Here is the mixup version of the answer (combination of mine and Chris)
You could place a Clear all filters button and that will redirect to your default ListView (/host/end/point/).
But some non-filter parameters (such as pagination or something else) may occur in URL. So the better option is, check for any filter fields in URL and if so, display the filter clearing link
The opted solution is,
def product_list(request):
f = ProductFilter(request.GET, queryset=Product.objects.all())
has_filter = any(field in request.GET for field in set(f.get_fields()))
return render(request, 'my_app/template.html', {
'filter': f,
'has_filter': has_filter
})
and in template,
{% if has_filter %}
{% trans 'Clear all filters' %}
{% endif %}
Just make a button and point to the base search field.
<a class="btn btn-warning" href="{% url 'App:FilterView' %}">Reset</a>
If your FilterSet instance is available on the template you can check for filter.is_bound like this:
{% if filter.is_bound %}
Clear filters
{% endif %}
If you are using the FilterMixin or the FilterView, your FilterSet instance will be available as filter to the template as above.
I like this simple solution, however when I attempt to use it the current filter parameters are some how getting appended to the url even though it's the base url in the anchor.
So hovering over the button my link (determined using {% url 'app:view' %} shows
localhost/app/view correctly
However when clicking the button the url in browser has the parameters appended
localhost/app/view/?filter1=val1&filter2=val2 etc.
Is django caching something? Is browser (Chrome) caching? Can I force something in the anchor to not use them?
Answered my own, but for anyone else passing by:
I had the anchor on a button within a the filter form, although it was not a submit button moving it outside the form gave the desired result.

loop in Django template: how to control the loop iterator?

I'm using Django to show a list of posts. Each post has a 'is_public' field, so if one post's 'is_public' equals to False, it should not be shown to the user. Also, I want to show a fixed number of posts in one page, but this number can be changing depending on views.
I decided to crop the queryset in template as a few views are using the same template, generating it in the view means a lot of repeated codes.
If written in python, it should look like this:
i=number_of_posts_to_show_in_one_page
while i:
if qs[i].is_public == True:
#show qs[i] to the page
i--
As the django template does not support while loop and for loop seems hard to control, is there a way of achieving this? Or should I do it in another way?(One idea is to crop the qs before looping)Thanks!
Update:
I've written this template tag to pre-process the queryset:
#register.simple_tag(takes_context=True)
def pre_process_list(context,list,numbers):
#if not user.has_perm('admin'):
context['result_list']=list.filter(is_public=True, is_removed=False)[0:numbers]
#else:
#context['result_list']=list[0:numbers]
return ''
Before using for loop in the template, I'll pass the queryset to this templage tag, and use a simple for loop to show its result.
If in the future I want to show non-public posts to admins(which is not decided yet), I can write in some logic like the commented ones, and have them styled differently in the template.
{% for post in posts %}
{% if post.is_public %}
{{ post }}
{% endif %}
{% endfor %}
Though this would be a perfect use case for a manager.
You could write a simple manager that filters public posts.
class PublicPostManager(models.Manager):
def get_query_set(self):
return super(PublicPostManager, self).get_query_set().filter(is_public=True)
Then you would add it to your Post Class:
class Post(models.Model):
...
public = PublicPostManager()
Then you could pass post.public.all() as public_posts to your template and simplify your loop:
{% for post in public_posts %}
{{ post }}
{% endfor %}
#arie has a good approach with the manager, but you can easily do the same without writing a manager:
# View
posts = Post.objects.filter(is_public=True) # or use the manager
# Now, you can either limit the number of posts you send
# posts = posts[:5] (only show five in the view)
return render_to_response('foo.html',{'posts':posts})
# Template
# Or you can do the limits in your template itself:
{% for post in posts|slice:":5" %}
{{ post }}
{% endfor %}
See the slice filter on more information.
However, since this is a common operation, with django 1.3 you can use class based views to automate most of this.

Displaying page content using django-page-cms

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.

Django pagination and "current page"

I'm currently developing a Django application which will make use of the infamous "pagination" technique. I'm trying to figure out how the django.core.paginator module works.
I have an application with a Question model. I will be listing all of the questions using this paginator. There will be 20 questions per page.
def show_question(question_pk):
questions = Question.objects.all()
paginator = Paginator(questions, 20)
page = ... # Somehow figure out which page the question is on
return render_to_response('show_question.html', { 'page' : page })
In the view, where I list the different pages as "... 2, 3, 4, 5, 6, ..." I want to highlight the current page somehow, like many pages do.
There are really two things I want to know:
How do I make Django figure out which page the question is located at?
How would I write my template to properly "highlight" the currently visited page?
EDIT: Sorry, I forgot part of this question. I would also like any page except for the current one to be a link to /questions/{{ that_page.start_index }}. So basically every page link would link to the first question on that page.
Hmm... I see from your comment that you don't want to do the ol' GET parameter, which is what django.core.paginator was written for using. To do what you want, I can think of no better way than to precompute the page that each question is on. As an example, your view will end up being something like:
ITEMS_PER_PAGE = 20
def show_question(question_pk):
questions = Question.objects.all()
for index, question in enumerate(questions):
question.page = ((index - 1) / ITEMS_PER_PAGE) + 1
paginator = Paginator(questions, ITEMS_PER_PAGE)
page = paginator.page(questions.get(pk=question_pk).page)
return render_to_response('show_question.html', { 'page' : page })
To highlight the current page in the template, you'd do something like
{% for i in page.paginator.page_range %}
{% ifequal i page.number %}
<!-- Do something special for this page -->
{% else %}
<!-- All the other pages -->
{% endifequal %}
{% endfor %}
As for the items, you'll have two different object_lists to work with...
page.object_list
will be the objects in the current page and
page.paginator.object_list
will be all objects, regardless of page. Each of those items will have a "page" variable that will tell you which page they're on.
That all said, what you're doing sounds unconventional. You may want to rethink, but either way, good luck.
Django, at least from version 1.2, allows us to complete this task by using pure default pagination template tags.
{% for page in article_list.paginator.page_range %}
{% if page == article_list.number %}
{{ page }}
{% else %}
{{ page }}
{% endif %}
{% endfor %}
Where article_list is instance of
paginator = Paginator(article_list, 20)
try:
article_list = paginator.page(int(page))
except (EmptyPage, InvalidPage):
article_list = paginator.page(paginator.num_pages)
django-pagination should do what you want and comes wrapped in a pretty package you can just plug-in and use. It essentially moves the code from your views to the templates and a middleware.
EDIT: I just saw your edit.
You can get the current objects on a page using {% autopaginate object_list %}, which replaces object_list with the current objects for any given page. You can iterate through it and if you want the first, you should be able to treat it like a list and do object_list[0].
If you want to keep this within your views, you could do something like this:
def show_question(question_pk):
questions = Question.objects.all()
paginator = Paginator(questions, 20)
return render_to_response('show_question.html', { 'page' : paginator })
Within your template, you can access the current page you're on by doing:
# Gives you the starting index for that page.
# For example, 5 objects, and you're on the second page.
# start_index will be 3.
page.start_index
# You can access the current page number with:
# 1-based index
page.number
With that, you should be able to do everything you need.
There are a couple good examples here.