bayman
bayman

Reputation: 1719

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")

Upvotes: 2

Views: 768

Answers (2)

user2390182
user2390182

Reputation: 73470

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).

Upvotes: 1

Ozgur Vatansever
Ozgur Vatansever

Reputation: 52163

You need to fetch Track objects that will be added:

Track.objects.filter(pk__in=add_tracks)

--

Since order field must be populated, you can't use .add() on M2M field. You gotta create objects by yourself:

def add_track(request):
    playlist = Playlist.objects.get(id=request.POST.get('playlist_id'))
    for i, track_id in enumerate(request.POST.getlist('track_id'), start=1):
        track = Track.objects.get(pk=track_id)
        PlaylistTrack.objects.create(track=track, playlist=playlist, order=i)
    return redirect("playlists:list_playlists")

Upvotes: 1

Related Questions