I am having trouble implementing pagination with Flask-SQLAlchemy or Flask-Pagination, either or. I am unsure how to initialize the pagination, setting pages, determining pages, offest, etc. I am coming from PHP, quite new to Python.
I am querying all the posts in my database
posts = Posts.query.order_by(Posts.time.desc()).all()
I have been looking at the following examples:
http://www.ergo.io/tutorials/better-pagination-in-flask/better-pagination-in-flask/
https://pythonhosted.org/Flask-paginate/
sqlalchemy pagination
I am confused on really what to do, the information I am finding greatly differs between articles. It's left me with confusion and not knowing where to begin. I want to query all rows of the database table, limit the results to 20 and paginate. I'm not seeing this clearly.
I recommend using Flask-SQLAlchemy's pagination: http://flask-sqlalchemy.pocoo.org/2.1/api/?highlight=pagination#flask.ext.sqlalchemy.Pagination
There's a well-written example here: https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-ix-pagination
Here's the basic idea for the view:
#app.route('/myview/<int:page>',methods=['GET'])
def view(page=1):
per_page = 10
posts = Posts.query.order_by(Posts.time.desc()).paginate(page,per_page,error_out=False)
return render_template('view.html',posts=posts)
And then for the template (I don't know your posts model so I made something up):
<html>
<head>
Posts
</head>
<body>
{% for post in posts.items %}
<p>
{{ post.post_name }} post body: <b>{{ post.body }}</b>
</p>
{% endfor %}
{% if posts.has_prev %}<< Newer posts{% else %}<< Newer posts{% endif %} |
{% if posts.has_next %}Older posts >>{% else %}Older posts >>{% endif %}
</body>
</html>
Controller
#app.route('/', methods=['GET'], defaults={"page": 1})
#app.route('/<int:page>', methods=['GET'])
def index(page):
page = page
per_page = 2
users = User.query.paginate(page,per_page,error_out=False)
# print("Result......", users)
return render_template("index.html", users=users)
in the View
{% for user in users.items %}
<h1> {{ user.name }} </h1>
{% endfor %}
<nav aria-label="Page navigation example">
<ul class="pagination">
{% if users.has_prev %}
<li class="page-item"> <a class="page-link" href="{{ url_for('index', page=users.prev_num) }}">Previous</a></li>
{% else %}
<li class="page-item"><a class="page-link btn disabled" href="#">Previous</a></li>
{% endif %}
{% if users.has_next %}
<li class="page-item"> <a class="page-link" href="{{ url_for('index', page=users.next_num) }}">Next</a></li>
{% else %}
<li class="page-item"><a class="page-link btn disabled" href="#">Next</a></li>
{% endif %}
</ul>
</nav>
I used #user3186184 code but I got this error:
TypeError: 'Pagination' object is not iterable
But when I add items to the end of these code:
users = User.query.paginate(page,per_page,error_out=False).items
My issue was fixed.
Here is the simplest answer to add pagination in flask
no_of_posts = 3
page = request.args.get("number")
if page is None:
page = 1
else:
page = int(page)
allPosts = Posts.query.filter_by().all()
length = len(allPosts)
allPosts = allPosts[(page-1)*no_of_posts: page*no_of_posts]
if page > 1:
prev = page -1
else:
prev = None
if page < math.ceil(length/no_of_posts):
next = page + 1
else:
next = None
In the template
{% if prev %}
« Previous
{% endif %}
{% if next %}
Next »
{% endif %}
Related
I have a list of data from my models that I would like to paginate as it looks flooded on one singular page and it generally takes a longer time for the page to load. However, when I tried to use a paginating method, it doesn't seem to work in my code.
What I've already done for my code is:
.../clubs/views.py
class ClubListView(generic.ListView):
model = Club
paginate_by = 6
.../clubs/urls.py
from django.urls import path
from . import views
app_name = "clubs"
urlpatterns = [
path('', views.ClubListView.as_view(), name="club-list"),
path('<int:pk>/', views.ClubDetailView.as_view(), name="club-detail"),
]
.../clubs/club_list.html
{% block group_content %}
<hr>
{% comment %} Complete making group display. {% endcomment %}
<p></p>
<div class="col-md-8">
<div class="container">
{% for club in club_list %}
<a class="list-group-item" href="{{ club.get_absolute_url }}">
<h3 class="title list-group-item-heading">{{ club.name }}</h3>
<span>{{ club.slogan|safe }}</span>
</a>
<p></p>
<br>
{% endfor %}
</div>
</div>
{% endblock %}
{% block pagination %}
{% if page_obj.has_previous %}
<a href="{% url 'club-list' page_obj.previous_page_number %}">
Previous Page
</a>
{% endif%}
{% if page_obj.has_next %}
<a href="{% url 'club-list' page_obj.next_page_number %}">
Next Page
</a>
{% endif%}
{% endblock %}
However, it still gives me the error of NoReverseMatch of 'club-list' in my html page even though it is given from the urls.py file. I'm not sure if I don't understand where the issue is coming from.
You are passing the next page number/prev page number as part of the {% url %} function which creates the path, however your URLS.py isn't expecting it as part of the URL path. eg, you don't have a urlpattern for listview/12/.
For a list view, by default next and prev pages numbers gets passed as part of the querystring rather than the path,eg,
/listview?page=12
so your template should look more like (from the docs)
{% block pagination %}
{% if page_obj.has_previous %}
« first
previous
{% endif %}
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
{% if page_obj.has_next %}
next
last »
{% endif %}
{% endblock %}
after writing the view for my pagination in django, the button works fine, meaning that they load new pagesz but the problem is that all the posts still remains in all the new pages and that is not what's expected.
views.py
def ElementLists(request):
vectors = Vectors.objects.filter(status="published").order_by("?")
paginator = Paginator(vectors, 6)
page_number = request.GET.get('page')
vector_paginator = paginator.get_page(page_number)
elementlist.html
<li class="page-item">
{% if vector_paginator.has_previous %}
<a class="page-link" href="?page={{vector_paginator.previous_page_number}}" arialabel="Previous">
<span class="ti-arrow-left">Previous</span>
<span class="sr-only">Previous</span>
</a>
{% endif %}
</li>
<li class="page-item">
{% if vector_paginator.has_next %}
<a class="page-link" href="?page={{vector_paginator.next_page_number}}" aria-label="Next">
<span class="ti-arrow-right">Load More</span>
<span class="sr-only">Next</span>
</a>
{% endif %}
</li>
I later got a fix for it
when looping thorught your posts in template, it should now be this
before
{% for post in posts %}
...code here
{% endfor %}
Now
{% for post in posts_paginator %}
...code here
{% endfor %}
What can I do when my pagination breaks after first page when I specify the keyword in a form for search results? When I'am moving over pages without any filters, everything works perfectly fine. I had the same problem in my Django project, but I fixed it by creating a custom template tag, it was hard tho and now I can't manage to find any solution for my Flask App. Any ideas? Maybe modifying url?
1st page when "jquery" keyword is specified:
2nd page - completly breaks pagination and search result:
VIEWS.PY
#app.route('/cve_list', methods=['GET', 'POST'])
def cve_list():
search_form = SearchForm()
page = request.args.get('page', 1, type=int)
cve = CVE.query.order_by(CVE.date.desc()).paginate(page=page, per_page=2)
if search_form.validate_on_submit(): # = when search_form was submitted
form_data = search_form.search.data
query = CVE.query.filter(CVE.content.contains(f'{form_data}')).paginate(page=page, per_page=2)
cve_amount = CVE.query.filter(CVE.content.contains(f'{form_data}')).count()
if cve_amount > 0:
flash(f'Found {cve_amount} vulnerabilities based on your search - {form_data}', 'success')
else:
flash(f'No vulnerabilities found based on your search - {form_data}', 'warning')
return render_template('cve_list.html',
title=f'Cve{form_data}',
form=search_form,
cve=query,
cve_amount=cve_amount)
return render_template('cve_list.html',
title='Cve List',
form=search_form,
cve=cve)
HTML
{% for cve in cve.items %}
<article class="media content-section">
<div class="media-body">
<div class="article-metadata">
<a class="mr-2">{{ cve.title }}</a>
<small class="text-muted">{{ cve.date }}</small>
</div>
<p class="article-content">{{ cve.content }}</p>
</div>
</article>
{% endfor %}
<!-- PAGINATION -->
{% for page_num in cve.iter_pages(left_edge=1, right_edge=1, left_current=1, right_current=2) %}
{% if page_num %}
{% if cve.page == page_num %}
<a class="btn btn-info mb-4" href="{{ url_for('cve_list', page=page_num) }}">{{ page_num }}</a>
{% else %}
<a class="btn btn-outline-info mb-4" href="{{ url_for('cve_list', page=page_num) }}">{{ page_num }}</a>
{% endif %}
{% else %}
{% endif %}
{% endfor %}
<!-- PAGINATION -->
i'm create template for search this table.it can search but click for next page is error in the picture
For Error
Same mycode.
#view.py
def search(request):
query = request.GET.get('q1')
qselect = request.GET.get('q2')
qs = Record_data.objects.filter(Q(invoice_no__startswith=query) | Q(product=query))
page = request.GET.get('page', 1)
paginator = Paginator(qs, 5)
try:
page1 = paginator.page(page)
except PageNotAnInteger:
page1 = paginator.page(1)
except EmptyPage:
page1 = paginator.page(paginator.num_pages)
template_name = 'status.html'
context = {'object_list': page1}
#
page function
#html code
{% if object_list.has_other_pages %}
<ul class="pagination">
{% if object_list.has_previous %}
<li class="page-item"><a class="page-link" href="?page={{ object_list.previous_page_number }}">Previous</a></li>
{% else %}
<li class="page-item disabled"><span class="page-link">Previous</span></li>
{% endif %}
{% for i in object_list.paginator.page_range %}
{% if object_list.number == i %}
<li class="page-item active"><span class="page-link">{{ i }}</span></li>
{% else %}
<li class="page-item"><a class="page-link" href="?page={{ i }}">{{ i }}</a></li>
{% endif %}
{% endfor %}
{% if object_list.has_next %}
<li class="page-item"><a class="page-link" href="?page={{ object_list.next_page_number }}">Next</a></li>
{% else %}
<li class="page-item disabled"><span class="page-link">Next</span></li>
{% endif %}
</ul>
{% endif %}
Rather than hard coding why not use the data tables jquery plugin?
https://datatables.net
use the above links and customise to your use ... i aint marketing but its simple to use a jquery plugin for data tables and hard coding will be easy for whole page Pagination.
I am trying to use a pagination to paginate one of my pages. I have tried a few different methods I have found online but for some reason, when I use paginate_by = 3, it doesn't actually paginate anything, yet still shows the html for the pagination at the bottom of the page.
View:
class SearchListView(ListView):
model = Post
template_name = "public/search.html"
paginate_by = 3
HTML:
{% extends 'public/base.html' %}
{% load staticfiles %}
{% block head %}
<link rel="stylesheet" type="text/css" href="{% static "public/css/search.css" %}" />
{% endblock %}
{% block content%}
<div class="search container-fluid">
<img src="/media/about-us.jpg" alt="">
<div class="search-title">
<h1 class="title">Search</h1>
</div>
<div class="search-main mb-5">
<form method='GET' action=''>
<input type="text" name='q' class="homebanner-search" placeholder="Enter your keywords" value='{{ request.get.q }}'>
</form>
</div>
</div>
<div class="container mt-5 mb-5">
<div class="detail-container">
{% for post in queryset %}
<a href="{% url 'post-detail' post.slug %}">
<div class="post-main">
<div class="post-image">
<img src="{{ post.image.url }}" class="card-img-top" alt="#">
<p class="post-category">{{ post.category }}</p>
</div>
<div class="post-body">
<div class="post-title">
<p class="post-title-p">Day in the life of {{ post.title }}</p>
</div>
<div class="post-text">
<p class="post-author-text text-muted">{{ post.sub_description|truncatewords:22 }}</p>
</div>
<div class="post-button">
<p>READ MORE ></p>
</div>
</div>
</div>
</a>
{% endfor %}
</div>
<div id="page_navigation" >
{% if is_paginated %}
<ul class="pagination">
{% if page_obj.has_previous %}
<li>«</li>
{% else %}
<li class="disabled"><span>«</span></li>
{% endif %}
{% for i in paginator.page_range %}
{% if page_obj.number == i %}
<li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
{% else %}
<li>{{ i }}</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li>»</li>
{% else %}
<li class="disabled"><span>»</span></li>
{% endif %}
</ul>
{% endif %}
</div>
</div>
{% endblock %}
So on the page, 3 items should be showing, and pagination should take me to the the next set of 3. The html is showing, and when I click the links it is taking me 2 page 2. The problem is the fact that 6 items are showing, and not 3, and when I go to page 2, there are the same 6 items there.
Unfortunately I couldn't reproduce your error exactly, but what did happen is that my objects wouldn't render when looping through queryset. So what I would recommend is try to loop through object_list instead:
{% for post in object_list %}
{{ post.name }}
{% endfor %}
Another thing you can do is add a context_object_name argument to your view:
class SearchListView(ListView):
model = Post
template_name = "public/search.html"
paginate_by = 3
context_object_name = 'posts'
and then loop through that:
{% for post in posts %}
{{ post.name }}
{% endfor %}
Also, I can't visualise what the search form is doing on this page since the ListView's model (Post) is the queryset, not whatever is searched for? So perhaps the link href is causing some trouble. Maybe try something like this instead:
<li>«</li>
Again, I could not reproduce the exact problem you are having, which is a shame because I feel like I have had the same issue before, so these are just suggestions pieced together from pagination that works for me and the code you posted. Hope it points you in the right direction.