Django song not being added to 'favourite songs'

Viewed 74

I want my users to be able to add certain songs to Favourite Songs but although the success message 'Added to favourite songs' but when I visit the Favourite Songs page, I see no songs there. How can I fix this? Thanks in advance!

My models.py:

class Songs(models.Model):
    title = models.CharField(max_length = 100)
    lyrics = models.TextField()
    author = models.CharField(max_length = 100)
    track_image = models.CharField(max_length=2083)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('/', kwargs={'pk': self.pk})

My views.py:

def home(request):
    context = {
        'songs': Songs.objects.all()
    }
    return render(request, 'home.html', context)

@login_required
def add_to_fav_songs(request, **kwargs):
    
    fav_song = Songs.objects.filter(id=kwargs.get('id'))
    messages.success(request, f'Added to favourite songs')
    return redirect('/')

class Fav_songs(ListView):
    model = Songs
    template_name = 'fav_songs.html'
    context_object_name = 'fav_song'
    paginate_by = 2

    def get_queryset(self):
        return Songs.objects.filter(pk=self.kwargs.get('pk'))

My favoutie_songs.html:

{% for song in fav_song %}
  <article class="media content-section">
    <div class="media-body">
      
      <h2><a class="article-title" href="{% url 'song-detail' song.id %}">{{ song.title }}</a></h2>
      <div class="article-metadata">
        <a class="mr-2" href="{% url 'author-songs' song.author %}">{{ song.author }}</a>
      </div>
      <br>
      <img class="card-img-bottom" height="339px" width="20px" src="{{ song.track_image }}">
    </div>
  </article>
{% endfor %}
1 Answers

Your Song is not connected to the User, so you never keep track about what user has wat song as favorite.

You should add a ManyToManyField to your Song model with:

from django.conf import settings

class Songs(models.Model):
    # …
    favorited_by = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        related_name='favorite_songs'
    )
    # …

Then in our view we can add the logged in user to the favorited_by field. Since we here alter data, this should be done with a POST request:

from django.contrib.auth.decorators import loginrequired
from django.shortcuts import get_object_or_404
from django.views.decorators.http import require_http_methods

@login_required
@require_POST
def add_to_fav_songs(request, pk):
    fav_song = get_object_or_404(Songs, id=pk)
    fav_song.favorited_by.add(request.user)
    messages.success(request, 'Added to favourite songs')
    return redirect('/')

For the ListView, we can then filter by the logged in user:

from django.contrib.auth.mixins import LoginRequiredMixin

class Fav_songs(LoginRequiredMixin, ListView):
    model = Songs
    template_name = 'fav_songs.html'
    context_object_name = 'fav_song'
    paginate_by = 2

    def get_queryset(self):
        return Songs.objects.filter(favorited_by=self.request.user)

You should change the button to add this to the favorite to a miniform:

<form method="post" action="{% url 'add-to-fav-songs' song.id %}">
    <button class="btn btn-danger" type="submit">Add to Favorite Songs</button>
</form>

Note: You can limit views to a class-based view to authenticated users with the LoginRequiredMixin mixin [Django-doc].

Related