I am building a web app music player using Django with Postgres as the database.
My setup right now displays the first song for each album. As one clicks through the play button, the view changes to the first track of every album.
I would like to display all the tracks for any given album on a single page.
A single page would display these objects:
From Album: album_title, artist, artwork_file and from Track: track_title and audio_file for each track in the album
To do this I need to supply an integer to the self.per_page parameter in Django's Paginator class. Currently it is set to 1.
The number of tracks changes depending on album, so I want to pass this as an integer as a variable (Album.number_tracks). The trouble is I can't create a queryset to iterate through and pass each iteration into self.per_page because the Paginator function takes all of the objects at once rather than object by object. So I can't use any conditional loops like:
queryset = Album.objects.all()
number_of_tracks = [a.number_tracks for a in queryset]
for num_track in number_of_tracks:
# pass album's number of tracks to Paginator(per_page=num_track)
How can I display all tracks from any given album on a single page, no matter the number of songs?
Here are the relevant fields from models.py:
class Album(models.Model):
title = models.TextField(blank=False, null=False)
artist = models.ForeignKey(Artist, on_delete=models.CASCADE)
number_tracks = models.TextField(blank=True, null=True)
track_list = models.TextField(blank=True, null=True)
artwork_file = models.ImageField(blank=True, null=True, max_length=500)
def __str__(self):
return self.title
class Track(models.Model):
title = models.TextField(blank=False, null=False)
album = models.ForeignKey(Album, on_delete=models.CASCADE)
artist = models.TextField(blank=True, null=True)
track_number = models.TextField(blank=True, null=True)
audio_file = models.FileField(blank=True, null=True, max_length=500)
def __str__(self):
return self.title
views.py
def index(request):
paginator_track = Paginator(Track.objects.order_by('album', 'track_number').all(), 1)
page_number = request.GET.get('page')
page_obj_track = paginator_track.get_page(page_number)
paginator_album = Paginator(Album.objects.order_by('artist', 'title').all(), 1)
page_number = request.GET.get('page')
page_obj_album = paginator_album.get_page(page_number)
context = {'page_obj_album': page_obj_album, 'page_obj_track': page_obj_track}
return render(request, 'index.html', context)
And in index.html I display the objects like so:
{% for album_obj in page_obj_album %}
<img src='{{ album_obj.artwork_file.url }}' alt='{{ album_obj.artwork_link }}' />
<h3> {{ album_obj.artist }} </h3>
<h3> {{ album_obj.title }} </h3>
{% endfor %}
{% for track_obj in page_obj_track %}
<h1> {{ track_obj.title }} </h1>
<a href='{% if page_obj_track.has_previous %}?page={{
page_obj_track.previous_page_number }} {% endif %}'>
<i class='fa fa-step-backward fa-2x'></i></a>
<a href='{% if page_obj_track.has_next %}?page={{page_obj_track.next_page_number}} {% endif %}'><i class='fa fa-step-forward fa-2x'></i></a>
<audio class='fc-media'>
<source src='{% if track_obj.audio_file %} {{ track_obj.audio_file.url }}
{% else %} {{ track_obj.audio_link }} {% endif %}'
type='audio/mp3'/></audio>
{% endfor %}
Just in case this will help anyone in the future -- I ended up refactoring my code to get this to work. Instead of using the in-built Paginator function I used a DetailView. Here are relevant bits of the code:
I added a slug field to models.py to create the URL:
slug = AutoSlugField(null=True, default=None, unique=True, max_length=500, populate_from='title')
views.py:
from django.views.generic.detail import DetailView
from datasource.models import Album, Track
class AlbumDetailView(DetailView):
model = Album
template_name = 'album_view.html'
def get_context_data(self, **kwargs):
context = super(AlbumDetailView, self).get_context_data(**kwargs)
context['next_album'] = Album.objects.filter(title__gt=self.object.title).order_by('title').first()
context['prev_album'] = Album.objects.filter(title__lt=self.object.title).order_by('-title').first()
context['track'] = Track.objects.filter(album=self.object).order_by('track_number')
return context
In the HTML template the view is rendered like this:
{% if prev_album %}
<a href='{% url "album_detail" slug=prev_album.slug %}'><i class='fa fa-step-backward fa-2x'></i></a>
{% endif %}
{% if next_album %}
<a href='{% url "album_detail" slug=next_album.slug %}'><i class='fa fa-step-forward fa-2x'></i></a>
{% endif %}
{% for t in track %}
<h1> {{ t.title }} </h1>
<audio class='fc-media' style='width: 100%;'>
<source src='{% if t.audio_file %} {{ t.audio_file.url }} {% else %} {{ t.audio_link }} {% endif %}'type='audio/mp3'/></audio>
{% endfor %}