On my page all the blog posts will be showing up. So I wanted to implement a next/previous button to better my page.
def PostLists(request):
num = request.session.get('num',-5)
request.session['num'] = num
num = num + 5
exp = Post.objects.order_by('-date').all()[num:(num+5)]
context = {
'object_list': exp
}
if (request.user.is_authenticated):
return render(request, 'dashboard/postlist.html', context=context)
else:
return redirect('login')
I added a next button in my html code which will redirect me to my the same views.py function as shown above and the variable(num) shall increment by 5 thus showing me my next 5 posts. However this seems not to be working as I see the same 5 posts always.
Is there a better way to implement a next/previous button? If so could you please specify that? Thanks a lot!
I think you try to do too much yourself. Django has support for this, it even has a lot of support when rendering lists, and enforcing that the user is logged in.
We can use a class-based view for this: a ListView:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import ListView
class PostListView(LoginRequiredMixin, ListView):
model = Post
template_name = 'dashboard/postlist.html'
paginate_by = 5
queryset = Post.objects.order_by('-date')
In your dashboard/postlist.html template, then you can add logic to render the buttons. Like for example:
<!-- render the list -->
{% if is_paginated %}
{% if page_obj.has_previous %}
previous
{% endif %}
{% if page_obj.has_next %}
next
{% endif %}
{% endif %}
In the urls.py you can then use the PostListView.as_view() instead of the PostLists. So the ListView will here handle authentication check, slicing, pagination, etc.
Related
I am using Django for develop a website. The website is intended to use to search information stored in a MySQL database.
This is the current basic flow of the web site.
1) index.html - this has a form to select an option
2) according the option, users will redirect to search.html (include a form)
3) once the user provides the criteria, the result will be displayed in reply.html
In my views.py , I have two functions.
from django.shortcuts import render
from website.models import WebsiteRepository
from .forms import SearchForm
from .forms import SelectTypeForm
def Search(request):
if request.method == 'POST':
#do something
return render(request, 'reply.html', {'env_dict':env_dict})
else:
#do something
return render(request, 'search.html', context = context)
def index(request):
if request.method =='POST':
#do something
return render(request, 'search.html', context = context)
else:
#do something
return render(request, 'index.html', context= context)
When I go to index.html page, I can select a option and it will direct me to search.html. After, I fill the form there and submit, it wont give me the reply.html page.
I have a feeling that, I could make this work by changing urls.py.
from django.urls import path
from website import views
urlpatterns = [
path('', views.index, name='index'),
#path('search/', view.Search, name ='Search')
]
I tried to google it. But its too much details and Iam kind of lost.
Do any of you guys know how to achieve this?
Thanks
search.html
{% extends "base_generic.html" %}
{% block content %}
<h3>Welcome to search information Repository</h3>
<form method="post">
{% csrf_token %}
{{form.as_p}}
<button type = 'submit'>submit</button>
</form>
{% endblock %}
index.html
{% block content %}
<h3>Welcome to information Repository</h3>
<form method="post">
{% csrf_token %}
{{form.as_p}}
<button type = 'submit'>submit</button>
</form>
just for clarify things more, ill add the forms.py too
from django import forms
from .models import WebsiteRepository
class SearchForm(forms.Form):
websiterepository = WebsiteRepository
env_indicators = websiterepository.objects.filter (key_aspect='Environmental').values_list('repo_id','indicator')
indicator = forms.ChoiceField(choices=env_indicators,label = 'Indicator' )
OPTIONS = (('2000','2000'),('2001','2001'),('2002','2002'), ('2003','2003'),('0000','0000'),)
year = forms.ChoiceField(choices=OPTIONS)
class SelectTypeForm(forms.Form):
OPTIONS = (('1', 'Envirnmental Indicators'),('2','Economic Indicators'),('3','Social Indicators'),)
types = forms.ChoiceField(choices=OPTIONS)
Your code is wrong on many points.
First thing first: for a search, you want a GET request, not a POST (POST is for updating the server's state - adding or updating your database mostly). This is the semantically correct method (since you want to GET data), and it will allow a user to bookmark the url.
Second point: you don't want to submit the search form to the index view but to the search view. No need for redirects etc, just use the {% url %} templatetag to fill the action attribute of your form (you of course need to have a 'Search' url in your urls.py):
<form method="get" action="{% url 'Search' %}">
{% csrf_token %}
{{form.as_p}}
<button type = 'submit'>submit</button>
</form>
if you want to have this form on more than one page (which is often the case for search forms), use an inclusion tag tha will take care of creating an unbound SearchForm and render the template fragment.
Then in your search view, you only want GET requests, and do not use two different templates, this will only lead to useless duplication.
def Search(request):
form = SearchForm(request.GET)
# use the form's data - if any - to get search results
# and put those results (even if empty) in you context
return render(request, 'reply.html', {'env_dict':env_dict})
And finally, your search form is totally broken:
class SearchForm(forms.Form):
# this is totally useless
websiterepository = WebsiteRepository
# this will only be evaluated once at process startup, so you will
# get stale data in production - and probably different data
# per process, in a totally unpredictable way.
# You need to either put this in the form's __init__ or wrap it
# in a callable and pass this callable
env_indicators = websiterepository.objects.filter (key_aspect='Environmental').values_list('repo_id','indicator')
indicator = forms.ChoiceField(choices=env_indicators,label = 'Indicator' )
# are you going to manually add a new year choice every year ???
OPTIONS = (('2000','2000'),('2001','2001'),('2002','2002'), ('2003','2003'),('0000','0000'),)
year = forms.ChoiceField(choices=OPTIONS)
For the "indicators" ChoiceField you want something like:
def get_indicators_choices():
return Websiterepository.objects.filter (key_aspect='Environmental').values_list('repo_id','indicator')
class SearchForm(forms.Form):
# IMPORTANT : we are NOT calling the function here, just
# passing it (python functions are objects) to the field, which
# will call it everytime the form is instanciated, so you don't
# have stale data
indicator = forms.ChoiceField(
choices=get_indicator_choices,
label='Indicator')
As a last note: be consistent with your namings (ie why name one view in all lower (index) and capitalize the other (Search) ? Whichever convention you choose (I strongly suggest respecting pep8 here), at least stick to it for the whole project.
The problem is that code is not redirecting to /search, instead rendering search.html after post from index.html.
Try doing like-
views.py-
#your code
def index(request):
#do something
if request.method == 'POST':
return redirect('Search')
else:
#render index.html
def search(request):
#do something
if request.method == 'POST':
#render reply.html
else:
#render search.html
Another way to achieve this is if you specify action in your form so that form posts on /search.
search.html
<form method="post" action="/search">
{% csrf_token %}
{{form.as_p}}
<button type = 'submit'>submit</button>
</form>
I am having problems with user authentication for my django site. I have a log-in screen that seems to work. When the user clicks log-in, I call the django.contrib.auth.login and it seems to work fine. However on subsequent pages have no knowledge that there is a user logged in. Example {% user.is_authenticated %} is false. There are also some menu functions that I want to be available for logged in users such as my-account and logout. Those functions are not available, except on the log-in page. Which is really strange.
This seems to be a user context problem. But I'm not sure how I am supposed to be passing a context around to ensure that my login is stable. Does anyone know at could be going on here? Any advice?
---------part of base.html------------
<!--- The following doesn't register even though I know I'm authenticated -->
{% if user.is_authenticated %}
<div id="menu">
<ul>
<li>My Customers</li>
<li>Customer Actions</li>
<li>My Account</li>
</ul>
</div>
{% endif %}
---------my views.py -----------------
# Should I be doing something to pass the user context here
def customer_list(request):
customer_list = Customer.objects.all().order_by('lastName')[:5]
c = Context({
'customer_list': customer_list,
})
t = loader.get_template(template)
return HttpResponse(t.render(cxt))
If you're using Django 1.3, you can use the render() shortcut, which automatically includes RequestContext for you.
from django.shortcuts import render
def customer_list(request):
customer_list = Customer.objects.all().order_by('lastName')[:5]
return render(request, "path_to/template.html",
{'customer_list': customer_list,})
In this case, you could go one step further, and use the generic ListView:
from django.views.generic import ListView
class CustomerList(Listview):
template_name = 'path_to/template.html'
queryset = Customer.objects.all().order_by('lastName')[:5]
Use a RequestContext.
As Daniel Suggested, use the RequestContext... or better, just use the render_to_response shortcut:
from django.template import RequestContext
from django.shortcuts import render_to_response
def customer_list(request):
customer_list = Customer.objects.all().order_by('lastName')[:5]
return render_to_response(
"path_to/template.html",
{'customer_list':customer_list,},
context_instance=RequestContext(request))
I'm new to Django and for my first project I'm building a portfolio. And I need a little kick-start with pagination help. I have an "index" view with a list of projects and a detail view of each project. In the detail view, I want a feature to be able to paginate between each individual object. I've gone through the Pagination documenation and applied what I learned with my index view but when I try to do the same thing with my detail view I get a TypeError saying my "object of type 'Project' has no len().
Here's a sample of my views.py code for reference:
def index( request ):
all_projects = Project.objects.all().order_by( '-pub_date' )
paginator = Paginator( all_projects, 12 )
try:
page = int( request.GET.get( 'page','1' ))
except ValueError:
page = 1
try:
projects = paginator.page( page )
except (EmptyPage,InvalidPage):
projects = paginator.page( paginator.num_pages )
return render_to_response( 'portfolio/index.html', { 'all_projects':all_projects, 'projects':projects, 'MEDIA_URL':MEDIA_URL })
def detail( request, project_id ):
project = get_object_or_404( Project, id=project_id )
return render_to_response( 'portfolio/detail.html', { 'project':project, 'MEDIA_URL':MEDIA_URL } )
Apologies if I sound n00b-ish because I am, and gratitude in advance for any help. Also, I read this previous post but it didn't seem to apply to me because my views aren't Class-based.
Django has built-in get_next_by_FOO()/get_previous_by_FOO() methods which will return the next/previous object depeding on a datetime-field.
You could access them in the template by somethow like:
{{ project.get_next_by_pub_date.title }}
I would say that this is the preferred method over using pagination for that, as you will get a nice url you can define in your model's get_absolute_url for every item!
To paginate you need an instance of a QuerySet, not an object! So you should replace your get_object_or_404 call by a filter/all. So it would be basically the same as the list view, but just pass the number 1 to the paginator, as you already do!
I actually do not have that setup through Django's pagination. I used this code:
prev_issue = Issue.objects.filter(title=title).filter(number__lt=issue.number).order_by('-number')[:1]
next_issue = Issue.objects.filter(title=title).filter(number__gt=issue.number).order_by('number')[:1]
In your case, I would do something like this, but you would have to filter by something, too:
prev_project = Project.objects.order_by('-pub_date')[:1]
next_project = Project.objects.order_by('pub_date')[:1]
Then put the two in the context.
I also recommend django-pagination. I notice you probably want the pagination to stay on the index, correct? http://code.google.com/p/django-pagination/
You just have to mess with the template codes and it works great.
The great thing about Django is, that there is code for nearly every common programming problem, that you can download and use in your own project. (and most of the django packages are quiet well written too)
for pagination check out http://pypi.python.org/pypi/django-pagination
it's really easy to install and setup, so you do not have to think about pagination and focus on coding your software!
hope this helps, Anton
In my views.py
def ProjectDetail(request,pk):
context = {}
template = 'views/projectdetail.html'
project = ''
prev = Project.objects.filter(pk__lt=pk).order_by('-pk')[:1]
next = Project.objects.filter(pk__gt=pk).order_by('pk')[:1]
try:
print(prev[0].pk)
print(next[0].pk)
except:
pass
project = Project.objects.filter(pk=pk)
context['categories'] = ProjectCategory.objects.all()
paginator = Paginator(project, 1) # Show 25 contacts per page
page = request.GET.get('page')
try:
data = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
data = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
data = paginator.page(paginator.num_pages)
if prev:
context['prev'] = prev[0].pk
if next:
context['next'] = next[0].pk
context['data'] = data
return render_to_response(template, context,context_instance=RequestContext(request))
in my template i have
<div class="row">
<a {% if next %} href="{% url 'task:project-detail' next %}" class="btn btn-primary pull-right" {% else %} class="btn btn-primary pull-right disabled" {% endif %}>Next</a>
<a {% if prev %} href="{% url 'task:project-detail' prev %}" class="btn btn-primary pull-left" {% else %} class="btn btn-primary pull-left disabled" {% endif %} >Previous</a>
</div>
I'm using this code for my pagination, and I'd like the user's choice to be persistent throughout the site (this has been solved so far)...the only problem now is that the session variable now is permanent until the session is cleared by closing the browser. Also, how can I get the adjacent pages displayed...like in the digg-style Django paginator. I haven't been able to make sense of how to implement this into my code.
The code is as follows:
from django.core.paginator import Paginator, InvalidPage, EmptyPage
def paginate(request, object_list, paginate_by=10):
try:
if "per_page" in request.session:
per_page = request.session["per_page"]
else:
request.session["per_page"] = int(request.REQUEST['p'])
per_page = request.session["per_page"]
request.session.set_expiry(0)
except:
per_page = 10
paginator = Paginator(object_list, per_page)
try:
page = int(request.GET.get('page', '1'))
except ValueError:
page = 1
try:
items = paginator.page(page)
except (EmptyPage, InvalidPage):
items = paginator.page(paginator.num_pages)
return items
Then in my template I have this to render the pagination links:
<div class="pagination" align="center">
<span class="step-links">
{% if items.has_previous %}
previous
{% endif %}
<span class="current">
Page {{ items.number }} of {{ items.paginator.num_pages }}
</span>
{% if items.has_next %}
next
{% endif %}
</span>
</div>
You can achieve this by enabling sessions.
I recommend reading through the chapter Sessions, Users, and Registration on the djangobook website.
Edit: Now that you've enabled sessions, I think the problem is the hyperlinks in the template. Use an ampersand to separate multiple parameters in a url, for example
next
Edit 2: I'm not sure if I understand what the problem is with the session expiry. The line that sets the session to expire when the browser closes is request.session.set_expiry(0). See the django docs on Using Sessions in views if you want to change that.
To make a Digg style paginator, you need to write a function that takes the current page number and the total number of pages, and returns a list of page numbers. Then, in the template, loop through the page numbers and construct links to the pages.
A list of lists of page numbers would allow you to split the page numbers into groups, eg
[[1,2], [20,21,22,23,24], [30,31]]
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.