flask/flaskr/blog.py
Neo 8e63ba89db Add pagination to blog posts
Implemented pagination in the blog to display 3 posts per page.
This change allows users to navigate through posts using a
pagination control at the bottom of the page. The pagination
follows the pattern: << < 1 2 3 ... n > >>, enabling navigation
to the first, previous, specific, next, or last pages. This
improves user experience by preventing long scrolls on pages
with many posts.

Issue ID: FLAS-33
2024-09-10 00:09:27 +01:00

139 lines
3.7 KiB
Python

from flask import Blueprint
from flask import flash
from flask import g
from flask import redirect
from flask import render_template
from flask import request
from flask import url_for
from werkzeug.exceptions import abort
import markdown
import math
from .auth import login_required
from .db import get_db
bp = Blueprint("blog", __name__)
@bp.route("/")
def index():
"""Show all the posts, most recent first."""
db = get_db()
page = request.args.get('page', 1, type=int)
per_page = 3
total_posts = db.execute("SELECT COUNT(*) FROM post").fetchone()[0]
total_pages = math.ceil(total_posts / per_page)
posts = db.execute(
"SELECT p.id, title, body, created, author_id, username"
" FROM post p JOIN user u ON p.author_id = u.id"
" ORDER BY created DESC"
" LIMIT ? OFFSET ?",
(per_page, (page - 1) * per_page)
).fetchall()
# Convert markdown to HTML for each post body
posts = [
{**post, 'body': markdown.markdown(post['body'])} for post in posts
]
return render_template("blog/index.html", posts=posts, page=page, total_pages=total_pages)
def get_post(id, check_author=True):
"""Get a post and its author by id.
Checks that the id exists and optionally that the current user is
the author.
:param id: id of post to get
:param check_author: require the current user to be the author
:return: the post with author information
:raise 404: if a post with the given id doesn't exist
:raise 403: if the current user isn't the author
"""
post = (
get_db()
.execute(
"SELECT p.id, title, body, created, author_id, username"
" FROM post p JOIN user u ON p.author_id = u.id"
" WHERE p.id = ?",
(id,),
)
.fetchone()
)
if post is None:
abort(404, f"Post id {id} doesn't exist.")
if check_author and post["author_id"] != g.user["id"]:
abort(403)
return post
@bp.route("/create", methods=("GET", "POST"))
@login_required
def create():
"""Create a new post for the current user."""
if request.method == "POST":
title = request.form["title"]
body = request.form["body"]
error = None
if not title:
error = "Title is required."
if error is not None:
flash(error)
else:
db = get_db()
db.execute(
"INSERT INTO post (title, body, author_id) VALUES (?, ?, ?)",
(title, body, g.user["id"]),
)
db.commit()
return redirect(url_for("blog.index"))
return render_template("blog/create.html")
@bp.route("/<int:id>/update", methods=("GET", "POST"))
@login_required
def update(id):
"""Update a post if the current user is the author."""
post = get_post(id)
if request.method == "POST":
title = request.form["title"]
body = request.form["body"]
error = None
if not title:
error = "Title is required."
if error is not None:
flash(error)
else:
db = get_db()
db.execute(
"UPDATE post SET title = ?, body = ? WHERE id = ?", (title, body, id)
)
db.commit()
return redirect(url_for("blog.index"))
return render_template("blog/update.html", post=post)
@bp.route("/<int:id>/delete", methods=("POST",))
@login_required
def delete(id):
"""Delete a post.
Ensures that the post exists and that the logged in user is the
author of the post.
"""
get_post(id)
db = get_db()
db.execute("DELETE FROM post WHERE id = ?", (id,))
db.commit()
return redirect(url_for("blog.index"))