Как успешно включать дополнительные поля и обновлять/удалять существующие поля (за один раз) с помощью modelformset_factory/inlineformset_factory

У меня есть готовые модели, добавление новых продуктов, с этим я разобрался, но включение дополнительных полей и обновление/удаление этих новых добавленных полей на одной html форме было такой болью, ниже приведены мои точные коды с небольшими изменениями

#my models.py

class Product(models.Model):
    title = models.CharField()

class ProductSpecification(models.Model):
    name = models.CharField()

class ProductSpecificationValue(models.Model):
    product = models.ForeignKey(Product)
    specification = models.ForeignKey(ProductSpecification)
    value = models.CharField()

class ProductImages(models.Model):
    product = models.ForeignKey(Product)
    images = models.ImageField(upload_to="images/uploads/")

Здесь я покажу мой рабочий код для ДОБАВЛЕНИЯ новых объектов продукта (на всякий случай, если я делаю это неправильно, и это имеет отношение к тому, почему часть обновления не работает)

#MY Добавление формы продукта.py

class AddNewProductForm(forms.ModelForm):
    title = forms.CharField()

    class Meta:
        model = Product
        fields = ['title',]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['title'].widget.attrs.update{'class': 'form-control'})


class AddNewProductSpecForm(forms.ModelForm):

    class Meta:
        model = ProductSpecificationValue
        fields = ['specification', 'value']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['specification'].widget.attrs.update({'class': 'form-control'})
        self.fields['value'].widget.attrs.update({'class': 'form-control'})


class AddNewProductImgForm(forms.ModelForm):
    #this is to upload multiple img in bulk at one go 
    images = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
    
    class Meta:
        model = ProductImages
        fields = ['images',]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['images'].widget.attrs.update({'class': 'form-control'})

#MY Добавление представления продукта.py

@login_required
def add_new_product(request):
    AddNewProductFormSet = modelformset_factory(ProductSpecificationValue, form=AddNewProductSpecForm, extra=3)
    if request.method == "POST":
        product_form = AddNewProductForm(request.POST, request.FILES)
        product_spec_form = AddNewProductFormSet(request.POST, request.FILES, queryset=ProductSpecificationValue.objects.none())
        product_img_form = AddNewProductImgForm(request.POST, request.FILES)
        if product_form.is_valid() and product_spec_form.is_valid() and product_img_form.is_valid:
            product_form = product_form.save(commit=False)
            product_form.save()
            for form in product_spec_form.cleaned_data:
                # this will not allow site crash if the user do not upload all the spec fields
                if form:
                    specification = form['specification']
                    value = form['value']
                    spec = ProductSpecificationValue(product=product_form, specification=specification, value=value)
                    spec.save()

            for img in request.FILES.getlist('images'):
                ProductImages.objects.create(product=product_form, images=img)

            return redirect('.')
    else:
        product_form=AddNewProductForm()
        product_spec_form = AddNewProductFormSet(queryset=ProductSpecificationValue.objects.none())
        product_img_form = AddNewProductImgForm()
    return render(request,'add_new_product.html', {'product_form':product_form,
                          'product_spec_form':product_spec_form, 'product_img_form':product_img_form})

#Мой добавленный продукт add_new_product.html

{% block content %}
<form method="post" action="." enctype="multipart/form-data">
    {% csrf_token %}
    <div class="block">
        <div class="label">{{ product_form.title.label }}:</div>
        <div class="input">{{ product_form.title}}</div>
    </div>

    <div class="block">
        {{ product_spec_form.management_form }}
        {% for form in product_spec_form %}
        <div>
            <div class="place1">
                <div class="label">{{ form.specification.label }}:</div>
                <div class="input">{{ form.specification}}</div>
            </div>
            <div class="place2">
                <div class="label">{{ form.value.label }}:</div>
                <div class="input">{{ form.value}}</div>
            </div>
        </div>
        {% endfor %}
    </div>
    <div class="block">
        <div class="label">{{ product_img_form.images.label }}</div>
        <div class="input">{{ product_img_form.images}}</div>
    </div>

    <button type="submit">Submit</button>
</form>
{% endblock %}

Это все для добавления нового продукта, теперь я покажу мой нерабочий код для добавления дополнительного объекта и обновления/удаления только что добавленного объекта

#MY Обновление формы продукта.py

class UpdateProductForm(forms.ModelForm):
    title = forms.CharField()

    class Meta:
        model = Product
        fields = ['title',]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['title'].widget.attrs.update{'class': 'form-control'})

class UpdateProductSpecForm(forms.ModelForm):

    class Meta:
        model = ProductSpecificationValue
        fields = ['specification', 'value']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['specification'].widget.attrs.update({'class': 'form-control'})
        self.fields['value'].widget.attrs.update({'class': 'form-control'})

class UpdateProductImgForm(forms.ModelForm): 

    class Meta:
            model = ProductImages
            fields = ['images',]
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.fields['images'].widget.attrs.update({'class': 'form-control'})

#My Update product views.py(modelformset_factory)

@login_required
def edit_product(request, id):
    product = get_object_or_404(Product, id=id)
    product_spec = ProductSpecificationValue.objects.filter(product__id=product.id)
    product_img = ProductImages.objects.filter(product__id=product.id)
    UpdateProductImgFormSet = modelformset_factory (ProductImages, form=UpdateProductImgForm, extra=2, can_delete=True)
    UpdateProductSpecFormSet = modelformset_factory (ProductSpecificationValue, form=UpdateProductSpecForm, extra=2, can_delete=True)
    if request.method == "POST":
        product_form = UpdateProductForm(request.POST or None, request.FILES or None, instance=product)
        product_spec_form = UpdateProductSpecFormSet(data=request.POST or None, queryset=product_spec)
        product_img_form = UpdateProductImgFormSet(data=request.FILES, queryset=product_img)
        if product_form.is_valid() and product_spec_form.is_valid() and product_img_form.is_valid:
            product_form = product_form.save(commit=False)
            product_spec_form = product_spec_form.save(commit=False)
            product_img_form = product_img_form.save(commit=False)
            product_form.save()
            product_spec_form.save()
            for img in product_img_form:
                updated_image = ProductImages(product=product_form, images=img)
                updated_image.save()

            return ('some_page')
    else:
        product_form = UpdateProductForm(instance=product)
        product_spec_form = UpdateProductSpecFormSet(query=product_spec)
        product_img_form = UpdateProductImgFormSet(query=product_img)
    return render(request, 'update_product.html', {'product': product, 'product_form':

product_form, 'product_spec_form': product_spec_form, 'product_img_form': product_img_form})

Теперь, используя вышеуказанный modelformset_factory, он не выдает ошибок, отображает форму, как и предполагалось в update_product.html, правильно обновляет модель Parent Product, затем обновляет только первый объект в ProductSpecificationValue Model (оставляя остальные), но ничего не делает с ProductImages Model

#My Update product views.py(inlineformset_factory)

@login_required
def edit_product(request, id):
    product = get_object_or_404(Product, id=id)
    #product_spec = ProductSpecificationValue.objects.filter(product__id=product.id)
    #product_img = ProductImages.objects.filter(product__id=product.id)
    UpdateProductImgFormSet = inlineformset_factory(Product, ProductImages, form=UpdateProductImageForm, extra=2, can_delete=True)
    UpdateProductSpecFormSet = inlineformset_factory(Product, ProductSpecificationValue, form=UpdateProductSpecForm, extra=2, can_delete=True)
    if request.method == "POST":
        product_form = UpdateProductForm(request.POST or None, request.FILES or None, instance=product)
        product_spec_form = UpdateProductSpecFormSet(data=request.POST or None, instance=product)
        product_img_form = UpdateProductImgFormSet(data=request.FILES, instance=product)
        if product_form.is_valid() and product_spec_form.is_valid() and product_img_form.is_valid:
            product_form = product_form.save(commit=False)
            product_spec_form = product_spec_form.save(commit=False)
            product_img_form = product_img_form.save(commit=False)
            product_form.save()
            product_spec_form.save()
            for img in product_img_form:
                updated_image = ProductImages(product=product_form, images=img)
                updated_image.save()

            return ('some_page')
    else:
        product_form = UpdateProductForm(instance=product)
        product_spec_form = UpdateProductSpecFormSet(instance=product)
        product_img_form = UpdateProductImgFormSet(instance=product)
    return render(request, 'update_product.html', {'product': product, 'product_form':

product_form, 'product_spec_form': product_spec_form, 'product_img_form': product_img_form})

Теперь, используя вышеуказанный inlineformset_factory, который кажется более подходящим для обновлений, он выбрасывает всевозможные ошибки и даже не получает возможности отображения в update_product.html. сначала я не знаю какой экземпляр использовать для набора форм, либо [product = get_object_or_404(Product, id=id)] для всего как выше, либо [product_spec = ProductSpecificationValue.objects.filter(product__id=product.id)] и [product_img = ProductImages.objects.filter(product__id=product.id)] соответственно.

Оставив код в том виде, в котором он приведен выше, вы получите следующую ошибку Объект 'set' не имеет атрибута 'append' Переход к их соответствующему экземпляру дает следующую ошибку Объект 'QuerySet' не имеет атрибута 'pk'

Итак, пожалуйста, мне нужна корректировка, где я делаю ошибку, чтобы я мог обновить & также удалить каждый ProductSpecification & ProductImages успешно

Вернуться на верх