2
\$\begingroup\$

I have recently finished a "django for beginners" book that ended with creating a small articles site. The last chapter adds a Comment model which is linked to Article model. However the comments are just created in the admin page. As a continuation of the book I wanted to test what I have learned and add the ability to generate comments from the site itself. I have written the below code and it works, but I just wanted to know if this is the best way to do it. Or if there may be a better approach or more builtin way of Django to do this.

In summary in the html for article list I added a link to add a new comment. The html for adding a comment doesn't give the user the ability to set the author (userid) or the article which they are commenting on. It only gives the field for the comment. This link uses the article pk in the url. Then in the view I get the user from the request and then fetch the article by the PK and set it as part of the form data.
Is this a reasonable approach or is there another way to do this?

URLS

from django.urls import path

from .views import (
    ArticleListView,
    ArticleUpdateView,
    ArticleDeleteView,
    ArticleDetailView,
    ArticleCreateView,
    CommentCreateView
)

urlpatterns = [
    path('<int:article_pk>/comment', CommentCreateView.as_view(), name='comment_new'),
    path('new', ArticleCreateView.as_view(), name='article_new'),
    path('<int:pk>/edit', ArticleUpdateView.as_view(), name='article_edit'),
    path('<int:pk>/delete', ArticleDeleteView.as_view(), name='article_delete'),
    path('<int:pk>', ArticleDetailView.as_view(), name='article_detail'),
    path('', ArticleListView.as_view(), name='article_list')
]

Models

from django.db import models
from django.contrib.auth import get_user_model
from django.urls import reverse


class Article(models.Model):
    title = models.CharField(max_length=255)
    body = models.TextField()
    date = models.DateTimeField(auto_now_add=True)
    author = models.ForeignKey(
        get_user_model(),
        on_delete=models.CASCADE
    )

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('article_detail', args=[str(self.id)])


class Comment(models.Model):
    comment = models.CharField(max_length=140)
    author = models.ForeignKey(
        get_user_model(),
        on_delete=models.CASCADE
    )
    article = models.ForeignKey(
        Article,
        on_delete=models.CASCADE,
        related_name='comments'
    )

    def __str__(self):
        return self.comment

    def get_absolute_url(self):
        return reverse('article_detail', args=[str(self.article.pk)])

VIEWS

from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.urls import reverse_lazy
from django.views.generic import ListView, DeleteView, UpdateView, DetailView, CreateView
from .models import Article, Comment


# Create your views here.
class ArticleListView(LoginRequiredMixin, ListView):
    model = Article
    template_name = 'article_list.html'
    context_object_name = 'articles'


class ArticleDetailView(LoginRequiredMixin, DetailView):
    model = Article
    template_name = 'article_detail.html'
    context_object_name = 'article'


class ArticleUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
    model = Article
    fields = ('title', 'body')
    template_name = 'article_edit.html'
    context_object_name = 'article'

    def test_func(self):
        article = self.get_object()
        return article.author == self.request.user


class ArticleDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
    model = Article
    template_name = 'article_delete.html'
    success_url = reverse_lazy('article_list')
    context_object_name = 'article'

    def test_func(self):
        article = self.get_object()
        return article.author == self.request.user


class ArticleCreateView(LoginRequiredMixin, CreateView):
    model = Article
    template_name = 'article_new.html'
    fields = ('title', 'body')

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)


class CommentCreateView(LoginRequiredMixin, CreateView):
    model = Comment
    template_name = 'comment_new.html'
    fields = ('comment',)

    def form_valid(self, form):
        form.instance.author = self.request.user
        form.instance.article = Article.objects.get(pk=self.kwargs['article_pk'])
        return super().form_valid(form)

Article HTML

{% extends 'base.html' %}

{% block title %}Article List{% endblock title %}

{% block content %}
    {% for article in articles %}
        <div class="card">
            <div class="card-header">
                <span class="font-weight-bold"><a
                        href="{% url 'article_detail' article.pk %}">{{ article.title }}</a></span>
                <span class="text-muted">by {{ article.author }} | {{ article.date }}</span>
            </div>
            <div class="card-body">
                {{ article.body }}
                <br />
                <a href="{% url 'article_edit' article.pk %}">Edit</a> | <a
                    href="{% url 'article_delete' article.pk %}">Delete</a>
            </div>
            <div class="card-footer">
                {% for comment in article.comments.all %}
                    <p>
                        <span class="font-weight-bold">{{ comment.author }} &middot;</span>
                        {{ comment }}
                    </p>
                {% endfor %}
                <a href="{% url 'comment_new' article.pk%}">+ New Comment</a>
            </div>
        </div>
        <br>
    {% endfor %}
{% endblock content %}

Comment HTML

{% extends 'base.html' %}
{% load crispy_forms_filters %}
{% block title %}Add Comment{% endblock title %}

{% block content %}
    <h1>Add comment</h1>
    <form method="post">
        {% csrf_token %}
        {{ form|crispy }}
        <button class="btn btn-info" type="submit">Add Comment</button>
    </form>
{% endblock content %}

Below are two screen shots just showing the html rendering

Screenshots enter image description here enter image description here

\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.