Refactorize to enable code sharing for comments

This commit is contained in:
Guillaume Bertholon 2020-12-27 18:01:37 +01:00
parent 98c517421c
commit d4a9faef3e
14 changed files with 158 additions and 117 deletions

0
comments/__init__.py Normal file
View file

5
comments/admin.py Normal file
View file

@ -0,0 +1,5 @@
from django.contrib import admin
class CommentAdmin(admin.ModelAdmin):
list_display = ("created_on", "author", "commented_object", "text")

5
comments/apps.py Normal file
View file

@ -0,0 +1,5 @@
from django.apps import AppConfig
class CommentsConfig(AppConfig):
name = 'comments'

View file

23
comments/models.py Normal file
View file

@ -0,0 +1,23 @@
from django.db import models
from accounts.models import User
class AbstractComment(models.Model):
commented_object = None # Fill this with a foreign key in subclasses
author = models.ForeignKey(
User, on_delete=models.CASCADE, verbose_name="auteur·ice"
)
text = models.TextField(verbose_name="texte")
created_on = models.DateTimeField(
auto_now_add=True, verbose_name="date de publication"
)
modified_on = models.DateTimeField(
auto_now=True, verbose_name="date de modification"
)
class Meta:
abstract = True
ordering = ["created_on"]
def __str__(self):
return "({}) {}: {}".format(self.commented_object, self.author, self.text)

View file

@ -0,0 +1,39 @@
{% for comment in comments %}
{% if comment == edited_comment %}
<form id="edited_comment" class="comment" method="post">
<div class="meta">
<span class="author">{{ comment.author }}</span>
<span class="date">posté le {{ comment.created_on|date }} à {{ comment.created_on|time }}</span>
<a href="{{ comment.commented_object.get_absolute_url }}" title="Annuler la modification"><i class="fa fa-ban" aria-hidden="true"></i></a>
</div>
{% csrf_token %}
<textarea name="comment_text" required>{{ comment.text }}</textarea>
<button type="submit"><i class="fa fa-pencil" aria-hidden="true"></i> Modifier mon commentaire</button>
</form>
{% else %}
<div class="comment">
<div class="meta">
<span class="author">{{ comment.author }}</span>
<span class="date">posté le {{ comment.created_on|date }} à {{ comment.created_on|time }}{% if comment.created_on|date:"YmdHi" != comment.modified_on|date:"YmdHi" %}, dernière modification le {{ comment.modified_on|date }} à {{ comment.modified_on|time }}{% endif %}</span>
{% if comment.author == request.user %}
<a href="{{ comment.get_modification_url }}#edited_comment" title="Éditer mon commentaire"><i class="fa fa-pencil" aria-hidden="true"></i></a>
{% endif %}
</div>
{{ comment.text|linebreaks }}
</div>
{% endif %}
{% empty %}
<p>(Aucun commentaire sur ce jeu)</p>
{% endfor %}
{% if not edited_comment %}
{% if request.user.is_authenticated %}
<form class="comment" method="post" action="{{ add_comment_url }}">
{% csrf_token %}
<textarea name="comment_text" placeholder="Ajouter un commentaire" required></textarea>
<button type="submit"><i class="fa fa-paper-plane" aria-hidden="true"></i> Envoyer le commentaire</button>
</form>
{% else %}
<p><a href="{% url "accounts:login" %}?next={{ request.get_full_path }}">Connectez-vous</a> pour ajouter un commentaire.</p>
{% endif %}
{% endif %}

3
comments/tests.py Normal file
View file

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

59
comments/views.py Normal file
View file

@ -0,0 +1,59 @@
from django.views.generic import TemplateView, RedirectView
from django.views.generic.detail import SingleObjectMixin
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib import messages
from django.http import Http404
from django.core.exceptions import PermissionDenied
from django.shortcuts import redirect, get_object_or_404
class AddCommentView(LoginRequiredMixin, SingleObjectMixin, RedirectView):
permanent = False
def get_redirect_url(self, *args, **kwargs):
req_dict = self.request.POST
if "comment_text" in req_dict:
comment_text = req_dict["comment_text"]
comment = self.comment_model(
commented_object=self.get_object(),
author=self.request.user,
text=comment_text,
)
comment.save()
messages.success(self.request, "Commentaire ajouté")
else:
messages.error(
self.request, "Pas de texte pour le commentaire dans la requête"
)
return super().get_redirect_url(*args, **kwargs)
class ModifyCommentView(LoginRequiredMixin, SingleObjectMixin, TemplateView):
def dispatch(self, *args, **kwargs):
comment_id = kwargs.pop("comment_id")
self.object = self.get_object()
self.comment = get_object_or_404(self.comment_model, id=comment_id)
if self.comment.commented_object != self.object:
raise Http404()
if self.comment.author != self.request.user:
raise PermissionDenied()
return super().dispatch(*args, **kwargs)
def post(self, *args, **kwargs):
req_dict = self.request.POST
if "comment_text" in req_dict:
self.comment.text = req_dict["comment_text"]
self.comment.save()
messages.success(self.request, "Commentaire modifié")
else:
messages.error(
self.request, "Pas de texte pour le commentaire dans la requête"
)
return redirect(self.success_pattern_name, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["edited_comment"] = self.comment
return context

View file

@ -25,12 +25,13 @@ INSTALLED_APPS = [
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django_cas_ng",
"markdownx",
"haystack",
"website",
"inventory",
"django_cas_ng",
"accounts",
"comments",
"inventory",
]
MIDDLEWARE = [

View file

@ -1,7 +1,8 @@
from django.contrib import admin
from .models import Category, Tag, Game, GameComment
from comments.admin import CommentAdmin
admin.site.register(Category)
admin.site.register(Tag)
admin.site.register(Game)
admin.site.register(GameComment)
admin.site.register(GameComment, CommentAdmin)

View file

@ -1,4 +1,4 @@
# Generated by Django 3.1.2 on 2020-12-21 01:20
# Generated by Django 3.1.2 on 2020-12-27 17:01
import autoslug.fields
from django.conf import settings
@ -70,7 +70,7 @@ class Migration(migrations.Migration):
('created_on', models.DateTimeField(auto_now_add=True, verbose_name='date de publication')),
('modified_on', models.DateTimeField(auto_now=True, verbose_name='date de modification')),
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='auteur·ice')),
('game', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='inventory.game', verbose_name='jeu')),
('commented_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='inventory.game', verbose_name='jeu')),
],
options={
'verbose_name': 'commentaire sur un jeu',

View file

@ -2,7 +2,7 @@ from django.db import models
from django.urls import reverse
from django.core.exceptions import ValidationError
from autoslug import AutoSlugField
from accounts.models import User
from comments.models import AbstractComment
class Category(models.Model):
@ -95,25 +95,17 @@ class Game(models.Model):
return reverse("inventory:game", args=(self.slug,))
class GameComment(models.Model):
game = models.ForeignKey(
class GameComment(AbstractComment):
commented_object = models.ForeignKey(
Game, on_delete=models.CASCADE, related_name="comments", verbose_name="jeu"
)
author = models.ForeignKey(
User, on_delete=models.CASCADE, verbose_name="auteur·ice"
)
text = models.TextField(verbose_name="texte")
created_on = models.DateTimeField(
auto_now_add=True, verbose_name="date de publication"
)
modified_on = models.DateTimeField(
auto_now=True, verbose_name="date de modification"
)
class Meta:
ordering = ["created_on"]
verbose_name = "commentaire sur un jeu"
verbose_name_plural = "commentaires sur des jeux"
def __str__(self):
return "({}) {}: {}".format(self.game, self.author, self.text)
def get_modification_url(self):
return reverse(
"inventory:modify_game_comment", args=(self.commented_object.slug, self.id)
)

View file

@ -38,43 +38,6 @@
{% endif %}
<h2>Commentaires et propositions de variantes</h2>
{% for comment in game.comments.all %}
{% if comment == edited_comment %}
<form id="edited_comment" class="comment" method="post">
<div class="meta">
<span class="author">{{ comment.author }}</span>
<span class="date">posté le {{ comment.created_on|date }} à {{ comment.created_on|time }}</span>
<a href="{% url "inventory:game" game.slug %}" title="Annuler la modification"><i class="fa fa-ban" aria-hidden="true"></i></a>
</div>
{% csrf_token %}
<textarea name="comment_text" required>{{ comment.text }}</textarea>
<button type="submit"><i class="fa fa-pencil" aria-hidden="true"></i> Modifier mon commentaire</button>
</form>
{% else %}
<div class="comment">
<div class="meta">
<span class="author">{{ comment.author }}</span>
<span class="date">posté le {{ comment.created_on|date }} à {{ comment.created_on|time }}{% if comment.created_on|date:"YmdHi" != comment.modified_on|date:"YmdHi" %}, dernière modification le {{ comment.modified_on|date }} à {{ comment.modified_on|time }}{% endif %}</span>
{% if comment.author == request.user %}
<a href="{% url "inventory:modify_game_comment" game.slug comment.id %}#edited_comment" title="Éditer mon commentaire"><i class="fa fa-pencil" aria-hidden="true"></i></a>
{% endif %}
</div>
{{ comment.text|linebreaks }}
</div>
{% endif %}
{% empty %}
(Aucun commentaire sur ce jeu)
{% endfor %}
{% if not edited_comment %}
{% if request.user.is_authenticated %}
<form class="comment" method="post" action="{% url "inventory:add_game_comment" game.slug %}">
{% csrf_token %}
<textarea name="comment_text" placeholder="Ajouter un commentaire à propos de {{ game.title }}" required></textarea>
<button type="submit"><i class="fa fa-paper-plane" aria-hidden="true"></i> Envoyer le commentaire</button>
</form>
{% else %}
<a href="{% url "accounts:login" %}?next={{ request.get_full_path }}">Connectez-vous</a> pour ajouter un commentaire.
{% endif %}
{% endif %}
{% url "inventory:add_game_comment" game.slug as add_comment_url %}
{% include "comments.html" with comments=game.comments.all %}
{% endblock %}

View file

@ -1,19 +1,8 @@
from django.views.generic import (
TemplateView,
ListView,
DetailView,
RedirectView,
)
from django.views.generic.detail import SingleObjectMixin
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse
from django.shortcuts import redirect, get_object_or_404
from django.contrib import messages
from django.http import Http404
from django.core.exceptions import PermissionDenied
from django.views.generic import TemplateView, ListView, DetailView
from haystack.generic_views import SearchView
from haystack.forms import SearchForm
from haystack.query import SearchQuerySet
from comments.views import AddCommentView, ModifyCommentView
from .models import Category, Tag, Game, GameComment
@ -59,53 +48,14 @@ class InventorySearchView(SearchView):
return SearchQuerySet().models(Category, Tag, Game)
class AddGameCommentView(LoginRequiredMixin, SingleObjectMixin, RedirectView):
class AddGameCommentView(AddCommentView):
model = Game
permanent = False
def get_redirect_url(self, *args, **kwargs):
req_dict = self.request.POST
if "comment_text" in req_dict:
comment_text = req_dict["comment_text"]
game = self.get_object()
game.comments.create(author=self.request.user, text=comment_text)
messages.success(self.request, "Commentaire ajouté")
else:
messages.error(
self.request, "Pas de texte pour le commentaire dans la requête"
)
return reverse("inventory:game", kwargs=kwargs)
comment_model = GameComment
pattern_name = "inventory:game"
class ModifyGameCommentView(LoginRequiredMixin, SingleObjectMixin, TemplateView):
class ModifyGameCommentView(ModifyCommentView):
model = Game
comment_model = GameComment
template_name = "inventory/game.html"
def dispatch(self, *args, **kwargs):
comment_id = kwargs["comment_id"]
self.object = self.get_object()
self.comment = get_object_or_404(GameComment, id=comment_id)
if self.comment.game != self.object:
raise Http404()
if self.comment.author != self.request.user:
raise PermissionDenied()
return super().dispatch(*args, **kwargs)
def post(self, *args, **kwargs):
req_dict = self.request.POST
if "comment_text" in req_dict:
self.comment.text = req_dict["comment_text"]
self.comment.save()
messages.success(self.request, "Commentaire modifié")
else:
messages.error(
self.request, "Pas de texte pour le commentaire dans la requête"
)
return redirect("inventory:game", slug=kwargs["slug"])
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["edited_comment"] = self.comment
return context
success_pattern_name = "inventory:game"