From b0acbf05974d4a5166b28cff701b895f0c3b1877 Mon Sep 17 00:00:00 2001 From: Stuart Maxwell Date: Wed, 1 May 2024 18:16:02 +1200 Subject: [PATCH 1/4] Category and markdown improvements --- djpress/models.py | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/djpress/models.py b/djpress/models.py index cff9592..ebcd335 100644 --- a/djpress/models.py +++ b/djpress/models.py @@ -23,6 +23,9 @@ CATEGORY_CACHE_KEY = "categories" PUBLISHED_CONTENT_CACHE_KEY = "published_content" +markdown_extensions = ["fenced_code", "codehilite"] +md = markdown.Markdown(extensions=markdown_extensions, output_format="html") + class CategoryManager(models.Manager): """Category manager.""" @@ -33,11 +36,11 @@ def get_categories(self: "CategoryManager") -> models.QuerySet: If CACHE_CATEGORIES is set to True, we return the cached queryset. """ if CACHE_CATEGORIES: - return self.get_cached_categories() + return self._get_cached_categories() return Category.objects.all() - def get_cached_categories(self: "CategoryManager") -> models.QuerySet: + def _get_cached_categories(self: "CategoryManager") -> models.QuerySet: """Return the cached categories queryset.""" queryset = cache.get(CATEGORY_CACHE_KEY) @@ -47,6 +50,25 @@ def get_cached_categories(self: "CategoryManager") -> models.QuerySet: return queryset + def get_category_by_slug(self: "CategoryManager", slug: str) -> "Category": + """Return a single category by its slug.""" + # First, try to get the category from the cache + categories = self.get_categories() + category = next( + (category for category in categories if category.slug == slug), None, + ) + + # If the category is not found in the cache, fetch it from the database + if not category: + try: + category = Category.objects.get(slug=slug) + except Category.DoesNotExist as exc: + msg = "Category not found" + # Raise a 404 error + raise Http404(msg) from exc + + return category + class Category(models.Model): """Category model.""" @@ -180,7 +202,11 @@ def get_published_content_by_category( Must have a date less than or equal to the current date/time for a specific category, ordered by date in descending order. """ - return self._get_published_content().filter(categories=category) + return ( + self._get_published_content() + .filter(categories=category) + .prefetch_related("categories", "author") + ) class Content(models.Model): @@ -222,11 +248,11 @@ def save(self: "Content", *args, **kwargs) -> None: # noqa: ANN002, ANN003 def render_markdown(self: "Content", markdown_text: str) -> str: """Return the markdown text as HTML.""" - return markdown.markdown( - markdown_text, - extensions=["fenced_code", "codehilite"], - output_format="html", - ) + html = md.convert(markdown_text) + logger.debug(f"Converted markdown to HTML: {html=}") + md.reset() + + return html @property def content_markdown(self: "Content") -> str: From bdd026c44083163ef96534f8757083500b953c72 Mon Sep 17 00:00:00 2001 From: Stuart Maxwell Date: Wed, 1 May 2024 18:16:49 +1200 Subject: [PATCH 2/4] Change views to return context --- djpress/views.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/djpress/views.py b/djpress/views.py index fcd0b43..8c6d282 100644 --- a/djpress/views.py +++ b/djpress/views.py @@ -8,25 +8,38 @@ def index(request: HttpRequest) -> HttpResponse: """View for the index page.""" - return render(request, "djpress/index.html") + posts = Content.post_objects.get_recent_published_content() + + return render( + request, + "djpress/index.html", + {"posts": posts}, + ) def content_detail(request: HttpRequest, slug: str) -> HttpResponse: """View for a single content page.""" - return render(request, "djpress/index.html", {"slug": slug}) + post = Content.post_objects.get_published_post_by_slug(slug) + + return render( + request, + "djpress/index.html", + {"post": post}, + ) def category_posts(request: HttpRequest, slug: str) -> HttpResponse: """View for posts by category.""" try: - category = Category.objects.get(slug=slug) - posts = Content.post_objects.get_published_content_by_category(category) + category: Category = Category.objects.get_category_by_slug(slug=slug) except Category.DoesNotExist as exc: msg = "Category not found" raise Http404(msg) from exc + posts = Content.post_objects.get_published_content_by_category(category) + return render( request, - "djpress/category_posts.html", - {"category": category, "posts": posts}, + "djpress/index.html", + {"posts": posts, "category": category}, ) From 699887af1635097b0c101f5bf4b5225c6f04701f Mon Sep 17 00:00:00 2001 From: Stuart Maxwell Date: Wed, 1 May 2024 18:17:14 +1200 Subject: [PATCH 3/4] Basic template improvments --- djpress/templates/djpress/index.html | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/djpress/templates/djpress/index.html b/djpress/templates/djpress/index.html index 56650d8..cd65dd2 100644 --- a/djpress/templates/djpress/index.html +++ b/djpress/templates/djpress/index.html @@ -14,10 +14,8 @@

{% get_blog_title %}

- {% if slug %} - {% comment %} If there's a slug, we only display a single post. {% endcomment %} - - {% get_single_published_content slug as post %} + {% comment %}If we get a single post, then show the full content.{% endcomment %} + {% if post %}
@@ -38,10 +36,17 @@

{{ post.title }}

- {% else %} - {% comment %} If there's no slug, we loop through recent published content. {% endcomment %} + {% endif %} + + {% comment %}If we get multiple posts, then show the truncated content.{% endcomment %} + {% if posts %} + + {% comment %}If we get posts for a category, then we can show the category header.{% endcomment %} + {% if category %} + +

{{ category.name }}

- {% get_recent_published_content as posts %} + {% endif %} {% for post in posts %} From 1e53d40daf5f6fc62efdc384ee6631e65ee8686a Mon Sep 17 00:00:00 2001 From: Stuart Maxwell Date: Wed, 1 May 2024 19:51:02 +1200 Subject: [PATCH 4/4] Template updates --- config/settings_testing.py | 23 +++++++++++++++- djpress/templates/djpress/index.html | 10 +++---- djpress/test_category_cache.py | 12 ++++----- templates/base.html | 3 ++- templates/djpress/category_posts.html | 14 ---------- templates/djpress/index.html | 38 ++++++++++++++++++--------- templates/djpress/post.html | 11 -------- 7 files changed, 60 insertions(+), 51 deletions(-) delete mode 100644 templates/djpress/category_posts.html delete mode 100644 templates/djpress/post.html diff --git a/config/settings_testing.py b/config/settings_testing.py index 734a26b..5756be0 100644 --- a/config/settings_testing.py +++ b/config/settings_testing.py @@ -3,4 +3,25 @@ from .settings import * # noqa: F403, F401, RUF100 # Use an in-memory database for testing -DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}} +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": ":memory:", + }, +} + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + }, + }, +] diff --git a/djpress/templates/djpress/index.html b/djpress/templates/djpress/index.html index cd65dd2..d9f2070 100644 --- a/djpress/templates/djpress/index.html +++ b/djpress/templates/djpress/index.html @@ -36,10 +36,8 @@

{{ post.title }}

- {% endif %} - {% comment %}If we get multiple posts, then show the truncated content.{% endcomment %} - {% if posts %} + {% elif posts %} {% comment %}If we get posts for a category, then we can show the category header.{% endcomment %} {% if category %} @@ -73,11 +71,11 @@

{{ post.title }}< - {% empty %} + {% endfor %} -

No posts available.

+ {% else %} - {% endfor %} +

No posts available.

{% endif %} diff --git a/djpress/test_category_cache.py b/djpress/test_category_cache.py index 8256c9b..1da7fbe 100644 --- a/djpress/test_category_cache.py +++ b/djpress/test_category_cache.py @@ -17,7 +17,7 @@ def test_get_cached_categories(): Category.objects.create(name="Category 2") # Call the get_cached_categories method - queryset = Category.objects.get_cached_categories() + queryset = Category.objects._get_cached_categories() # Assert that the queryset is cached cached_queryset = cache.get(CATEGORY_CACHE_KEY) @@ -26,7 +26,7 @@ def test_get_cached_categories(): assert len(cached_queryset) == 2 # Assert that subsequent calls retrieve the queryset from cache - queryset2 = Category.objects.get_cached_categories() + queryset2 = Category.objects._get_cached_categories() assert list(queryset2) == list(cached_queryset) @@ -36,7 +36,7 @@ def test_cache_invalidation_on_save(): category = Category.objects.create(name="Category 1") # Call the get_cached_categories method - queryset = Category.objects.get_cached_categories() + queryset = Category.objects._get_cached_categories() # Assert that the queryset is cached cached_queryset = cache.get(CATEGORY_CACHE_KEY) @@ -52,7 +52,7 @@ def test_cache_invalidation_on_save(): assert cached_queryset is None # Call the get_cached_categories method again - queryset2 = Category.objects.get_cached_categories() + queryset2 = Category.objects._get_cached_categories() # Assert that the queryset is cached again with the updated data cached_queryset2 = cache.get(CATEGORY_CACHE_KEY) @@ -67,7 +67,7 @@ def test_cache_invalidation_on_delete(): category = Category.objects.create(name="Category 1") # Call the get_cached_categories method - queryset = Category.objects.get_cached_categories() + queryset = Category.objects._get_cached_categories() # Assert that the queryset is cached cached_queryset = cache.get(CATEGORY_CACHE_KEY) @@ -82,7 +82,7 @@ def test_cache_invalidation_on_delete(): assert cached_queryset is None # Call the get_cached_categories method again - queryset2 = Category.objects.get_cached_categories() + queryset2 = Category.objects._get_cached_categories() # Assert that the queryset is cached again with the updated data cached_queryset2 = cache.get(CATEGORY_CACHE_KEY) diff --git a/templates/base.html b/templates/base.html index eee46c7..4e8afec 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,11 +1,12 @@ {% load static %} +{% load djpress_tags %} - {% block title %}stuartm.nz{% endblock %} + {% spaceless %}{% block title %}{% get_blog_title %}{% endblock %}{% endspaceless %} diff --git a/templates/djpress/category_posts.html b/templates/djpress/category_posts.html deleted file mode 100644 index 7767136..0000000 --- a/templates/djpress/category_posts.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends 'base.html' %} - -{% block title %}{{ category.name }}{% endblock %} - -{% block content %} -

Category: {{ category.name }}

-

{{ category.description }}

-
- {% for post in posts %} - {% include "djpress/snippets/content_summary.html" %} - {% empty %} -

No posts available in this category.

- {% endfor %} -{% endblock %} diff --git a/templates/djpress/index.html b/templates/djpress/index.html index 71e7a57..a45b352 100644 --- a/templates/djpress/index.html +++ b/templates/djpress/index.html @@ -1,32 +1,46 @@ {% extends 'base.html' %} {% load djpress_tags %} -{% block title %}Home{% endblock %} -{% block content %} +{% block title %} - {% if slug %} - {% comment %} If there's a slug, we only display a single post. {% endcomment %} + {% if post %} - {% get_single_published_content slug as post %} + {{ post.title }} - {% get_blog_title %} - {% include "djpress/snippets/content_detail.html" %} + {% elif category %} + + {{ category.name }} - {% get_blog_title %} {% else %} - {% comment %} If there's no slug, we loop through recent published content. {% endcomment %} - {% get_recent_published_content as posts %} + {% get_blog_title %} + + {% endif %} + +{% endblock title %} + + +{% block content %} + + {% if post %} + + {% include "djpress/snippets/content_detail.html" %} + + {% elif posts %} + +

The posts go here.

{% for post in posts %} {% include "djpress/snippets/content_summary.html" %} - {% empty %} + {% endfor %} -

No posts available.

+ {% else %} - {% endfor %} +

No posts available.

{% endif %} -{% endblock %} +{% endblock content %} diff --git a/templates/djpress/post.html b/templates/djpress/post.html deleted file mode 100644 index 8e11726..0000000 --- a/templates/djpress/post.html +++ /dev/null @@ -1,11 +0,0 @@ -{% extends 'base.html' %} - -{% block title %}{{ post.title }}{% endblock %} - -{% block content %} - - {% include "djpress/snippets/content_detail.html" %} - -
- Back to Home -{% endblock %}