Search code examples
djangomodelmanytomanyfield

django saving list of foreignkey objects to m2m field w/ through model with ordering


I have a html table w/ javascript that allows ordering of rows which passes the track_id list and row order list through a form on POST.

I just added the class PlaylistTrack using through for the model so I can add ordering to the tracks.m2m field. The view I have below works before I added the through model, but now I am not sure how I should save a list of tracks w/ its associated order number since I can't use add() and I must use create(). How should I use create() in my view to save a list of track_id's and associate the order number w/ list? Can I use bulk_create?

models.py:

class Playlist(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
    name = models.CharField(max_length=50)
    tracks = models.ManyToManyField(Track, through='PlaylistTrack')

    def __str__(self):
        return self.name

class PlaylistTrack(models.Model):
    track = models.ForeignKey(Track)
    playlist = models.ForeignKey(Playlist)
    order = models.PositiveIntegerField()

    class Meta:
        ordering = ['order']

views.py:

def add_track(request):
    active_playlist = Playlist.objects.get(id=request.POST.get('playlist_id'))
    add_tracks = request.POST.getlist('track_id')
    if request.POST.get('playlist_id'):
        active_playlist.tracks.add(*add_tracks) # how to use create or bulk_create?
    return redirect("playlists:list_playlists")

Solution

  • Ozgur's answer has you mostly covered. However, you do NOT need to fetch Playlist or Track instances from the database and you CAN use bulk_create:

    def add_track(request):
        playlist_id = request.POST.get('playlist_id')
        track_ids = enumerate(request.POST.getlist('track_id'), start=1)
        PlaylistTrack.objects.bulk_create([
            PlaylistTrack(playlist_id=playlist_id, track_id=track_id, order=i) 
                for i, track_id in track_ids
        ])
        return redirect("playlists:list_playlists")
    

    This reduces the entire procedure to a single db operation where you had (1 + 2n) operations before (n being the number of tracks).