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.sessions",
"django.contrib.messages", "django.contrib.messages",
"django.contrib.staticfiles", "django.contrib.staticfiles",
"django_cas_ng",
"markdownx", "markdownx",
"haystack", "haystack",
"website", "website",
"inventory",
"django_cas_ng",
"accounts", "accounts",
"comments",
"inventory",
] ]
MIDDLEWARE = [ MIDDLEWARE = [

View file

@ -1,7 +1,8 @@
from django.contrib import admin from django.contrib import admin
from .models import Category, Tag, Game, GameComment from .models import Category, Tag, Game, GameComment
from comments.admin import CommentAdmin
admin.site.register(Category) admin.site.register(Category)
admin.site.register(Tag) admin.site.register(Tag)
admin.site.register(Game) 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 import autoslug.fields
from django.conf import settings 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')), ('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')), ('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')), ('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={ options={
'verbose_name': 'commentaire sur un jeu', 'verbose_name': 'commentaire sur un jeu',

View file

@ -2,7 +2,7 @@ from django.db import models
from django.urls import reverse from django.urls import reverse
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from autoslug import AutoSlugField from autoslug import AutoSlugField
from accounts.models import User from comments.models import AbstractComment
class Category(models.Model): class Category(models.Model):
@ -95,25 +95,17 @@ class Game(models.Model):
return reverse("inventory:game", args=(self.slug,)) return reverse("inventory:game", args=(self.slug,))
class GameComment(models.Model): class GameComment(AbstractComment):
game = models.ForeignKey( commented_object = models.ForeignKey(
Game, on_delete=models.CASCADE, related_name="comments", verbose_name="jeu" 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: class Meta:
ordering = ["created_on"] ordering = ["created_on"]
verbose_name = "commentaire sur un jeu" verbose_name = "commentaire sur un jeu"
verbose_name_plural = "commentaires sur des jeux" verbose_name_plural = "commentaires sur des jeux"
def __str__(self): def get_modification_url(self):
return "({}) {}: {}".format(self.game, self.author, self.text) return reverse(
"inventory:modify_game_comment", args=(self.commented_object.slug, self.id)
)

View file

@ -38,43 +38,6 @@
{% endif %} {% endif %}
<h2>Commentaires et propositions de variantes</h2> <h2>Commentaires et propositions de variantes</h2>
{% for comment in game.comments.all %} {% url "inventory:add_game_comment" game.slug as add_comment_url %}
{% if comment == edited_comment %} {% include "comments.html" with comments=game.comments.all %}
<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 %}
{% endblock %} {% endblock %}

View file

@ -1,19 +1,8 @@
from django.views.generic import ( from django.views.generic import TemplateView, ListView, DetailView
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 haystack.generic_views import SearchView from haystack.generic_views import SearchView
from haystack.forms import SearchForm from haystack.forms import SearchForm
from haystack.query import SearchQuerySet from haystack.query import SearchQuerySet
from comments.views import AddCommentView, ModifyCommentView
from .models import Category, Tag, Game, GameComment from .models import Category, Tag, Game, GameComment
@ -59,53 +48,14 @@ class InventorySearchView(SearchView):
return SearchQuerySet().models(Category, Tag, Game) return SearchQuerySet().models(Category, Tag, Game)
class AddGameCommentView(LoginRequiredMixin, SingleObjectMixin, RedirectView): class AddGameCommentView(AddCommentView):
model = Game model = Game
permanent = False comment_model = GameComment
pattern_name = "inventory:game"
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)
class ModifyGameCommentView(LoginRequiredMixin, SingleObjectMixin, TemplateView): class ModifyGameCommentView(ModifyCommentView):
model = Game model = Game
comment_model = GameComment
template_name = "inventory/game.html" template_name = "inventory/game.html"
success_pattern_name = "inventory:game"
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