Ahmed Wagdi
Ahmed Wagdi

Reputation: 4421

object is not iterable - Django Modelform

I'm using Model forms to add a row to a table in Django, here are my views and forms file

views.py

def add_ad_mod(request):
    current_user = request.user
    current_ip = get_client_ip(request)
    selected  = Temp.objects.filter(created_by_ip=current_ip).order_by('-created_at')[0]
    selected_category = selected.cat
    selected_town = selected.town
    if request.method == 'POST':
        add_ad_mod_form = AddAdModForm(request.POST, request.FILES, cat=selected_category, loc=selected_town)
        if add_ad_mod_form.is_valid():
            model_instance = add_ad_mod_form.save(commit=False)
            model_instance.created_by = current_user.email
            model_instance.category = selected_category
            model_instance.town=selected_town
            if request.user.is_superuser:
                model_instance.is_active = True
            else:
                model_instance.is_active = False
            add_ad_mod_form.save()
            return redirect('dashboard')
    else:
        add_ad_mod_form = AddAdModForm(cat=selected_category, loc=selected_town)

    context = {
        'add_ad_mod_form': add_ad_mod_form,
        'selected_category': selected_category,
        'selected_town': selected_town,
    }
    return render(request, 'add_ad_mod.html', context)

I am using views.py to send a variable as you see to the form itself

forms.py

class AddAdModForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        current_categ = kwargs.pop('cat')
        current_loc = kwargs.pop('loc')
        super(AddAdModForm, self).__init__(*args, **kwargs)
        self.fields['sub_category'] = forms.ModelChoiceField(label="Sniffer", queryset=SubCate.objects.filter(main_category=current_categ))
        self.fields['sub_location'] = forms.ModelMultipleChoiceField (widget=forms.CheckboxSelectMultiple,label="Sniffer", queryset=SubLoc.objects.filter(main_town=current_loc))

    title = forms.CharField(
        widget=forms.TextInput(
            attrs={
                'placeholder': 'Ad Title here',
                'style': 'width: 100%; max-width: 800px;'
            }
        )
    )

    description = forms.CharField(
        widget=forms.Textarea(
            attrs={
                'placeholder': 'Ad description is here',
                'style': 'width: 100%; max-width: 800px;'
            }
        )
    )

    image = forms.ImageField(required=True)
    image2 = forms.ImageField(required=False)
    image3 = forms.ImageField(required=False)
    image4 = forms.ImageField(required=False)
    image5 = forms.ImageField(required=False)



    address = forms.CharField(max_length=100,
        widget=forms.Textarea(
            attrs={
                'placeholder': 'Detailed Address is here ',
                'style': 'width: 100%; max-width: 800px;'
            }
        )
    )

    class Meta:
        model = Item
        fields = ['title', 'sub_category', 'price', 'description', 'sub_location', 'address', 'image', 'image2', 'image3', 'image4',
                  'image5', 'phone']

And here is my models.py file:

class Category(models.Model):
    category_name = models.CharField(max_length=60)

    def __str__(self):
        return self.category_name


class Town(models.Model):
    town_name = models.CharField(max_length=300)

    def __str__(self):
        return self.town_name


class SubCate(models.Model):
    sub_category_name = models.CharField(max_length=100)
    main_category = models.ForeignKey(Category, on_delete=CASCADE)

    def __str__(self):
        return self.sub_category_name

class SubLoc(models.Model):
    sub_location_name = models.CharField(max_length=100)
    main_town = models.ForeignKey(Town, on_delete=CASCADE)

    def __str__(self):
        return self.sub_location_name


class Item(models.Model):
    category = models.ForeignKey(Category, on_delete=CASCADE)
    sub_category = models.ManyToManyField(SubCate)
    title = models.CharField(max_length=250)
    description = models.TextField(max_length=1200)
    price = models.IntegerField(default=0)
    town = models.ForeignKey(Town, on_delete=CASCADE)
    sub_location = models.ManyToManyField(SubLoc)
    address = models.TextField(max_length=100)
    image = models.ImageField(upload_to='media/', null=False, blank=False)
    image2 = models.ImageField(upload_to='media/', null=True,  blank=True)
    image3 = models.ImageField(upload_to='media/', null=True, blank=True)
    image4 = models.ImageField(upload_to='media/', null=True, blank=True)
    image5 = models.ImageField(upload_to='media/', null=True, blank=True)
    created_by = models.CharField(max_length=600)
    created_at = models.DateField(auto_now=True)
    phone = models.IntegerField(default=0)
    is_active = models.BooleanField(default=False)
    is_deleted = models.BooleanField(default=False)

    def __str__(self):
        return self.title

Despite the items is added to the database and if I reloaded my website I can see the item posted - it gives me the error instead of redirecting normally (The error is not in the page user redirected to after submitting, as I can browse into it normally )

Traceback:

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

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

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

File "C:\Users\lito\Desktop\DJ\JEHLUM - Copy - Copy\web_site\views.py" in add_ad_mod
  219.             add_ad_mod_form.save()

File "C:\Users\lito\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\forms\models.py" in save
  457.             self._save_m2m()

File "C:\Users\lito\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\forms\models.py" in _save_m2m
  439.                 f.save_form_data(self.instance, cleaned_data[f.name])

File "C:\Users\lito\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\db\models\fields\related.py" in save_form_data
  1619.         getattr(instance, self.attname).set(data)

File "C:\Users\lito\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\db\models\fields\related_descriptors.py" in set
  947.             objs = tuple(objs)

Exception Type: TypeError at /add_ad/mod/
Exception Value: 'SubCate' object is not iterable

Upvotes: 1

Views: 1664

Answers (1)

Daniel Roseman
Daniel Roseman

Reputation: 600059

As I mentioned in the comments, it looks like both of these fields should be multiple choice, since they are both representing ManyToManyFields in the model.

self.fields['sub_category'] = forms.ModelMultipleChoiceField(label="Sniffer", queryset=SubCate.objects.filter(main_category=current_categ))
self.fields['sub_location'] = forms.ModelMultipleChoiceField (widget=forms.CheckboxSelectMultiple,label="Sniffer", queryset=SubLoc.objects.filter(main_town=current_loc))

Also as mentioned, you need to call save on the model instance, and save_m2m on the form:

if add_ad_mod_form.is_valid():
    model_instance = add_ad_mod_form.save(commit=False)
    ...
    model_instance.save()
    add_ad_mod_form.save_m2m()
    return redirect('dashboard')

Upvotes: 2

Related Questions