First/Previous and Next/Last links for Django paginated query - python

I've made a filter form using Django, which returns a list of paginated matches.
When I initially run the filter, I get the correct number of pages showing. However, when I click on another page number, the page then reverts to that relevant page number for the unfiltered results. For example, I filter the results on 'Bug' and I get all the paginated results for tickets with a type of 'Bug'. The pagination shows the links to the different pages (1, 2, 3, etc.) and also the next and last buttons. However, if I click on '3', the results revert to the unfiltered results, I'm given the results for page 3 of the unfiltered results instead of page 3 of the filtered results, and the total number of pages for the unfiltered results show in the pagination links.
Please could anybody help me to understand how to fix the issue and keep the filtered results when navigating through the pagination?
all_tickets.html:
// filter form code //
// Displayed results (unfiltered results displayed when page is first loaded) //
<!-- Pagination - only visible if there are multiple pages -->
{% if tickets.has_other_pages %}
<ul class="pagination center-align">
<!-- Previous/First button - only visible if previous page available -->
{% if tickets.has_previous %}
<!-- First page button -->
<li>
<a href="?page=1">
<i class="fas fa-angle-double-left" aria-hidden="true"></i>
</a>
</li>
<!-- Previous page button -->
<li>
<a href="?page={{ tickets.previous_page_number }}">
<i class="fas fa-angle-left" aria-hidden="true"></i>
</a>
</li>
{% endif %}
<!-- Show link to current page and only few other surrounding pages, not all -->
{% for num in tickets.paginator.page_range %}
<!-- Show the current page number but disable it -->
{% if tickets.number == num %}
<li class="disabled">
<a>
{{ num }}
</a>
</li>
<!-- Show the 4 surrounding (2 next and 2 previous) pages -->
{% elif num > tickets.number|add:'-3' and num < tickets.number|add:'3' %}
<li>
<a href="?page={{ num }}">
{{ num }}
</a>
</li>
{% endif %}
{% endfor %}
<!-- Next/Last button - only visible if previous page available -->
{% if tickets.has_next %}
<!-- Next page button -->
<li>
<a href="?page={{ tickets.next_page_number }}">
<i class="fas fa-angle-right" aria-hidden="true"></i>
</a>
</li>
<!-- Last page button -->
<li>
<a href="?page={{ tickets.paginator.num_pages }}">
<i class="fas fa-angle-double-right" aria-hidden="true"></i>
</a>
</li>
{% endif %}
</ul>
{% endif %}
search view in views.py file:
def view_all_tickets(request):
'''
View all tickets
Allows users to filter tickets based on type or status
'''
tickets = Ticket.objects.all()
page = request.GET.get('page', 1)
ticket_type_dropdown = TicketType.objects.all()
ticket_status_dropdown = TicketStatus.objects.all()
# Query parameters
ticket_type = request.GET.get("ticket_type")
ticket_status = request.GET.get("ticket_status")
# Filter by query parameters
if ticket_type:
tickets = tickets.filter(ticket_type__id=ticket_type)
else:
tickets
if ticket_status:
tickets = tickets.filter(ticket_status__id=ticket_status)
else:
tickets
# Pagination
paginator = Paginator(tickets, 1)
try:
tickets = paginator.page(page)
except PageNotAnInteger:
tickets = paginator.page(1)
except:
tickets = paginator.page(paginator.num_pages)
args = {
"tickets": tickets,
"ticket_type_dropdown": ticket_type_dropdown,
"ticket_status_dropdown": ticket_status_dropdown,
}
return render(request, "all_tickets.html", args)

You need to include all the query params in every link. So you should pass them back to the template and add them to the params:
args = {
"tickets": tickets,
"ticket_type_dropdown": ticket_type_dropdown,
"ticket_status_dropdown": ticket_status_dropdown,
"ticket_type": ticket_type,
"ticket_status": ticket_status,
}
return render(request, "all_tickets.html", args)
...
<a href="?page={{ tickets.previous_page_number }}&ticket_type={{ ticket_type }}&ticket_status={{ ticket_status }}">
etc.
To make this easier you could define a template filter that outputs the existing link automatically and allows you to append only the page number variable, some of the third-party pagination libraries probably include this.

Related

How can I open a page with only one django model object's info after clicking it's ImageField on another page?

I am displaying django models on one of my website's pages. When I press one's ImageField on the page, I want it to open another page including only that one object. How do I do that ?
I thought about using the filter method in my views.py for filtering through my objects and finding that exact one, but I don't know what arguments to use.
Any ideas? (I am a beginner in django)
VIEWS.PY
from django.shortcuts import render
import requests
from . import models
def index(request):
return render(request, 'base.html')
def new_search(request): ********NOT IMPORTANT (I THINK)********
search = request.POST.get('search')
models.Search.objects.create(search=search)
objects = models.Object.objects.all()
results = objects.filter(name__contains=search).all()
args = { 'results': results }
return render(request, "my_app/new_search.html", args)
def individual_page(request):
link = request.GET.get('object-link')
objects = models.Object.objects.all()
return render(request, "my_app/individual_page.html")
MY TEMPLATE
{% extends "base.html" %}
{% block content %}
{% load static %}
<h2 style="text-align: center">{{ search | title }}</h2>
<div class="row">
{% for result in results %}
<div class="col s4">
<div class="card medium">
<div class="card-image">
<a name="object-link" href="{% url 'individual_page' %}"><img src="{{ result.image.url }}" alt=""></a>
</div>
<div class="card-content">
<p>{{result.name}}</p>
</div>
<div class="card-action">
View listing: Price TEST
</div>
</div>
</div>
{% endfor %}
</div>
{% endblock %}
So, the thing I want to do is: when I press the anchor tag that includes the image, I get redirectioned to another page which contains only that one object's info.

how to do ajax pagination in Django?

!! I have a model named blog !!
class blog(models.Model):
image = models.ImageField(upload_to='Media/awards')
title = models.CharField(max_length=255,blank=True)
content = models.TextField(blank=True)
and in the frontend I have
<div class="col-md-6" id= "mydiv">
<div>
<!-- pagination design start -->
<div class="blog_bottom_pagination">
<div class="counter">/</div>
<button class="paginate left"><i class="fas fa-angle-left"></i></button>
<button class="paginate right">
<i class="fas fa-angle-right"></i>
</button>
</div>
<!-- pagination design end -->
I don't find any reference how i implement pagination without page refresh and render two data at a time in that div section. and paginate right and left by the button for getting next two data and will replace those previous data in that div.... thank you in advance
So the view side should look like this:
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
blog_objects= Blog.objects.filter()
paginator = Paginator(blog_objects, 10)
page = request.GET.get('page', 1)
try:
blogs = paginator.page(page)
except PageNotAnInteger:
blogs = paginator.page(1)
except EmptyPage:
blogs = paginator.page(paginator.num_pages)
page_list = blogs.paginator.page_range
Button to trigger ajax function on template:
{% for i in page_list %}
<button onclick="ajax_function('{{i}}','{{title}}')">{{i}}</button>
Note "i" is the page number for the ajax function and "title" is the argumant for query.
Ajax function from template is at the end...
Ajax view:
def paginate(request):
page= request.GET.get('page', None)
title= request.GET.get('title', None)
starting_number= (page-1)*10
ending_number= page*10
"here you should multiply the 'page' by the number of results you want per page, in my example it's 10"
result= Blog.objects.filter(title= title)[starting_number:ending_number]
"By [starting_number:ending_number] we specify the interval of results. Order them by date or whatever you want"
data={result}
return JsonResponse(data)
The result object is now sent to the html side, now its time for the ajax function:
function ajax_function(page,title) {
$.ajax({
url: '/ajax/paginate/',
type: "get",
data: {
'page': page,
'title': title,
},
dataType: 'json',
success: function (data) {
$('#id-of-your-choice').empty();
for (i = 0; i < data.result.length; i++) {
$('#id-of-your-choice').append(i)
}
"at first the function cleans the old childs of the element on which you display your results then it appends the new page's content by iterating over 'results'"
Hope that is what you are looking for, have fun!
In Django, the HTML is filled with data before getting served to the end client. So if you want next page data in your front-end code and that too from Django Views then you will have to go to backend again and get the data ant that will require the page to be reloaded.
If you don't want to reload the page then you will have to write APIs using Django Rest Framework. And in the front-end code just use those APIs to navigate between different pages.
Hope this helps!!
First of all in your views.py write this in your view function from which you are rendering this template
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from .models import blog
def your_view_function(request):
blogs = blog.objects.all()
page = request.GET.get('page', 1)
paginator = Paginator(blogs, 5) #number of blogs you want to paginate
try:
blogs = paginator.page(page)
except PageNotAnInteger:
blogs = paginator.page(1)
except EmptyPage:
blogs = paginator.page(paginator.num_pages)
return render(request,'your_html_page.html',{'blogs':blogs}
and in your html page write this most preferred boostrap pagination
<nav aria-label="Page navigation example">
<ul class="pagination justify-content-start mt-3">
{% if not blogs.has_previous%}
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1">Previous</a>
</li>
{% endif %}
{% if blogs.has_previous%}
<li class="page-item">
<a class="page-link" href="?page={{blogs.previous_page_number}}" tabindex="-1">Previous</a>
</li>
{% endif %}
{% if blogs.has_previous%}
<li class="page-item"><a class="page-link" href="?page={{blogs.previous_page_number}}">{{blogs.previous_page_number}}</a></li>
{% endif %}
<li class="page-item"><a class="page-link" href="#">{{blogs.number}}</a></li>
{% if blogs.has_next%}
<li class="page-item"><a class="page-link" href="?page={{blogs.next_page_number}}">{{blogs.next_page_number}}</a></li>
{% endif %}
{% if blogs.has_next%}
<li class="page-item">
<a class="page-link" href="?page={{blogs.next_page_number}}">Next</a>
</li>
{% endif %}
{% if not blogs.has_next%}
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1">Next</a>
</li>
{% endif %}
</ul>
</nav>

Django user authentication working properly EXCEPT 1 view/template

I am working with Django 1.11.5 and logging-in and validating users via Social Auth.
The user authentication, log-in and logoff are working as expected in 12/13 of my templates. All my templates extend my navbar and footer base.html template.
In 'base.html' I have the following code for the navbar:
{% if user.is_authenticated %}
<li class="nav-item">
<span class="nav-link" id="user-name">{{ request.user.username }}</span>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'logout' %}">Logoff</a>
</li>
{% else %}
<li class="nav-item log-in-link">
<a class="btn btn-primary nav-link log-in" href="{% url 'social:begin' 'google-oauth2' %}"><span>Log-In <i class="fa fa-google-plus" aria-hidden="true"></i></span></a>
</li>
{% endif %}
Now, this navbar code works properly in 12/13 of my templates. It is only for 1 template/view/URL where the user is not authenticated.
I have tried debugging by printing out the user name and information in the template giving me errors but it looks like the user is getting logged out when reaching that template via its URL and view.
I am totally lost.
Could someone point out things I can check/do to debug and locate the source of this logoff error? I can provide the relevant code if needed.
views.py code
def details_request(request, request_data_ID):
data_request_object = DatasetRequest.objects.get(pk=request_data_ID)
user_ID = data_request_object.user.username
has_attributes = False
request_data_attrs = []
if len(data_request_object.attr_names) > 0:
if len(data_request_object.attr_names['names']) > 0:
has_attributes = True
for idx, attr_names in enumerate(data_request_object.attr_names['names']):
request_data_attrs.append([attr_names,
data_request_object.attr_names['descriptions'][idx],
data_request_object.attr_names['types'][idx]])
data_request_detail_template = {
'dataset_request_title': data_request_object.dataset_title,
'dataset_request_description': data_request_object.dataset_description,
'dataset_votes': data_request_object.dataset_votes,
'dataset_date': data_request_object.created_date.strftime("%Y-%m-%d"),
'request_data_ID': request_data_ID,
'has_attributes': has_attributes,
'request_attrs': request_data_attrs,
'user': user_ID,
'is_completed': data_request_object.is_completed
}
data_comment_object = Comments.objects.filter(request_dataset_FK=data_request_object).order_by("-comment_votes")
if len(data_comment_object) > 0:
comment_list = []
for comment_object in data_comment_object:
if comment_object.isComment:
comment_list.append([comment_object.comment, comment_object.created_date.strftime("%Y-%m-%d"), comment_object.comment_votes, comment_object.pk, comment_object.user.username])
data_request_detail_template['comments'] = comment_list
return render(request, "detail_requests.html", data_request_detail_template)
You are specifying:
user_ID = data_request_object.user.username
and put it into context under user key.
In template you have {% if user.is_authenticated %} which means that you are trying to access missing attribute is_authenticated of user.username which always evaluates to False.

Python Flask SQLAlchemy-Paginator Access next page in html view

I have currently a problem to include the pagination function into my project. I know there is LIMIT/OFFSETor yield_per(), but I was not able to implement them.
I am using SQLAlchemy not Flask-SQLAlchemy so paginate wont work.
My Database is not that big. I am trying to show rooms which have been added by a user. So all in all a user will have 20~ rooms, big users maybe 100. I want to show on the profile page the 6 last inserted rooms and if there are more, there should be pagination, like page 2 shows the next 6 etc.
I am using SQLAlchemy-Paginator.
I already implemented it and tested it, it works fine. It also limits already the results depending on which page I am. But how do I access the next page while on HTML?
Here is the python code:
#app.route("/user/logged_in")
#login_required
#check_confirmed
def logged_in():
if current_user.email_verified:
users_room = db_session.query(Zimmer).filter_by(users_id=current_user.id).order_by(desc("id"))
paginator = Paginator(users_room, 2)
for page in paginator:
print "page number of current page in iterator", page.number
print "this is a list that contains the records of current page", page.object_list
return render_template('logged_in.html', paginator=paginator)
return redirect(url_for('unconfirmed'))
Here is the view. The solution must be somewhere here. I can access pages by page.previous_page_number or page.next_page_number. But there is no example in the docu how to do it in view.
<div class="user-rooms">
<h2> Ihre Zimmer </h2>
{% for page in paginator %}
{% if page.number == 1 % }
{% for zimmer in page.object_list %}
{% if zimmer.users_id == current_user.id %}
<div class="col-sm-4 col-xs-6 col-xxs-12 img-holder">
<img src="../static/userimg/{{ zimmer.hauptbild }}"/>
<div class="buttons-del-work"> Bearbeiten Löschen </div>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
If I manually change the page numbers here it show me the correct items, so I feel like I am close:
{% if page.number == 1 % }
Okay here is a solution (which does not use any further methods from the SQLAlchemy-Paginator package). I coded everything myself, but I would still like to know how it is done with page.next_page_number etc.
Explained:
First of all I added an argument (pagenumber) to my logged_in function. Everytime url_for("logged_in", pagenumber=1) is called the pagenumber has to be set to the defaultvalue 1.
I created an empty list, where I add all the page.number items, so I know how many pages my resultset will have:
pages_list = []
for page in paginator:
pages_list.append(page.number)
I use the pages_list also in the view to generate the buttons which can be clicked to see the next page, I also give the pagenumber to the html view:
return render_template('logged_in.html', paginator=paginator, pagenumber=pagenumber, pages_list=pages_list)
Here is the HTML view where I show the buttons:
<div class="col-xs-12">
{% for number in pages_list %}
{{ number }}
{% endfor %}
</div>
Now if a user clicks one of the Buttons the logged_in will be called with a new pagenumber argument (the actual pagesite you clicked)
In the logged_in I added also typecasted pagenumber to int before giving it to html view:
pagenumber = int(pagenumber)
Solution Code
Python:
def logged_in(pagenumber):
if current_user.email_verified:
users_room = db_session.query(Zimmer).filter_by(users_id=current_user.id).order_by(desc("id"))
paginator = Paginator(users_room, 2)
pages_list = []
for page in paginator:
pages_list.append(page.number)
pagenumber = int(pagenumber)
return render_template('logged_in.html', paginator=paginator, pagenumber=pagenumber, pages_list=pages_list)
return redirect(url_for('unconfirmed'))
HTML:
<div class="user-rooms">
<h2> Ihre Zimmer </h2>
{% for page in paginator %}
{% if page.number == pagenumber %}
{% for zimmer in page.object_list %}
<div class="col-sm-4 col-xs-6 col-xxs-12 img-holder">
<img src="../static/userimg/{{ zimmer.hauptbild }}"/>
<div class="buttons-del-work"> Bearbeiten Löschen </div>
</div>
{% endfor %}
{% endif %}
{% endfor %}
</div>
<div class="col-xs-12">
{% for number in pages_list %}
{{ number }}
{% endfor %}
</div>

URLs being produced correctly but not directing to correct template

I'm trying to follow the Simple Blog tutorial at Django By Example. I've managed to get as far as producing a site that loads correctly, but while the index view is loading find, and the links to the individual posts show up and appear to be formatted correctly, they point back to the index template so all that happens when you click on them is that it reloads the index view. I'm new to Django and the tutorial is sparse to say the least and not helped by the fact it's written for an old version of Django and I'm using 1.5. I've been staring at it all afternoon and I'm pretty lost.
Here's my urls.py
from django.conf.urls import patterns, url
from blog import views
urlpatterns = patterns('blog.views',
#index
(r"$", 'main'),
#ex: /1/
(r"^(\d+)/$", 'post'),
#ex: /add_comment/1/
(r"^add_comment/(\d+)/$", 'add_comment'),
)
my views.py
from blog.models import Post, PostAdmin, Comment, CommentAdmin
class CommentForm(ModelForm):
class Meta:
model = Comment
exclude = ["post"]
def main (request):
"""Main Listing."""
posts = Post.objects.all().order_by("-created")
paginator = Paginator(posts, 10)
try: page = int(request.GET.get("page", '1'))
except ValueError: page = 1
try:
posts = paginator.page(page)
except (InvalidPage, EmptyPage):
posts = patinator.page(paginator.num_pages)
return render_to_response("blog/list.html", dict(posts=posts, user=request.user))
def post (request, pk):
"""single post with comments and comment form"""
post = Post.objects.get(pk=int(pk))
comments = Comment.objects.filter(post=post)
d = dict(post=post, comments=comments, form=CommentForm(), user=request.user)
d.update(csrf(request))
return render_to_response("blog/post.html", d)
and the list.html that contains the links that aren't going anywhere!
{% extends "blog/bbase.html" %}
{% block content %}
<div class="main">
<!-- Posts -->
<ul>
{% for post in posts.object_list %}
<div class="title">{{ post.title }}</div>
<ul>
<div class="time">{{ post.created }}</div>
<div class="body">{{ post.body|linebreaks }}</div>
<div class="commentlink">Comments</div>
</ul>
{% endfor %}
</ul>
<!-- Next/Prev page links -->
{% if posts.object_list and posts.paginator.num_pages > 1 %}
<div class="pagination" style="margin-top: 20px; margin-left: -20px; ">
<span class="step-links">
{% if posts.has_previous %}
newer entries <<
{% endif %}
<span class="current">
Page {{ posts.number }} of {{ posts.paginator.num_pages }}
</span>
{% if posts.has_next %}
>> older entries
{% endif %}
</span>
</div>
{% endif %}
</div>
{% endblock %}
The Django URL resolver will return the first URL pattern that matches the incoming request. The regex for your 'main' view r"$" will match ANY incoming request since you are only looking for $ which is an end of string character.
You need to alter your 'main' URL regex to be r'^$'.
Alternatively, if you would like a catch-all view, you could move the 'main' view to the bottom of your URLs

Categories