Search code examples
htmldjangoinline-formset

Django TypeError 'ProductFeaturesValue' object is not iterable


I want to render multiple inline formsets, the first formset is being rendered but the objects of the second and third formsets are not iterable. I don't understand what I'm doing wrong here. Please help me.

Here's my Model:

class Product(models.Model):
    product_code        = models.CharField(max_length=50, unique=True)
    product             = models.CharField(max_length=100, unique=True)
    keywords            = models.CharField(max_length=50, blank=True, null=True)
    description         = models.CharField(max_length=255, blank=True, null=True)
    detail              = models.TextField(blank=True, null=True)
    cost_price          = models.DecimalField(max_digits=10, decimal_places=2)
    sale_price          = models.DecimalField(max_digits=10, decimal_places=2)
    quantity_per_unit   = models.CharField(max_length=100, blank=True, null=True)
    discontinue         = models.BooleanField(default=False)
    photo               = models.ImageField(upload_to = 'images/product/', default = 'images/product/None/no-img.jpg')
    categories          = models.ManyToManyField('Category', blank=True)
    brand               = models.ForeignKey('Brand', blank=True, null=True, on_delete=models.SET_NULL)
    slug                = AutoSlugField(_('slug'), max_length=50, unique=True, populate_from=('product',))
    created_at          = models.DateTimeField(auto_now_add=True)
    updated_at          = models.DateTimeField(auto_now=True)
    created_by          = models.ForeignKey(User,
                                   related_name="+", blank=True, null=True,
                                   on_delete=models.SET_NULL)
    updated_by          = models.ForeignKey(User,
                                   related_name="+", blank=True, null=True,
                                   on_delete=models.SET_NULL)
    is_deleted          = models.BooleanField(default=False)

    class Meta:
        default_related_name = 'product'


    def __str__(self):
        return self.product

    def __str__(self):
        return "%s (%s)" % (
            self.product,
            ", ".join(categories.category for categories in self.categories.all()),
        )

    def image_tag(self):
        return mark_safe('<img src="{}" height="50"/>').format(self.photo.url)


class Image(models.Model):
    def images_directory_path(instance, filename):
        return '/'.join(['images/product/', str(instance.product_id.id), str(uuid.uuid4().hex + ".png")])

    product     = models.ForeignKey('Product', on_delete=models.CASCADE, related_name='images')
    title       = models.CharField(max_length=50, blank=True, null=True)
    image       = models.FileField(blank=True, null=True, upload_to=images_directory_path, default = None)

    def __str__(self):
        return self.title

class ProductFeaturesValue(models.Model):
    product             = models.ForeignKey('Product', on_delete=models.CASCADE, related_name='featuresvalues')
    feature             = models.ForeignKey('ProductFeatures', blank=True, null=True, on_delete=models.CASCADE)
    defined_value       = models.CharField(max_length=50, blank=True, null=True)
    custom_value        = models.CharField(max_length=50, blank=True, null=True)

class ProductSpecificValue(models.Model):
    product = models.ForeignKey('Product', on_delete=models.CASCADE, related_name='specificvalues')
    start   = models.DateTimeField(blank=True, null=True)
    end     = models.DateTimeField(blank=True, null=True)
    unit    = models.PositiveIntegerField(blank=True, null=True)
    type    = models.CharField(max_length=1, blank=True, null=True)
    percent = models.DecimalField(max_digits=5, decimal_places=2, default=0)
    amount  = models.DecimalField(max_digits=5, decimal_places=2, default=0)

Forms:

class ProductForm(forms.ModelForm):
    class Meta:
        model = Product
        exclude = ()
ImageFormSet    = inlineformset_factory(Product, Image, extra=1, fields='__all__')
FeatureFormSet  = inlineformset_factory(Product, ProductFeaturesValue, extra=1, fields='__all__')
SpecificFormSet = inlineformset_factory(Product, ProductSpecificValue, extra=1, fields='__all__')

View:

class ProductCreateView(CreateView):
    template_name = 'products/index.html'
    model = Product
    form_class = ProductForm
    success_url = reverse_lazy('accpack:products')

    def get(self, request, *args, **kwargs):
        self.object     = None
        form_class      = self.get_form_class()
        form            = self.get_form(form_class)
        image_form      = ImageFormSet()
        feature_form    = ProductFeaturesValue()
        specific_form   = ProductSpecificValue()
        return self.render_to_response(
            self.get_context_data(form=form,
                                  image_form=image_form,
                                  feature_form=feature_form,
                                  specific_form=specific_form,))

Template: When I am removing the second and third form sets, the form render is going well.

{% load crispy_forms_tags %}
{% load static %}
<form action="." method="post">
    {% csrf_token %}
    <div>
        {{form.as_p}}
    </div>
    <fieldset>
        {{ image_form.management_form }}
        {{ image_form.non_form_errors }}
        {% for form in image_form %}
        {{ form.id }}
        <div class="inline {{ image_form.prefix }}">
            {{ form.title.errors }}
            {{ form.title.label_tag }}
            {{ form.title }}
            {{ form.image.errors }}
            {{ form.image.label_tag }}
            {{ form.image }}
        </div>
        {% endfor %}
    </fieldset>
    <fieldset>
        {{ feature_form.management_form }}
        {{ feature_form.non_form_errors }}
        {% for form in feature_form %}
        {{ form.id }}
        <div class="inline {{ feature_form.prefix }}">
            {{ form.feature.errors }}
            {{ form.feature.label_tag }}
            {{ form.feature }}
        </div>
        {% endfor %}
    </fieldset>
    <fieldset>
        {{ specific_form.management_form }}
        {{ specific_form.non_form_errors }}
        {% for form in specific_form %}
        {{ form.id }}
        <div class="inline {{ specific_form.prefix }}">
            {{ form.amount.errors }}
            {{ form.amount.label_tag }}
            {{ form.amount }}
        </div>
        {% endfor %}
    </fieldset>
    <input type="submit" value="Add recipe" class="submit" />
</form>

<script src="{% static 'js/jquery/jquery.formset.js' %}"></script>
<script type="text/javascript">
    $(function() {
        $(".inline.{{ image_form.prefix }}").formset({
            addText: "<i class='far fa-plus-square fa-2x'></i>",
            deleteText: "<i class='pl-3 far fa-minus-square fa-2x'></i>",
            prefix: "{{ image_form.prefix }}",
        })
    })
</script>

Traceback (most recent call last):

  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\core\handlers\base.py", line 202, in _get_response
    response = response.render()
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\response.py", line 105, in render
    self.content = self.rendered_content
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\response.py", line 83, in rendered_content
    return template.render(context, self._request)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\backends\django.py", line 61, in render
    return self.template.render(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\base.py", line 170, in render
    return self._render(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\base.py", line 162, in _render
    return self.nodelist.render(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\base.py", line 938, in render
    bit = node.render_annotated(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\base.py", line 905, in render_annotated
    return self.render(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\loader_tags.py", line 150, in render
    return compiled_parent._render(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\base.py", line 162, in _render
    return self.nodelist.render(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\base.py", line 938, in render
    bit = node.render_annotated(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\base.py", line 905, in render_annotated
    return self.render(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\loader_tags.py", line 62, in render
    result = block.nodelist.render(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\base.py", line 938, in render
    bit = node.render_annotated(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\base.py", line 905, in render_annotated
    return self.render(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\defaulttags.py", line 312, in render
    return nodelist.render(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\base.py", line 938, in render
    bit = node.render_annotated(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\base.py", line 905, in render_annotated
    return self.render(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\defaulttags.py", line 312, in render
    return nodelist.render(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\base.py", line 938, in render
    bit = node.render_annotated(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\base.py", line 905, in render_annotated
    return self.render(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\loader_tags.py", line 192, in render
    return template.render(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\base.py", line 172, in render
    return self._render(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\base.py", line 162, in _render
    return self.nodelist.render(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\base.py", line 938, in render
    bit = node.render_annotated(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\base.py", line 905, in render_annotated
    return self.render(context)
  File "C:\Users\Dell\anaconda3\envs\webapp\lib\site-packages\django\template\defaulttags.py", line 167, in render
    values = list(values)

Exception Type: TypeError at /products/
Exception Value: 'ProductFeaturesValue' object is not iterable

Solution

  • def get(self, request, *args, **kwargs):
        self.object     = None
        form_class      = self.get_form_class()
        form            = self.get_form(form_class)
        image_form      = ImageFormSet()
        feature_form    = ProductFeaturesValue()
        specific_form   = ProductSpecificValue()
    

    feature_form and specific_form aren't formsets. I think you intended to do this instead:

        image_form      = ImageFormSet()
        feature_form    = FeatureFormSet()
        specific_form   = SpecificFormSet()