dev/playlist #7

Merged
lovinervy merged 2 commits from dev/playlist into main 2026-01-06 17:08:27 +05:00
7 changed files with 158 additions and 10 deletions

View File

@@ -1,11 +1,10 @@
from django.urls import path from django.urls import path
from core.views import index, sentry_debug from core.views import IndexView
app_name = "index" app_name = "index"
urlpatterns = [ urlpatterns = [
path("", index, name="main_index"), path("", IndexView.as_view(), name="main_index"),
path("sentry-debug/", sentry_debug, name="sentry_debug"),
] ]

View File

@@ -1,9 +1,19 @@
from django.shortcuts import render from django.shortcuts import render
from django.views.generic import View
from django.http.request import HttpRequest
from django.shortcuts import get_object_or_404
from music.models import RecommendationPlaylist
def index(request, *args, **kwargs): class IndexView(View):
return render(request, "index.html") def get(self, request: HttpRequest, *args, **kwargs):
if recommendation_playlist := RecommendationPlaylist.objects.filter(
is_actual=True
def sentry_debug(request): ).first():
division_by_zero = 1 / 0 # noqa tracks = recommendation_playlist.playlist.tracks.all().select_related(
"album", "album__artist"
)
else:
tracks = []
return render(request, "index.html", {"tracks": tracks})

View File

@@ -5,7 +5,11 @@ from django.http import HttpRequest
from django.urls import reverse from django.urls import reverse
from django.utils.html import format_html from django.utils.html import format_html
from music.models import Track, Album, Artist from music.models import Track
from music.models import Album
from music.models import Artist
from music.models import Playlist
from music.models import RecommendationPlaylist
@admin.register(Track) @admin.register(Track)
@@ -79,3 +83,25 @@ class ArtistAdmin(admin.ModelAdmin):
return format_html('<a class="button" href="{}">Add Album</a>', url) return format_html('<a class="button" href="{}">Add Album</a>', url)
add_album_link.short_description = "Add Album" add_album_link.short_description = "Add Album"
@admin.register(Playlist)
class PlaylistAdmin(admin.ModelAdmin):
list_display = ("name", "created_by", "created_at")
search_fields = ("name",)
list_filter = ("created_at",)
filter_horizontal = ("tracks",)
@admin.register(RecommendationPlaylist)
class RecommendationPlaylistAdmin(admin.ModelAdmin):
list_display = ("name", "is_actual", "created_by", "created_at")
search_fields = ("name",)
list_filter = ("is_actual", "created_at")
actions = ["make_actual"]
@admin.action(description="Set selected recommendation playlists as actual")
def make_actual(self, request: HttpRequest, queryset: Any) -> None:
for recommendation in queryset:
recommendation.switch_actual()

View File

@@ -0,0 +1,48 @@
# Generated by Django 6.0 on 2026-01-06 11:42
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('music', '0010_album_release_date'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Playlist',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=200)),
('created_by', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created', to=settings.AUTH_USER_MODEL)),
('tracks', models.ManyToManyField(related_name='playlists', to='music.track')),
('updated_by', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated', to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='RecommendationPlaylist',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=200)),
('description', models.TextField(blank=True)),
('is_actual', models.BooleanField(default=False)),
('created_by', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_created', to=settings.AUTH_USER_MODEL)),
('playlist', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='recommendations', to='music.playlist')),
('updated_by', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated', to=settings.AUTH_USER_MODEL)),
],
options={
'constraints': [models.UniqueConstraint(condition=models.Q(('is_actual', True)), fields=('is_actual',), name='unique_actual_recommendation_playlist')],
},
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 6.0 on 2026-01-06 12:04
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('music', '0011_playlist_recommendationplaylist'),
]
operations = [
migrations.AlterField(
model_name='recommendationplaylist',
name='playlist',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='recommendations', to='music.playlist'),
),
]

View File

@@ -79,3 +79,40 @@ class Track(BaseModel):
def __str__(self): def __str__(self):
return f"{self.album.artist} - {self.title}" return f"{self.album.artist} - {self.title}"
class Playlist(BaseModel):
name = models.CharField(max_length=200)
tracks = models.ManyToManyField(Track, related_name="playlists")
def __str__(self):
return f"Playlist: {self.name}"
class RecommendationPlaylist(BaseModel):
class Meta:
constraints = [
models.UniqueConstraint(
fields=["is_actual"],
condition=models.Q(is_actual=True),
name="unique_actual_recommendation_playlist",
)
]
name = models.CharField(max_length=200)
description = models.TextField(blank=True)
playlist = models.OneToOneField(
Playlist, on_delete=models.CASCADE, related_name="recommendations"
)
is_actual = models.BooleanField(default=False)
def __str__(self):
return f"Recommendation Playlist: {self.name}"
def switch_actual(self):
if not self.is_actual:
(RecommendationPlaylist.objects
.filter(is_actual=True)
.update(is_actual=False))
self.is_actual = True
self.save()

View File

@@ -1 +1,10 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static %}
{% block content %}
<div class="container">
<h1>Рекомендации:</h1>
{% include "components/track_list.html" with tracks=tracks %}
</div>
{% include "components/player.html" %}
{% endblock %}