diff --git a/docs/templatetags.md b/docs/templatetags.md
index 9510e87..a3b9b0f 100644
--- a/docs/templatetags.md
+++ b/docs/templatetags.md
@@ -452,12 +452,14 @@ Outputs:
## post_title_link
-Returns the title of the current post as a link if it's part of a collection, or just the title if it's a
-single post.
+Returns the title of the current post as a link if it's part of a collection, or just the title if it's single post.
+
+The `force_link` argument can be used to always return the link, regardless if it's part of a collection or not.
### Arguments
- `link_class` (optional): CSS class(es) to apply to the link.
+- `force_link` (optional, boolean): Always displays a link when true. *Note* - this is a keyword-only argument.
### Returns
@@ -493,6 +495,20 @@ Outputs:
My Post Title
```
+Or, to force the link, even when just a single post is displayed:
+
+```django
+{% post_title_link force_link=True %}
+```
+
+Outputs:
+
+```html
+My Post Title
+```
+
+
+
## post_author
Returns the display name of the post's author.
diff --git a/pyproject.toml b/pyproject.toml
index b9a5129..046fc02 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "djpress"
-version = "0.9.4"
+version = "0.9.5"
description = "A blog application for Django sites, inspired by classic WordPress."
readme = "README.md"
requires-python = ">=3.10"
diff --git a/src/djpress/templatetags/djpress_tags.py b/src/djpress/templatetags/djpress_tags.py
index 4c3998c..29d686d 100644
--- a/src/djpress/templatetags/djpress_tags.py
+++ b/src/djpress/templatetags/djpress_tags.py
@@ -256,6 +256,21 @@ def have_posts(context: Context) -> list[Post | None] | Page:
return []
+@register.simple_tag(takes_context=True)
+def get_recent_posts(context: Context) -> models.QuerySet[Post]:
+ """Return the recent posts.
+
+ This returns the most recent published posts, and tries to be efficient by checking if there's a `posts` object we
+ can use.
+ """
+ posts: Page | None = context.get("posts")
+
+ if isinstance(posts, Page) and posts.number == 1:
+ return posts.object_list
+
+ return Post.post_objects.get_recent_published_posts()
+
+
@register.simple_tag(takes_context=True)
def post_title(context: Context) -> str:
"""Return the title of a post.
@@ -274,19 +289,19 @@ def post_title(context: Context) -> str:
@register.simple_tag(takes_context=True)
-def post_title_link(context: Context, link_class: str = "") -> str:
+def post_title_link(context: Context, link_class: str = "", *, force_link: bool = False) -> str:
"""Return the title link for a post.
- If the post is part of a posts collection, then return the title and a link to the
- post.
-
- If the post is a single post, then return just the title of the post with no link.
+ If the post is part of a posts collection, then return the title and a link to the post. If the post is a single
+ post, then return just the title of the post with no link. But this behavior can be overridden by setting
+ `force_link` to `True`.
Otherwise return and empty string.
Args:
context: The context.
link_class: The CSS class(es) for the link.
+ force_link: Whether to force the link to be displayed.
Returns:
str: The title link for the post.
@@ -294,7 +309,7 @@ def post_title_link(context: Context, link_class: str = "") -> str:
post: Post | None = context.get("post")
posts: Page | None = context.get("posts")
- if posts and post:
+ if (posts and post) or force_link:
link_class_html = f' class="{link_class}"' if link_class else ""
output = f'{post.title}'
diff --git a/tests/test_templatetags_djpress_tags.py b/tests/test_templatetags_djpress_tags.py
index 56315a9..1cb420b 100644
--- a/tests/test_templatetags_djpress_tags.py
+++ b/tests/test_templatetags_djpress_tags.py
@@ -106,6 +106,22 @@ def test_post_title_link_no_context():
assert djpress_tags.post_title_link(context) == expected_output
+@pytest.mark.django_db
+def test_post_title_link_single_post(test_post1):
+ context = Context({"post": test_post1})
+ assert djpress_tags.post_title_link(context) == test_post1.title
+
+
+@pytest.mark.django_db
+def test_post_title_link_single_post_force_link(test_post1):
+ context = Context({"post": test_post1})
+
+ # this generates a URL based on the slug only - this is prefixed with the POST_PREFIX setting
+ post_url = test_post1.url
+ expected_output = f'{test_post1.title}'
+ assert djpress_tags.post_title_link(context, force_link=True) == expected_output
+
+
@pytest.mark.django_db
def test_post_title_link_with_prefix(settings, test_post1):
# Confirm settings in settings_testing.py
@@ -1443,3 +1459,41 @@ def test_rss_url(settings):
assert settings.DJPRESS_SETTINGS["RSS_PATH"] == "test-rss"
expected_output = "/test-rss/"
assert djpress_tags.rss_url() == expected_output
+
+
+@pytest.mark.django_db
+def test_get_recent_posts(settings, test_post1, test_post2, test_post3):
+ assert settings.DJPRESS_SETTINGS["RECENT_PUBLISHED_POSTS_COUNT"] == 3
+ posts = Paginator(
+ Post.post_objects.get_published_posts(),
+ settings.DJPRESS_SETTINGS["RECENT_PUBLISHED_POSTS_COUNT"],
+ )
+ page = posts.get_page(number=None)
+ context = Context({"posts": page})
+ tag_get_recent_posts = list(djpress_tags.get_recent_posts(context))
+ page_posts = list(page.object_list)
+ assert tag_get_recent_posts == page_posts
+
+ # Test case 2 - 2 pages, i.e. 3 posts with 2 posts per page
+ settings.DJPRESS_SETTINGS["RECENT_PUBLISHED_POSTS_COUNT"] = 2
+ posts = Paginator(
+ Post.post_objects.get_published_posts(),
+ settings.DJPRESS_SETTINGS["RECENT_PUBLISHED_POSTS_COUNT"],
+ )
+ page = posts.get_page(number=2)
+ context = Context({"posts": page})
+ tag_get_recent_posts = list(djpress_tags.get_recent_posts(context))
+ page_posts = list(page.object_list)
+ assert tag_get_recent_posts != page_posts
+
+ # Test case 3 - 3 pages, i.e. 3 posts with 1 posts per page
+ settings.DJPRESS_SETTINGS["RECENT_PUBLISHED_POSTS_COUNT"] = 1
+ posts = Paginator(
+ Post.post_objects.get_published_posts(),
+ settings.DJPRESS_SETTINGS["RECENT_PUBLISHED_POSTS_COUNT"],
+ )
+ page = posts.get_page(number=3)
+ context = Context({"posts": page})
+ tag_get_recent_posts = list(djpress_tags.get_recent_posts(context))
+ page_posts = list(page.object_list)
+ assert tag_get_recent_posts != page_posts
diff --git a/uv.lock b/uv.lock
index 39cb68e..07fe660 100644
--- a/uv.lock
+++ b/uv.lock
@@ -145,7 +145,7 @@ wheels = [
[[package]]
name = "djpress"
-version = "0.9.4"
+version = "0.9.5"
source = { editable = "." }
dependencies = [
{ name = "django" },