Search code examples
pythondjangopython-3.xgetmanytomanyfield

Direct assignment to the forward side of a many-to-many set is prohibited. Use songs.set() instead


I'm trying to add a song to the playlist model that I have but it keeps saying

Direct assignment to the forward side of a many-to-many set is prohibited. Use songs.set() instead. My code for Playlist model:

class Playlist(models.Model):
    image = models.ImageField()
    name = models.CharField(max_length=100)
    artist = models.ForeignKey(User, on_delete=models.CASCADE)
    fav = models.BooleanField(default=False)
    songs = models.ManyToManyField(Music)
    slug = models.SlugField(editable=False, default=name)

    def __str__(self):
        return self.name

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

    @classmethod
    def add_music(cls, new_song):
        playlist, created = cls.objects.get_or_create(songs=new_song)
        playlist.songs.add(new_song)

    @classmethod
    def remove_music(cls, new_song):
        playlist, created = cls.objects.get_or_create(songs=new_song)
        playlist.songs.remove(new_song)

My code for Song model:

class Music(models.Model):
    image = models.ImageField()
    name = models.CharField(max_length=100)
    song = models.FileField(upload_to='musics/', blank=False, null=False)
    artist = models.ForeignKey(User, on_delete=models.CASCADE)
    fav = models.BooleanField(default=False)
    slug = models.SlugField(editable=False, default=name)
    lyrics = models.TextField(default="Unknown lyrics")
    genre = models.CharField(
        max_length=2,
        choices=GENRE,
        null=False,
        blank=False,
        default=""
    )

    def __str__(self):
        return self.name

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

my view method that adds the song to the playlist object

def add_to_playlist(request, operation, pk):
    new_song = Music.objects.get(pk=pk)
    if operation == 'add':
        Playlist.add_music(new_song)
    elif operation == 'remove':
        Playlist.remove_music(new_song)
    return redirect('/')

Html code:

 {% for i in music %}
  {% for v in playlist %}
     <a class="dropdown-item" href="{% url 'add_to_playlist' operation='add' pk=i.pk %}">Add to{{v.name}}</a>
   {% endfor %}  
 {% endfor %}

Full error:

Traceback:

File "C:\Users\hooriaishtiaq\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\db\models\query.py" in get_or_create
  538.             return self.get(**kwargs), False

File "C:\Users\hooriaishtiaq\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\db\models\query.py" in get
  408.                 self.model._meta.object_name

During handling of the above exception (Playlist matching query does not exist.), another exception occurred:

File "C:\Users\hooriaishtiaq\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\core\handlers\exception.py" in inner
  34.             response = get_response(request)

File "C:\Users\hooriaishtiaq\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\core\handlers\base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "C:\Users\hooriaishtiaq\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\core\handlers\base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "C:\Users\hooriaishtiaq\workspace\sound\core\views.py" in add_to_playlist
  129.         Playlist.add_music(new_song)

File "C:\Users\hooriaishtiaq\workspace\sound\core\models.py" in add_music
  70.         playlist, created = cls.objects.get_or_create(songs=new_song)

File "C:\Users\hooriaishtiaq\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\db\models\manager.py" in manager_method
  82.                 return getattr(self.get_queryset(), name)(*args, **kwargs)

File "C:\Users\hooriaishtiaq\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\db\models\query.py" in get_or_create
  541.             return self._create_object_from_params(kwargs, params)

File "C:\Users\hooriaishtiaq\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\db\models\query.py" in _create_object_from_params
  575.                 obj = self.create(**params)

File "C:\Users\hooriaishtiaq\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\db\models\query.py" in create
  420.         obj = self.model(**kwargs)

File "C:\Users\hooriaishtiaq\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\db\models\base.py" in __init__
  496.                             _setattr(self, prop, kwargs[prop])

File "C:\Users\hooriaishtiaq\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\db\models\fields\related_descriptors.py" in __set__
  538.             % self._get_set_deprecation_msg_params(),

Exception Type: TypeError at /add_to_playlist/add/11/
Exception Value: Direct assignment to the forward side of a many-to-many set is prohibited. Use songs.set() instead.


Solution

  • Here you lack the Playlist id:

     <a class="dropdown-item" href="{% url 'add_to_playlist' operation='add' pk=i.pk playlist_id=v.id%}">Add to{{v.name}}</a>
    

    In that case you can do:

    def add_to_playlist(request, operation, pk, playlist_id):
        playlist = Playlist.objects.get(pk=playlist_id)
        new_song = Music.objects.get(pk=pk)
        if operation == 'add':
            playlist.songs.add(new_song)
        elif operation == 'remove':
            playlist.songs.remove(new_song)
        return redirect('/')