Skip to content

Commit

Permalink
Merge pull request #22 from stuartmaxwell/dev
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartmaxwell authored May 1, 2024
2 parents 5c15faf + 1e53d40 commit a1f948c
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 69 deletions.
23 changes: 22 additions & 1 deletion config/settings_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
],
},
},
]
42 changes: 34 additions & 8 deletions djpress/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand All @@ -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)

Expand All @@ -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."""
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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:
Expand Down
23 changes: 13 additions & 10 deletions djpress/templates/djpress/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@ <h1><a href="{% url "djpress:index" %}">{% get_blog_title %}</a></h1>

<main>

{% 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 %}

<article>
<header>
Expand All @@ -38,10 +36,15 @@ <h1>{{ post.title }}</h1>
</footer>
</article>

{% else %}
{% comment %} If there's no slug, we loop through recent published content. {% endcomment %}
{% comment %}If we get multiple posts, then show the truncated content.{% endcomment %}
{% elif posts %}

{% comment %}If we get posts for a category, then we can show the category header.{% endcomment %}
{% if category %}

<h1>{{ category.name }}</h1>

{% get_recent_published_content as posts %}
{% endif %}

{% for post in posts %}

Expand All @@ -68,11 +71,11 @@ <h1><a href="{% url 'djpress:content_detail' post.slug %}">{{ post.title }}</a><
</footer>
</article>

{% empty %}
{% endfor %}

<p>No posts available.</p>
{% else %}

{% endfor %}
<p>No posts available.</p>

{% endif %}

Expand Down
12 changes: 6 additions & 6 deletions djpress/test_category_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)


Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down
25 changes: 19 additions & 6 deletions djpress/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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},
)
3 changes: 2 additions & 1 deletion templates/base.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{% load static %}
{% load djpress_tags %}

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}stuartm.nz{% endblock %}</title>
<title>{% spaceless %}{% block title %}{% get_blog_title %}{% endblock %}{% endspaceless %}</title>
<link rel="alternate" type="application/rss+xml" title="Latest Posts" href="{% url 'djpress:rss_feed' %}">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="{% static "css/pygments/nord.min.css" %}" rel="stylesheet">
Expand Down
14 changes: 0 additions & 14 deletions templates/djpress/category_posts.html

This file was deleted.

38 changes: 26 additions & 12 deletions templates/djpress/index.html
Original file line number Diff line number Diff line change
@@ -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 %}

<h1>The posts go here.</h1>

{% for post in posts %}

{% include "djpress/snippets/content_summary.html" %}

{% empty %}
{% endfor %}

<p>No posts available.</p>
{% else %}

{% endfor %}
<p>No posts available.</p>

{% endif %}

{% endblock %}
{% endblock content %}
11 changes: 0 additions & 11 deletions templates/djpress/post.html

This file was deleted.

0 comments on commit a1f948c

Please sign in to comment.