diff --git a/base/static/css/main.css b/base/static/css/main.css index e916ab0..60f2420 100644 --- a/base/static/css/main.css +++ b/base/static/css/main.css @@ -64,9 +64,3 @@ article.message { color: var(--pico-color-red-500); } } - -.form-error::before { - margin-right: .5em; - font-family: remixicon; - content: "\eca0"; -} diff --git a/base/templates/base/form.html b/base/templates/base/form.html index 979daa3..df40bc7 100644 --- a/base/templates/base/form.html +++ b/base/templates/base/form.html @@ -1,4 +1,8 @@ -{% for error in form.non_field_errors %}
{{ error }}
{% endfor %} +{% if form.non_field_errors %} + +{% endif %}
{% csrf_token %}
@@ -6,10 +10,12 @@ + {% if field.errors %} + + {% endif %} {% endfor %}
diff --git a/game/migrations/0013_alter_group_unique_together_and_more.py b/game/migrations/0013_alter_group_unique_together_and_more.py deleted file mode 100644 index 438a287..0000000 --- a/game/migrations/0013_alter_group_unique_together_and_more.py +++ /dev/null @@ -1,53 +0,0 @@ -# Generated by Django 5.2.3 on 2025-06-14 08:13 - -import django.db.models.functions.text -from django.conf import settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("game", "0012_alter_musikgame_playlist"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.AlterUniqueTogether( - name="group", - unique_together=set(), - ), - migrations.AlterUniqueTogether( - name="musicgameorder", - unique_together=set(), - ), - migrations.AlterUniqueTogether( - name="musicvideo", - unique_together=set(), - ), - migrations.AddConstraint( - model_name="group", - constraint=models.UniqueConstraint( - django.db.models.functions.text.Lower("name"), - models.F("owner"), - name="unique_group_name", - ), - ), - migrations.AddConstraint( - model_name="musicgameorder", - constraint=models.UniqueConstraint( - fields=("game", "player", "music_video"), name="unique_music_in_game" - ), - ), - migrations.AddConstraint( - model_name="musicgameorder", - constraint=models.UniqueConstraint( - fields=("game", "order"), name="unique_order" - ), - ), - migrations.AddConstraint( - model_name="musicvideo", - constraint=models.UniqueConstraint( - fields=("yt_id", "owner", "group"), name="unique_music_in_group" - ), - ), - ] diff --git a/game/models.py b/game/models.py index 886bd21..8383915 100644 --- a/game/models.py +++ b/game/models.py @@ -1,6 +1,5 @@ from django.contrib.auth.models import User from django.db import models -from django.db.models.functions import Lower from django.urls import reverse @@ -20,9 +19,7 @@ class Group(models.Model): return reverse("group_detail", kwargs={"pk": self.pk}) class Meta: - constraints = [ - models.UniqueConstraint(Lower("name"), "owner", name="unique_group_name") - ] + unique_together = ["name", "owner"] class MusicVideo(models.Model): @@ -34,11 +31,7 @@ class MusicVideo(models.Model): blacklisted = models.BooleanField(default=False) class Meta: - constraints = [ - models.UniqueConstraint( - fields=("yt_id", "owner", "group"), name="unique_music_in_group" - ) - ] + unique_together = ["yt_id", "owner", "group"] class MusikGame(models.Model): @@ -59,10 +52,5 @@ class MusicGameOrder(models.Model): order = models.PositiveIntegerField() class Meta: - constraints = [ - models.UniqueConstraint( - fields=("game", "player", "music_video"), name="unique_music_in_game" - ), - models.UniqueConstraint(fields=("game", "order"), name="unique_order"), - ] + unique_together = [["game", "player", "music_video"], ["game", "order"]] ordering = ["order"] diff --git a/game/views.py b/game/views.py index a24c1c6..1b188e5 100644 --- a/game/views.py +++ b/game/views.py @@ -4,13 +4,12 @@ import google.oauth2.credentials import google_auth_oauthlib import googleapiclient.discovery from django.conf import settings -from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.models import User from django.contrib.messages.views import SuccessMessageMixin -from django.db import IntegrityError from django.db.models import Count, Q -from django.shortcuts import redirect +from django.http import JsonResponse +from django.shortcuts import get_object_or_404, redirect from django.views import View from django.views.generic.detail import DetailView, SingleObjectMixin from django.views.generic.edit import CreateView, DeleteView, UpdateView @@ -38,22 +37,13 @@ class GroupMixin: fields = ["name"] -class GroupIntegrityMixin: - def form_valid(self, form): - try: - return super().form_valid(form) - except IntegrityError: - form.add_error("name", "Ce nom de groupe existe déjà.") - return super().form_invalid(form) - - -class GroupCreateView(LoginRequiredMixin, GroupMixin, GroupIntegrityMixin, CreateView): +class GroupCreateView(LoginRequiredMixin, GroupMixin, CreateView): def form_valid(self, form): form.instance.owner = self.request.user return super().form_valid(form) -class GroupUpdateView(OwnerFilterMixin, GroupMixin, GroupIntegrityMixin, UpdateView): +class GroupUpdateView(OwnerFilterMixin, GroupMixin, UpdateView): pass @@ -89,26 +79,14 @@ class GroupAddMusicView(MemberFilterMixin, SingleObjectMixin, View): group = self.get_object() yt_id = request.POST.get("yt_id") if not yt_id: - messages.add_message(request, messages.ERROR, "Aucun identifiant donné") - return redirect(group) + return JsonResponse({"error": "You must provide a YouTube ID."}, status=400) yt_id = utils.parse_musik(yt_id) title = utils.get_yt_title(yt_id) if not title: - messages.add_message( - request, messages.ERROR, f"Vidéo Youtube invalide : {yt_id}" - ) - return redirect(group) - try: - group.musicvideo_set.create(yt_id=yt_id, title=title, owner=request.user) - except IntegrityError: - messages.add_message( - request, messages.ERROR, f"Vidéo Youtube déjà ajoutée : {yt_id}" - ) - - messages.add_message( - request, messages.SUCCESS, f"Vidéo Youtube ajoutée : {yt_id}" - ) + return JsonResponse({"error": "Invalid YouTube ID."}, status=400) + group.musicvideo_set.create(yt_id=yt_id, title=title, owner=request.user) + group.save() return redirect(group) @@ -119,15 +97,6 @@ class GroupAddMemberView(OwnerFilterMixin, SingleObjectMixin, View): group = self.get_object() username = request.POST.get("username") user = User.objects.get(username=username) - if user == group.owner: - messages.add_message( - request, messages.WARNING, f"{user} est le propriétaire du groupe." - ) - return redirect(group) - if user in group.members.all(): - messages.add_message( - request, messages.WARNING, f"{user} est déjà membre du groupe." - ) group.members.add(user) return redirect(group) @@ -143,25 +112,17 @@ class GroupRemoveMusicView(OwnerFilterMixin, SingleObjectMixin, View): return redirect(group) -class GroupRemoveMemberView(OwnerFilterMixin, SingleObjectMixin, View): - model = models.Group - +class GroupRemoveMemberView(View): def get(self, request, pk, user_pk): - group = self.get_object() - user = User.objects.get(pk=user_pk) - - relation = models.Group.members.through.objects.filter( - group=group, user=user - ).first() - if not relation: - messages.add_message( - request, - messages.ERROR, - f"L'utilisateur {user} n'est pas membre du groupe.", - ) - else: - relation.delete() + relation = get_object_or_404( + models.Group.members.through, + group_id=pk, + user_id=user_pk, + group__owner=request.user, + ) + group = relation.group + relation.delete() return redirect(group) @@ -176,11 +137,6 @@ class GroupRemoveGameView(SingleObjectMixin, SuccessMessageMixin, View): game = self.get_object() group = game.group game.delete() - messages.add_message( - request, - messages.SUCCESS, - f"Le jeu du {game.date.strftime('%x')} a été supprimé avec succès.", - ) return redirect(group) @@ -294,11 +250,9 @@ class YoutubeLoginView(LoginRequiredMixin, View): class YoutubeCallbackView(LoginRequiredMixin, View): def get(self, request): - if err := request.GET.get("error"): - messages.add_message( - request, messages.ERROR, f"Échec de la connexion à Youtube : {err}" - ) + if request.GET.get("error"): return redirect("/") + print(request.GET) state = self.request.session.get("state") flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( @@ -324,8 +278,6 @@ class YoutubeCallbackView(LoginRequiredMixin, View): } }, ) - - messages.add_message(request, messages.SUCCESS, "Connexion à Youtube réussie.") return redirect("/") @@ -335,5 +287,4 @@ class GroupClearBlacklistView(OwnerFilterMixin, SingleObjectMixin, View): def get(self, request, pk): group = self.get_object() group.musicvideo_set.filter(blacklisted=True).update(blacklisted=False) - messages.add_message(request, messages.SUCCESS, "La blacklist a été vidée.") return redirect(group)