forked from DGNum/gestiojeux
Refactorize to enable code sharing for comments
This commit is contained in:
parent
98c517421c
commit
d4a9faef3e
14 changed files with 158 additions and 117 deletions
0
comments/__init__.py
Normal file
0
comments/__init__.py
Normal file
5
comments/admin.py
Normal file
5
comments/admin.py
Normal 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
5
comments/apps.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class CommentsConfig(AppConfig):
|
||||||
|
name = 'comments'
|
0
comments/migrations/__init__.py
Normal file
0
comments/migrations/__init__.py
Normal file
23
comments/models.py
Normal file
23
comments/models.py
Normal 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)
|
39
comments/templates/comments.html
Normal file
39
comments/templates/comments.html
Normal 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
3
comments/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
59
comments/views.py
Normal file
59
comments/views.py
Normal 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
|
|
@ -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 = [
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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)
|
||||||
|
)
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue