Skip to content

Commit

Permalink
Merge pull request #11 from stuartmaxwell/dev
Browse files Browse the repository at this point in the history
Add Pagination
  • Loading branch information
stuartmaxwell authored Jun 7, 2024
2 parents e71c80c + 442442a commit 8dbde10
Show file tree
Hide file tree
Showing 8 changed files with 238 additions and 99 deletions.
35 changes: 19 additions & 16 deletions djpress/models/post.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,30 +26,33 @@ def get_queryset(self: "PostsManager") -> models.QuerySet:
"""Return the queryset for published posts."""
return super().get_queryset().filter(post_type="post").order_by("-date")

def _get_published_posts(self: "PostsManager") -> models.QuerySet:
"""Returns all published posts.
For a post to be considered published, it must meet the following requirements:
- The status must be "published".
- The date must be less than or equal to the current date/time.
"""
return self.get_queryset().filter(
status="published",
date__lte=timezone.now(),
)

def get_recent_published_posts(self: "PostsManager") -> models.QuerySet:
"""Return recent published posts.
This does not return a paginated queryset. Use get_paginated_published_posts
instead.
If CACHE_RECENT_PUBLISHED_POSTS is set to True, we return the cached queryset.
"""
if settings.CACHE_RECENT_PUBLISHED_POSTS:
return self._get_cached_recent_published_posts()

return self._get_published_posts().prefetch_related("categories", "author")[
return self.get_published_posts().prefetch_related("categories", "author")[
: settings.RECENT_PUBLISHED_POSTS_COUNT
]

def get_published_posts(self: "PostsManager") -> models.QuerySet:
"""Returns all published posts.
For a post to be considered published, it must meet the following requirements:
- The status must be "published".
- The date must be less than or equal to the current date/time.
"""
return self.get_queryset().filter(
status="published",
date__lte=timezone.now(),
)

def _get_cached_recent_published_posts(self: "PostsManager") -> models.QuerySet:
"""Return the cached recent published posts queryset.
Expand Down Expand Up @@ -117,7 +120,7 @@ def get_published_post_by_slug(
# If the post is not found in the cache, fetch it from the database
if not post:
try:
post = self._get_published_posts().get(slug=slug)
post = self.get_published_posts().get(slug=slug)
except Post.DoesNotExist as exc:
msg = "Post not found"
raise ValueError(msg) from exc
Expand Down Expand Up @@ -178,7 +181,7 @@ def get_published_posts_by_category(
category, ordered by date in descending order.
"""
return (
self._get_published_posts()
self.get_published_posts()
.filter(categories=category)
.prefetch_related("categories", "author")
)
Expand All @@ -193,7 +196,7 @@ def get_published_posts_by_author(
author, ordered by date in descending order.
"""
return (
self._get_published_posts()
self.get_published_posts()
.filter(author=author)
.prefetch_related("categories", "author")
)
Expand Down
11 changes: 8 additions & 3 deletions djpress/templates/djpress/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ <h1>{% blog_title_link %}</h1>
<main>

{% have_posts as posts %}
{% for post in posts %}

{% category_name "h1" pre_text="View Posts in the " post_text=" Category" %}
{% author_name "h1" pre_text="View Posts by " %}
{% category_name "h1" pre_text="View Posts in the " post_text=" Category" %}
{% author_name "h1" pre_text="View Posts by " %}

{% for post in posts %}

<article>
<header>
Expand All @@ -42,6 +43,10 @@ <h1>{% post_title_link %}</h1>

{% endfor %}

{% posts_nav_links %}

<br><br><br>

</main>

<footer>
Expand Down
65 changes: 62 additions & 3 deletions djpress/templatetags/djpress_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from django import template
from django.contrib.auth.models import User
from django.core.paginator import Page
from django.db import models
from django.template import Context
from django.urls import reverse
Expand Down Expand Up @@ -83,22 +84,27 @@ def blog_categories(


@register.simple_tag(takes_context=True)
def have_posts(context: Context) -> list[Post | None]:
def have_posts(context: Context) -> list[Post | None] | Page:
"""Return the posts in the context.
If there's a `_post` in the context, then we return a list with that post.
If there's a `_posts` in the context, then we return the posts. The `_posts` should
be a Page object.
Args:
context: The context.
Returns:
list[Post]: The posts in the context.
"""
post: Post | None = context.get("_post")
posts: models.QuerySet[Post] | None = context.get("_posts")
posts: Page | None = context.get("_posts")

if post:
return [post]
if posts:
return list(posts)
return posts

return []

Expand Down Expand Up @@ -462,3 +468,56 @@ def post_categories_link(
return ""

return mark_safe(categories_html(categories, outer, outer_class, link_class))


@register.simple_tag(takes_context=True)
def posts_nav_links(
context: Context,
) -> str:
"""Return the previous and next post links.
This checks if there is a Page object in the context. If there is, then we return
the previous and next post links. If there is no Page object in the context, then
we return an empty string.
Args:
context: The context.
Returns:
str: The previous and next post links.
"""
page: Page | None = context.get("posts")
if not page or not isinstance(page, Page):
return ""

if page.has_previous():
previous_output = (
f'<span class="previous">'
f'<a href="?page=1">&laquo; first</a> '
f'<a href="?page={page.previous_page_number()}">previous</a>'
f"</span>"
)
else:
previous_output = ""

if page.has_next():
next_output = (
f'<span class="next">'
f'<a href="?page={page.next_page_number()}">next</a> '
f'<a href="?page={page.paginator.num_pages}">last &raquo;</a>'
f"</span>"
)
else:
next_output = ""

current_output = (
f'<span class="current">'
f"Page {page.number} of {page.paginator.num_pages}"
f"</span>"
)

return mark_safe(
f'<div class="pagination">'
f"{previous_output} {current_output} {next_output}"
"</div>",
)
45 changes: 45 additions & 0 deletions djpress/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import markdown
from django.contrib.auth.models import User
from django.utils.timezone import datetime

from djpress.conf import settings

Expand Down Expand Up @@ -35,3 +36,47 @@ def get_author_display_name(user: User) -> str:
return user.first_name

return user.username


def validate_date(year: str, month: str, day: str) -> None:
"""Test the date values.
Convert the date values to integers and test if they are valid dates.
The regex that gets the date values checks for the following:
- year: four digits
- month: two digits
- day: two digits
Args:
year (str): The year.
month (str | None): The month.
day (str | None): The day.
Raises:
ValueError: If the date is invalid.
Returns:
None
"""
int_year: int = int(year)
int_month: int | None = int(month) if month else None
int_day: int | None = int(day) if day else None

if int_month == 0 or int_day == 0:
msg = "Invalid date"
raise ValueError(msg)

try:
if int_month and int_day:
datetime(int_year, int_month, int_day)

elif int_month:
datetime(int_year, int_month, 1)

else:
datetime(int_year, 1, 1)

except ValueError as exc:
msg = "Invalid date"
raise ValueError(msg) from exc
Loading

0 comments on commit 8dbde10

Please sign in to comment.