Как отобразить картинку в Django DetailView на основе отношения внешнего ключа?
В настоящее время я создаю сайт на Django, на котором будет размещаться информация о музыке и альбомах/песнях. Мои модели настроены так, что каждая песня имеет внешний ключ к связанному с ней альбому, или к объекту базы данных под названием "Singles", если они не являются частью альбома.
Эти отношения между песней и альбомом отлично работали при создании сайта, пока я не добрался до страницы 'Play Song', над которой я сейчас работаю. Каждый сингл имеет связанную с ним иллюстрацию в модели 'Song', в то время как каждая песня в альбоме не имеет "картинки" в базе данных, поскольку "картинка" является частью модели Album, а не модели Song в этих случаях. Я пытаюсь передать данные из модели Song и модели Album в DetailView так, чтобы если воспроизводимая песня из альбома, а не одиночная, он брал "картинку" из модели Album, к которой у него есть внешний ключ, а не из своего собственного внутреннего объекта "картинка". Мой код приведен ниже, он отлично отображает "картинку" для синглов, но не может отобразить объект "картинка" из альбома и вместо этого показывает мое условие {% else %} - картинка не найдена. HTML файл содержит логику, которую я использую для поиска связанной картинки:
{% elif song in album.song_set.all %}
<img src="{{ album.picture.url }}">
Любая помощь будет принята с благодарностью.
models.py
class Album(models.Model):
title = models.CharField(
max_length=200,
validators=[MinLengthValidator(2, "Must be at least two characters.")]
)
release_date = models.DateField()
picture = models.ImageField(upload_to='albums/', blank=True)
content_type = models.CharField(max_length=256, blank=True, help_text='The MIMEType of the file')
description = models.TextField(blank=True)
def __str__(self):
return self.title
def __unicode__(self):
return self.title
class Song(models.Model):
title = models.CharField(
max_length=200,
validators=[MinLengthValidator(2, "Must be at least two characters.")]
)
release_date = models.DateField(blank=True)
length = models.CharField(
max_length=200)
featured_artists = models.CharField(
max_length=200,
blank=True)
picture = models.ImageField(upload_to='singles/', blank=True)
content_type = models.CharField(max_length=256, blank=True, help_text='The MIMEType of the file')
description = models.TextField(blank=True)
alb = models.ForeignKey(Album, on_delete=models.CASCADE)
audio_file = models.FileField(upload_to='music_audio/', blank=True)
def __str__(self):
return self.title
views.py
class PlaySongView(DetailView):
model = Song
template_name = 'music/play_song.html'
def get_context_data(self, **kwargs):
context = super(PlaySongView, self).get_context_data(**kwargs)
context['album'] = Album.objects.exclude(title="Single")
return context
urls.py
urlpatterns = [
path('', views.MusicView.as_view(), name='music'),
path('<int:pk>/', views.AlbumDetailView.as_view(), name='album_detail'),
path('<int:pk>/play', views.PlaySongView.as_view(), name='play')
]
play_song.html
{% load static tailwind_tags %}
<!DOCTYPE html>
<head>
<title>LewnyToons - {{ song.title }}</title>
{% tailwind_css %}
</head>
<body class="antialiased text-slate-400 bg-slate-900">
<div class="w-full sm:w-9/12 lg:w-8/12 px-4 sm:pr-2 lg:pr-4 mx-auto">
<div class="flex flex-col font-bold text-2xl text-center items-center text-white mx-auto pt-2">
<h1 class="mb-4">Listen to {{ song.title }} now!</h1>
{% if song.picture %}
<img src="{{ song.picture.url }}" class="min-h-64 min-w-64 md:h-96 md:w-96"alt="">
{% elif song in album.song_set.all %}
<img src="{{ album.picture.url }}" class="min-h-64 min-w-64 md:h-96 md:w-96"alt="">
{% else %}
<img src="{% static 'something_wrong.jpg' %}" class="min-h-64 min-w-64 md:h-96 md:w-96"alt="">
{% endif %}
</div>
{% if song.audio_file %}
<span class="flex justify-center py-10">
<div>
<audio controls><source src="{{ song.audio_file.url }}" type="audio/mpeg"></audio>
</div>
</span>
{% else %}
<p>The file could not be found.</p>
<a href="{% url 'the_music:music' %}" class="text-white bold_underline">Return to music.</p>
{% endif %}
</div>
</body>
</html>
Проблема заключается в контексте альбома, передаваемом шаблону в PlaySongView.get_context_data()
.
Вы передаете QuerySet альбомов вместо объекта альбома, с которым связана песня. Поэтому, когда вы пытаетесь выполнить album.song_set.all
, вы выполняете это действие на QuerySet вместо объекта Album. Условие не выполняется, поэтому происходит возврат к блоку else.
Вместо:
context['album'] = Album.objects.exclude(title="Single")
Лучшим способом подойти к этому в get_context_data()
было бы:
song = self.get_object()
context['album'] = song.alb
self.get_object()
будет искать pk в ваших URL kwargs (в данном случае '<int:pk>/play'
) и искать песню на основе ее первичного ключа.
Затем в вашем шаблоне, это так же просто, как сделать:
{{ album }}
Здесь нет необходимости делать обратное отношение в шаблоне, потому что у вас есть прямой доступ к альбому из вашего Song DetailView с помощью self.get_object()
. Кроме того, таким образом вы не делаете лишних запросов, получая все ваши альбомы и передавая их в качестве контекста в шаблон. Все, что вам нужно, это один альбом.
Общее эмпирическое правило: если вы можете выполнить то, что вам нужно, с помощью запросов в представлении, это обычно более оптимальное решение.