Django DRF歌曲收藏功能故障:重复收藏与状态判断失效求助
收藏夹功能修复方案:解决重复添加与按钮状态异常问题
核心问题梳理
- 模板调用
song.is_favorite时未传入当前用户,导致收藏状态判断完全失效 - API的POST接口未做重复收藏校验,允许同一用户重复添加同一歌曲
- HTML按钮缺少
data-song-id属性,导致JS无法准确定位元素更新状态 - API的GET接口逻辑错误,未正确返回歌曲的收藏状态
具体修复步骤
1. 修复模板中的收藏状态判断与按钮属性
修改song_list.html中的按钮代码,传入用户参数给is_favorite方法,并添加data-song-id属性:
{% if user.is_authenticated %} {% if song.is_favorite(user) %} <button data-song-id="{{ song.id }}" onclick="removeFavorite({{ song.id }})">Supprimer des favoris</button> {% else %} <button data-song-id="{{ song.id }}" onclick="addFavorite({{ song.id }})">Ajouter aux favoris</button> {% endif %} {% else %} <a href="{% url 'login' %}?next={{ request.path }}">Connectez-vous pour ajouter aux favoris</a> {% endif %}
2. 优化Song模型的is_favorite方法(可选,语义更清晰)
因为Favorite模型中song字段设置了related_name='favorites',可以把方法改成更直观的写法:
def is_favorite(self, user): # 用related_name替代favorite_set,语义更清晰 return self.favorites.filter(user=user).exists()
3. 修复API视图逻辑,避免重复添加并正确返回收藏状态
更新FavoritesongView,修正GET方法逻辑,同时在POST方法中添加重复校验:
class FavoritesongView(APIView): def get(self, request, song_id): try: song = Song.objects.get(id=song_id) # 返回当前用户是否收藏该歌曲的状态 is_favorited = song.is_favorite(request.user) return Response({"is_favorite": is_favorited}, status=status.HTTP_200_OK) except Song.DoesNotExist: return Response({"error": "Song not found"}, status=status.HTTP_404_NOT_FOUND) except Exception as e: print(f"Error in get favorite: {e}") return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR) def post(self, request, song_id): try: song = Song.objects.get(id=song_id) # 检查是否已收藏,避免重复添加 if Favorite.objects.filter(user=request.user, song=song).exists(): return Response({"error": "Song already in favorites"}, status=status.HTTP_409_CONFLICT) favorite = Favorite(user=request.user, song=song) favorite.save() return Response({"is_favorite": True}, status=status.HTTP_201_CREATED) except Song.DoesNotExist: return Response({"error": "Song not found"}, status=status.HTTP_404_NOT_FOUND) except Exception as e: print(f"ERROR in post request: {e}") return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST) def delete(self, request, song_id): try: favorite = Favorite.objects.get(user=request.user, song_id=song_id) favorite.delete() return Response({"is_favorite": False}, status=status.HTTP_200_OK) except Favorite.DoesNotExist: return Response({"error": "Favorite not found"}, status=status.HTTP_404_NOT_FOUND) except Exception as e: print(f"ERROR in delete request: {e}") return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
4. 优化JavaScript逻辑,处理API返回状态并增强可靠性
更新JS代码,确保请求失败时能正确处理,同时添加页面加载时的按钮状态初始化:
function updateFavoriteButton(songId, isFavorite) { const button = document.querySelector(`button[data-song-id="${songId}"]`); if (!button) return; // 避免找不到元素报错 if (isFavorite) { button.textContent = 'Supprimer des favoris'; button.onclick = () => removeFavorite(songId); } else { button.textContent = 'Ajouter aux favoris'; button.onclick = () => addFavorite(songId); } } async function addFavorite(songId) { try { const response = await fetch(`/api/songs/${songId}/favorite/`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': getCookie('csrftoken'), }, body: JSON.stringify({ song_id: songId }), }); const data = await response.json(); if (response.ok) { updateFavoriteButton(songId, data.is_favorite); } else { console.error('添加失败:', data.error); } } catch (error) { console.error('请求错误:', error); } } async function removeFavorite(songId) { try { const response = await fetch(`/api/songs/${songId}/favorite/`, { method: 'DELETE', headers: { 'X-CSRFToken': getCookie('csrftoken'), }, }); const data = await response.json(); if (response.ok) { updateFavoriteButton(songId, data.is_favorite); } else { console.error('删除失败:', data.error); } } catch (error) { console.error('请求错误:', error); } } // 页面加载时初始化所有按钮状态 async function initFavoriteButtons() { const userIsAuthenticated = {{ user.is_authenticated|lower }}; if (!userIsAuthenticated) return; const buttons = document.querySelectorAll('button[data-song-id]'); for (const button of buttons) { const songId = button.dataset.songId; const response = await fetch(`/api/songs/${songId}/favorite/`); const data = await response.json(); updateFavoriteButton(songId, data.is_favorite); } } // 页面加载完成后执行初始化 document.addEventListener('DOMContentLoaded', initFavoriteButtons); function getCookie(name) { const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) return parts.pop().split(';').shift(); }
内容的提问来源于stack exchange,提问作者Roy Akany
相关产品推荐
相关产品推荐

