Add basis for a comment system

This commit is contained in:
Gabriel Augendre 2020-08-18 18:49:41 +02:00
parent fb2eae301a
commit 698a5ca30f
12 changed files with 233 additions and 11 deletions

View file

@ -5,7 +5,7 @@ from django.contrib.auth.admin import UserAdmin
from django.db import models
from django.shortcuts import redirect
from .models import Article, Page, User
from .models import Article, Comment, Page, User
admin.site.register(User, UserAdmin)
@ -108,3 +108,17 @@ class PageAdmin(ArticleAdmin):
),
("Content", {"fields": ("content",)}),
]
@register(Comment)
class CommentAdmin(admin.ModelAdmin):
list_display = ("username", "email", "content", "article", "created_at", "approved")
list_filter = ("approved",)
search_fields = ("username", "email", "content")
actions = ["approve_comments", "censor_comments"]
def approve_comments(self, request, queryset):
queryset.update(approved=True)
def censor_comments(self, request, queryset):
queryset.update(approved=False)

9
articles/forms.py Normal file
View file

@ -0,0 +1,9 @@
from django import forms
from articles.models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ["username", "email", "content"]

View file

@ -0,0 +1,59 @@
# Generated by Django 3.1 on 2020-08-18 16:05
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("articles", "0008_auto_20200817_1748"),
]
operations = [
migrations.CreateModel(
name="Comment",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"username",
models.CharField(
help_text="Will be displayed with your comment.", max_length=255
),
),
(
"email",
models.EmailField(
blank=True,
help_text="Not mandatory, fill only if you want me to be able to contact you. Will never be displayed here nor shared with any third party.",
max_length=254,
null=True,
),
),
(
"content",
models.TextField(
help_text="Your comment, limited to 500 characters.",
max_length=500,
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
(
"article",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="comments",
to="articles.article",
),
),
],
),
]

View file

@ -0,0 +1,21 @@
# Generated by Django 3.1 on 2020-08-18 16:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("articles", "0009_comment"),
]
operations = [
migrations.AlterModelOptions(
name="comment", options={"ordering": ["-created_at"]},
),
migrations.AddField(
model_name="comment",
name="active",
field=models.BooleanField(default=False),
),
]

View file

@ -0,0 +1,16 @@
# Generated by Django 3.1 on 2020-08-18 16:29
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("articles", "0010_auto_20200818_1825"),
]
operations = [
migrations.RenameField(
model_name="comment", old_name="active", new_name="approved",
),
]

View file

@ -0,0 +1,21 @@
# Generated by Django 3.1 on 2020-08-18 16:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("articles", "0011_auto_20200818_1829"),
]
operations = [
migrations.AlterModelOptions(
name="comment", options={"ordering": ["created_at"]},
),
migrations.AlterField(
model_name="comment",
name="approved",
field=models.BooleanField(default=True),
),
]

View file

@ -92,8 +92,26 @@ class Page(Article):
ordering = ["position", "-published_at"]
# class Comment(models.Model):
# username = models.CharField(max_length=255)
# email = models.EmailField(blank=True, null=True)
# content = models.TextField()
# article = models.ForeignKey(Article, on_delete=models.CASCADE)
class Comment(models.Model):
username = models.CharField(
max_length=255, help_text="Will be displayed with your comment."
)
email = models.EmailField(
blank=True,
null=True,
help_text=(
"Not mandatory, fill only if you want me to be able to contact you. "
"Will never be displayed here nor shared with any third party."
),
)
content = models.TextField(
max_length=500, help_text="Your comment, limited to 500 characters."
)
article = models.ForeignKey(
Article, on_delete=models.CASCADE, related_name="comments"
)
created_at = models.DateTimeField(auto_now_add=True)
approved = models.BooleanField(default=True)
class Meta:
ordering = ["created_at"]

View file

@ -75,6 +75,10 @@ a:hover, a:focus {
margin-bottom: .2em;
}
.article-detail h1 {
font-size: 2em;
}
.date {
margin-top: .2em;
color: var(--main2);

View file

@ -4,11 +4,34 @@
{{ article.title }}
{% endblock %}
{% block content %}
<div class="article-detail">
<article class="article-detail">
<h1>{{ article.title }}{% if article.status != article.PUBLISHED %} ({{ article.status }}){% endif %}</h1>
{% include "articles/metadata_snippet.html" %}
<div>
{{ article.get_formatted_content|safe }}
</div>
</div>
</article>
<section class="comments">
<h2>Comments</h2>
<form action="{% url 'create-comment' slug=article.slug %}" method="post">
{% csrf_token %}
{{ comment_form.as_p }}
<button type="submit">Submit</button>
</form>
{% for comment in comments %}
<article class="comment">
<p class="metadata">
{{ comment.username }} |
<time datetime="{{ comment.created_at|date:'c' }}">
{{ comment.created_at|date:"DATETIME_FORMAT" }}
</time>
</p>
<p class="content">
{{ comment.content }}
</p>
</article>
{% empty %}
No reaction yet, write your own!
{% endfor %}
</section>
{% endblock %}

View file

@ -28,6 +28,13 @@
{% endfor %}
{% endif %}
</nav>
{% if messages %}
<div class="messages">
{% for message in messages %}
<p>{{ message }}</p>
{% endfor %}
</div>
{% endif %}
<div class="content">
{% block content %}
{% endblock %}

View file

@ -1,7 +1,10 @@
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import F
from django.http import HttpResponseRedirect
from django.views import generic
from articles.forms import CommentForm
from articles.models import Article
@ -38,9 +41,19 @@ class ArticleDetailView(generic.DetailView):
template_name = "articles/article_detail.html"
def get_queryset(self):
queryset = super().get_queryset()
if self.request.user.is_authenticated:
return super().get_queryset()
return super().get_queryset().filter(status=Article.PUBLISHED)
return queryset
return queryset.filter(status=Article.PUBLISHED)
def get_context_data(self, **kwargs):
context = super(ArticleDetailView, self).get_context_data(**kwargs)
article = self.object
if hasattr(article, "article"):
article = article.article
context["comments"] = article.comments.filter(approved=True)
context["comment_form"] = CommentForm()
return context
def get_object(self, queryset=None):
obj = super().get_object(queryset)
@ -51,3 +64,16 @@ class ArticleDetailView(generic.DetailView):
obj.save(update_fields=["views_count"])
return obj
class CreateCommentView(generic.CreateView):
model = Article
form_class = CommentForm
def form_valid(self, form):
self.object = self.get_object()
self.comment = form.save(commit=False)
self.comment.article = self.object
self.comment.save()
messages.success(self.request, "Comment successfully saved.")
return HttpResponseRedirect(self.get_success_url())

View file

@ -22,6 +22,10 @@ urlpatterns = [
path("admin/", admin.site.urls),
path("", html.ArticlesListView.as_view(), name="articles-list"),
path("drafts/", html.DraftsListView.as_view(), name="drafts-list"),
path("<slug:slug>", html.ArticleDetailView.as_view(), name="article-detail"),
path("feed/", feeds.CompleteFeed(), name="complete-feed"),
path("<slug:slug>", html.ArticleDetailView.as_view(), name="article-detail"),
path("<slug:slug>/", html.ArticleDetailView.as_view(), name="article-detail"),
path(
"<slug:slug>/comment/", html.CreateCommentView.as_view(), name="create-comment"
),
]