views.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. # mysite/views.py
  2. from django.views.generic import ListView, CreateView, UpdateView, DeleteView, DetailView
  3. from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
  4. from django.contrib.auth import logout
  5. from django.shortcuts import render, redirect, get_object_or_404
  6. from django.urls import reverse_lazy
  7. from django.db.models import Avg, Count
  8. from django.contrib import messages
  9. from .models import Musician, Song, Meloman, Rating, Album, Genre
  10. from .forms import MusicianForm, SongForm, RatingForm, RegistrationForm
  11. from django.views.generic import TemplateView
  12. # Регистрация
  13. def register(request):
  14. if request.method == 'POST':
  15. form = RegistrationForm(request.POST)
  16. if form.is_valid():
  17. form.save()
  18. messages.success(request, 'Регистрация прошла успешно! Теперь вы можете войти в систему.')
  19. return redirect('mysite:login')
  20. else:
  21. form = RegistrationForm()
  22. return render(request, 'mysite/register.html', {'form': form})
  23. # Выход из системы
  24. def custom_logout(request):
  25. logout(request)
  26. messages.success(request, 'Вы успешно вышли из системы')
  27. return redirect('mysite:login')
  28. # Главная страница со списком песен
  29. class SongListView(ListView):
  30. model = Song
  31. template_name = 'mysite/song_list.html'
  32. context_object_name = 'songs'
  33. def get_queryset(self):
  34. return Song.objects.select_related('album__musician', 'genre').annotate(
  35. avg_rating=Avg('rating__score'),
  36. ratings_count=Count('rating')
  37. )
  38. def get_context_data(self, **kwargs):
  39. context = super().get_context_data(**kwargs)
  40. search_query = self.request.GET.get('search', '')
  41. if search_query:
  42. context['songs'] = context['songs'].filter(
  43. title__icontains=search_query
  44. ) | context['songs'].filter(
  45. album__musician__name__icontains=search_query
  46. )
  47. context['search_query'] = search_query
  48. return context
  49. # Список исполнителей
  50. class MusicianListView(LoginRequiredMixin, ListView):
  51. model = Musician
  52. template_name = 'mysite/musician_list.html'
  53. context_object_name = 'musicians'
  54. paginate_by = 10
  55. def get_queryset(self):
  56. return Musician.objects.annotate(albums_count=Count('album'))
  57. class MusicianCreateView(LoginRequiredMixin, CreateView):
  58. model = Musician
  59. form_class = MusicianForm
  60. template_name = 'mysite/musician_form.html'
  61. success_url = reverse_lazy('mysite:musician_list')
  62. def form_valid(self, form):
  63. messages.success(self.request, 'Исполнитель успешно добавлен')
  64. return super().form_valid(form)
  65. class MusicianUpdateView(LoginRequiredMixin, UpdateView):
  66. model = Musician
  67. form_class = MusicianForm
  68. template_name = 'mysite/musician_form.html'
  69. success_url = reverse_lazy('mysite:musician_list')
  70. def form_valid(self, form):
  71. messages.success(self.request, 'Данные исполнителя обновлены')
  72. return super().form_valid(form)
  73. class MusicianDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
  74. model = Musician
  75. template_name = 'mysite/musician_confirm_delete.html'
  76. success_url = reverse_lazy('mysite:musician_list')
  77. def test_func(self):
  78. return self.request.user.is_superuser
  79. def delete(self, request, *args, **kwargs):
  80. messages.success(request, 'Исполнитель удален')
  81. return super().delete(request, *args, **kwargs)
  82. # Список песен для CRUD
  83. class SongListDataView(LoginRequiredMixin, ListView):
  84. model = Song
  85. template_name = 'mysite/song_list_data.html'
  86. context_object_name = 'songs'
  87. def get_queryset(self):
  88. return Song.objects.select_related('album__musician', 'genre').annotate(
  89. avg_rating=Avg('rating__score')
  90. )
  91. class SongCreateView(LoginRequiredMixin, CreateView):
  92. model = Song
  93. form_class = SongForm
  94. template_name = 'mysite/song_form.html'
  95. success_url = reverse_lazy('mysite:song_list_data')
  96. def form_valid(self, form):
  97. messages.success(self.request, 'Песня успешно добавлена')
  98. return super().form_valid(form)
  99. class SongUpdateView(LoginRequiredMixin, UpdateView):
  100. model = Song
  101. form_class = SongForm
  102. template_name = 'mysite/song_form.html'
  103. success_url = reverse_lazy('mysite:song_list_data')
  104. def form_valid(self, form):
  105. messages.success(self.request, 'Данные песни обновлены')
  106. return super().form_valid(form)
  107. class SongDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
  108. model = Song
  109. template_name = 'mysite/song_confirm_delete.html'
  110. success_url = reverse_lazy('mysite:song_list_data')
  111. def test_func(self):
  112. return self.request.user.is_superuser
  113. # Список меломанов
  114. class MelomanListView(LoginRequiredMixin, ListView):
  115. model = Meloman
  116. template_name = 'mysite/meloman_list.html'
  117. context_object_name = 'melomans'
  118. def get_queryset(self):
  119. return Meloman.objects.annotate(
  120. ratings_count=Count('rating'),
  121. avg_score=Avg('rating__score')
  122. )
  123. def dispatch(self, request, *args, **kwargs):
  124. if not request.user.is_superuser:
  125. return redirect('mysite:song_list')
  126. return super().dispatch(request, *args, **kwargs)
  127. # Профиль меломана
  128. class MelomanProfileView(LoginRequiredMixin, DetailView):
  129. model = Meloman
  130. template_name = 'mysite/profile.html'
  131. context_object_name = 'meloman'
  132. def get_object(self):
  133. try:
  134. return Meloman.objects.get(user=self.request.user)
  135. except Meloman.DoesNotExist:
  136. return None
  137. def get_context_data(self, **kwargs):
  138. context = super().get_context_data(**kwargs)
  139. meloman = self.get_object()
  140. if meloman:
  141. context['total_ratings'] = meloman.rating_set.count()
  142. context['avg_user_rating'] = meloman.rating_set.aggregate(Avg('score'))['score__avg']
  143. context['favorite_genres'] = meloman.preference_set.select_related('genre').all()
  144. context['age'] = meloman.age
  145. context['recent_ratings'] = meloman.rating_set.select_related('song').order_by('-rating_date')[:5]
  146. return context
  147. # Оценка песни
  148. def rate_song(request, song_id):
  149. if not request.user.is_authenticated:
  150. return redirect('mysite:login')
  151. song = get_object_or_404(Song, pk=song_id)
  152. try:
  153. meloman = Meloman.objects.get(user=request.user)
  154. except Meloman.DoesNotExist:
  155. messages.error(request, 'У вас нет профиля меломана. Обратитесь к администратору.')
  156. return redirect('mysite:song_list')
  157. rating, created = Rating.objects.get_or_create(
  158. meloman=meloman,
  159. song=song,
  160. defaults={'score': 5}
  161. )
  162. if request.method == 'POST':
  163. form = RatingForm(request.POST, instance=rating)
  164. if form.is_valid():
  165. form.save()
  166. messages.success(request, f'Оценка для "{song.title}" сохранена!')
  167. return redirect('mysite:song_list')
  168. else:
  169. form = RatingForm(instance=rating)
  170. return render(request, 'mysite/rate_song.html', {'form': form, 'song': song})
  171. from django.db import connection
  172. class SongDetailsView(ListView):
  173. """Использование представления song_details_view"""
  174. template_name = 'mysite/song_details.html'
  175. context_object_name = 'songs'
  176. def get_queryset(self):
  177. with connection.cursor() as cursor:
  178. cursor.execute("""
  179. SELECT song_id, title, musician_name, album_name,
  180. genre_name, duration_sec, avg_rating, ratings_count
  181. FROM song_details_view
  182. ORDER BY avg_rating DESC NULLS LAST
  183. """)
  184. columns = [col[0] for col in cursor.description]
  185. return [dict(zip(columns, row)) for row in cursor.fetchall()]
  186. class RecommendationsView(LoginRequiredMixin, TemplateView):
  187. """Использование функции recommend_songs"""
  188. template_name = 'mysite/recommendations.html'
  189. def get_context_data(self, **kwargs):
  190. context = super().get_context_data(**kwargs)
  191. try:
  192. meloman = Meloman.objects.get(user=self.request.user)
  193. meloman_id = meloman.meloman_id
  194. except Meloman.DoesNotExist:
  195. context['recommendations'] = []
  196. context['error'] = 'Профиль меломана не найден'
  197. return context
  198. with connection.cursor() as cursor:
  199. cursor.execute("""
  200. SELECT * FROM recommend_songs(%s, %s)
  201. """, [meloman_id, 10])
  202. columns = [col[0] for col in cursor.description]
  203. recommendations = [dict(zip(columns, row)) for row in cursor.fetchall()]
  204. context['recommendations'] = recommendations
  205. return context