I'm getting the error "Cannot unpack non-iterable object" in Django app

82 Views Asked by At

I've looked through a few topics with questions almost the same as mine but none of them really helped. The thing is that I'm writing a training Django app where I'm storing info about music albums that I like. Currently I'm adding a form via which I want to add albums to the database (instead of the standard form within "/admin/"). So, the models I have are quite simple:

from django.db import models


class Genre(models.Model):
    name = models.CharField(max_length=30)

    class Meta:
        verbose_name_plural = "genres"

    def __str__(self):
        return self.name


class Album(models.Model):
    author = models.CharField(max_length=100)
    name = models.CharField(max_length=100)
    release_date = models.IntegerField()
    cover = models.ImageField(upload_to="media")
    genres = models.ManyToManyField("Genre", related_name="album")

    def __str__(self):
        return self.name

As you can see, Album and Genre classes are related to each other via ManyToManyField. The form responsible for adding an album to the database looks like this:

from django import forms
from .models import Genre

CHOICES = Genre.objects.all()


class AddAlbumForm(forms.Form):
    author = forms.CharField(label="Album's author", max_length=100)
    name = forms.CharField(label="Album's name", max_length=100)
    release_date = forms.IntegerField(label="Album's release year")
    genres = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=CHOICES)
    cover = forms.FileField()

All in all, the form is operational, except for the line with "genres" variable (if I comment it, the form works just fine). The related view looks like this:

from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.core.paginator import Paginator

from albums.models import Album, Genre
from albums.forms import AddAlbumForm

def add_album(request):
    if request.method == "POST":
        form = AddAlbumForm(request.POST, request.FILES)
        if form.is_valid():
            album = Album()
            album.name = form.cleaned_data.get("name")
            album.author = form.cleaned_data.get("author")
            album.release_date = form.cleaned_data.get("release_date")
            album.cover = form.cleaned_data.get("cover")
            album.genres = form.cleaned_data.get("genres")
            album.save()
            return HttpResponseRedirect("/")
    else:
        form = AddAlbumForm()
    return render(request, "albums/add_album.html", {"form": form})

And finally, the template of the form looks like this:

{% extends "base.html" %}

{% block page_title %}
    <h2>Add an album: </h2>
{% endblock page_title %}

{% block page_content %}
<form action="/add_album/" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {{ form.as_p }}
    <p><input type="submit" value="Submit"></p>
</form>
{% endblock page_content %}

As for the urls, I won't publish the related code since my problem isn't connected to this issue. So, when I try to access the url with the form, I get the following error:

TypeError at /add_album/ Cannot unpack non-iterable Genre object

I want all of the genres to choose from to be displayed in the form so a user could choose multiple options and save them as 'genre' attribute of a related album.

What am I doing wrong, guys? Any help would be greatly appreciated!

3

There are 3 best solutions below

1
rahul.m On

try this

genres = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple, queryset=Genre.objects.all())

you are trying to pass queryset in as choices, which will not work.

refer this

https://docs.djangoproject.com/en/5.0/ref/forms/fields/#fields-which-handle-relationships

0
Pycm On

Try this.

album.genres = form.cleaned_data.get("genres")
album.save()
album.save_m2m()
0
Ryan_Midnight On

So, I'm ready to answer my own question now for the people facing the same issue in future. Two things helped me get this form rolling:

  1. Replacing forms.MultipleChoiceField with forms.ModelMultipleChoiceField

Before:

genres = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=Genre.objects.all())

After:

genres = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple, queryset=Genre.objects.all())
  1. Changing the way of adding genres attribute to an album instance within the view responsible for the form.

Before:

album.genres = form.cleaned_data.get("genres")
album.save()

After:

album.save()
album.genres.set(form.cleaned_data.get("genres"))
album.save()

As soon as I implemented those chnges to the code, the form began working just fine, as expected.