Day 6 Dev Notes

This morning, I wanted to implement a simple, category-based, menu in the navbar. As with most things, this was simple to implement, but I then spent some time optimising it, which lead me down an interesting pathway. Here's what I accomplished...

@register.simple_tag
def get_categories() -> models.QuerySet[Category] | None:
    """Return all categories."""
    return Category.get_categories()
      {% get_categories as categories %}

      {% for category in categories %}
        <li class="nav-item">
          <a class="nav-link" href="{% url 'djpress:category_posts' category.slug %}">{{ category.name }}</a>
        </li>
      {% endfor %}
CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
    },
}
CATEGORY_CACHE_KEY = "categories"

...

    @classmethod
    def _get_categories(cls: type["Category"]) -> models.QuerySet:
        """Return all categories."""
        return cls.objects.all()

    @classmethod
    def get_cached_queryset(cls: type["Category"]) -> models.QuerySet:
        """Return the cached categories queryset."""
        queryset = cache.get(CATEGORY_CACHE_KEY)
        if queryset is None:
            queryset = cls._get_categories()
            cache.set(CATEGORY_CACHE_KEY, queryset, timeout=None)
        return queryset
@receiver(post_save, sender=Category)
@receiver(post_delete, sender=Category)
def invalidate_category_cache(**kwargs) -> None:  # noqa: ARG001, ANN003
    """Invalidate the category cache."""
    cache.delete(CATEGORY_CACHE_KEY)
    def ready(self: "DjpressConfig") -> None:
        """Import signals."""
        import djpress.signals  # noqa: F401
    def save(self: "Category", *args, **kwargs) -> None:  # noqa: ANN002, ANN003
        """Override the save method to auto-generate the slug."""
        if not self.slug:
            self.slug = slugify(self.name)
            if not self.slug or self.slug.strip("-") == "":
                msg = "Invalid name. Unable to generate a valid slug."
                raise ValueError(msg)
        super().save(*args, **kwargs)

Now that I have this basic caching framework in place, the next step will be to introducing more caching, especially for the Content model.

Github link to changed files

Update

Did some more work today, which started with the aim of doing some caching of posts. This spiralled out of control a bit and brought about a lot of refactoring. But the end result was that I'm now caching the published posts queryset which means that once cached, I can view the home page and click through to a single post without touching the database.

Github Link