Skip to main content

Add and display htmx comments to arbitrary Django models.

Project description

django-add-comments

Add comments to a Django model via mixin:

# app/models.py
from comment.models import AbstractCommentable # import mixin
class Sentinel(AbstractCommentable): # add to class declaration
    """Any `app`, e.g. `essay`, `article`... can be 'commentable'."""
    title = models.CharField(max_length=50)
Action Authorization Description
View comments list All users Add filter public/private later
Get comment form Authenticated users Reactive via htmx / hyperscript [^1]
Delete / edit comment Authorized authors Reactive via htmx / hyperscript [^1]

Setup

Load virtual env

.venv> poetry add django-add-comments # pip3 install django-add-comments

Will include dependencies from pyproject.toml:

python = "^3.8"
Django = "^4.0"
django-extensions = "^3.1.5"
django-crispy-forms = "^1.13.0"

Add app to project settings

# in project_folder/settings.py
INSTALLED_APPS = [
    ...,
    'crispy_forms',  # add crispy_forms at least > v1.13, if not yet added
    'comments' # this is the new django-comments folder
]

Add basic routes to urlpatterns

# in project_folder/urls.py
from django.urls import path, include # new
urlpatterns = [
    ...,
    path('comments/', include('comments.urls')) # routes for update, delete, view, toggle comment
]

Add Comment model to database

.venv> python manage.py migrate

Configuration

What we're going to do

>>> obj = Sentinel.objects.create(title="A sample title") # instance is made, e.g. id=1, id=2, etc.
>>> obj.add_comment_url # url to add a comment to `A sample title`

A sentinel is the model being commented on.

We've created a dummy Sentinel model to represent this construct.

Let's say we've initialized one model instance called obj with slug="a-sample-title".

What we'd like is the ability to write a comment to obj through a url represented by: obj.add_comment_url

@add_comment_url thus needs to become a property of the Sentinel model.

Add imports

# sentinels/models.py
from comments.models import AbstractCommentable # new
from django.template.response import TemplateResponse # new
from django.urls import reverse, URLPattern # new
from django.utils.functional import cached_property, classproperty # new

Make sentinel model inherit from abstract base model

# sentinels/models.py
class Sentinel(AbstractCommentable): # new
    ...

Add model properties

# sentinels/models.py
class Sentinel(AbstractCommentable):

    id = models.UUIDField ... # identifier is UUID
    slug = models.Slugfield ...

    @cached_property # copy this to the sentinel model, note `slug` as identifier
    def add_comment_url(self) -> str:
        return self.set_add_comment_url(self.slug)

    @classmethod # copy this to the sentinel model, note `slug` as identifier
    def add_comment_func(cls, request, slug: str) -> TemplateResponse:
        target = cls.objects.get(slug=slug)
        return cls.allow_commenting_form_on_target_instance(request, target)

    @classproperty # copy this to the sentinel model, note `slug` as identifier
    def add_comment_path(cls) -> URLPattern:
        return cls.set_add_comment_path("<slug:slug>", cls.add_comment_func)

Gotcha: if pk is identifier, revise <slug:slug> to <pk:int> above:

  1. self.set_add_comment_url(self.pk)
  2. def add_comment_func(cls, request, pk: int):
  3. target = cls.objects.get(pk=pk)
  4. cls.set_add_comment_path("<int:pk>", cls.add_comment_func)

Add sentinel namespaced url for adding comments

Add path to the sentinel's url patterns:

# sentinels/urls.py
from .models import Sentinel
from .apps import SentinelConfig # already pre-made during `python manage.py startapp sentinels`
app_name = SentinelConfig.name # remember the `app_name` in relation to the `add_comment_url` property
url_patterns = [
    Sentinel.add_comment_path, # This is really just a shortcut to a created path.
    ...
]

Add template tag for displaying comment form with list of added comments

Add template tag to sentinel's template to show form with list

<!-- sentinels/templates/sentinel_detail.html -->
<h1>Title: {{ object.title }}</h1>
{% load comments %} <!-- see templatetags/comments.py which contains `object.add_comment_url`  -->
{% list_comments sentinel_target_obj=object head_label='Add an Answer'%}
<!-- the `object` is whatever variable passed to the template, the head_label provides an ability to label the items to be inputted. The default is 'Add a Comment' -->

The form that represents this "add comment" action / url will be loaded in every comment list. See context in template tag.

[^1]: No page refresh

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-add-comments-0.0.7.tar.gz (47.8 kB view hashes)

Uploaded Source

Built Distribution

django_add_comments-0.0.7-py3-none-any.whl (50.3 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page