I'm trying to do Django class-based CreateView inline formsets. I have a product model and a productImage model and productImage is inline everything looks fine but after i submit my product the images that are selected in formset will not save. Here is my code
models.py:
class Product(models.Model):
name=models.CharField(max_length=200, db_index=True),
slug=models.SlugField(max_length=200, db_index=True, allow_unicode=True),
description=models.TextField(blank=True),
vector_column=SearchVectorField(null=True, editable=False),
meta={"indexes": (GinIndex(fields=["vector_column"]),)}
category = models.ForeignKey(Category, related_name='products',
on_delete=models.CASCADE)
price = models.DecimalField(max_digits=10, decimal_places=2)
available = models.BooleanField(default=True)
def get_absolute_url(self):
return reverse('shop:product_detail', args=[self.id, self.slug])
class ProductImage(models.Model):
product = models.ForeignKey(Product, default=None, on_delete=models.CASCADE)
image = models.ImageField(upload_to='products/%y/%m/%d')
def __str__(self):
return self.product.name
views.py
class ProductCreate(StaffAccessMixin, ProductFormValidMixin, ProductFieldsMixin,
CreateView):
model = Product
def get_context_data(self, **kwargs):
context = super(ProductCreate, self).get_context_data(**kwargs)
if self.request.POST:
context['product_image_formset'] = ProductImageFormset(self.request.POST)
else:
context['product_image_formset'] = ProductImageFormset()
return context
template_name = 'products/product-create-update.html'
ProductFormValidMixin:
class ProductFormValidMixin():
def form_valid(self, form):
context = self.get_context_data()
product_image_formset = context['product_image_formset']
if self.request.user.is_superuser:
self.obj = form.save()
if product_image_formset.is_valid():
product_image_formset.instance = self.obj
product_image_formset.save()
return redirect('administration:product-update', pk=self.obj.pk)
else:
self.obj = form.save(commit=False)
self.obj.available = False
return super().form_valid(form)
ProductFieldsMixin:
class ProductFieldsMixin():
def dispatch(self, request, *args, **kwargs):
if request.user.is_superuser:
self.fields = ["name",
"slug",
"description",
"category",
"price",
"available",
"image",
]
else:
raise Http404
return super().dispatch(request, *args, **kwargs)
forms.py:
from django.forms.models import inlineformset_factory
from .models import Product, ProductImage
ProductImageFormset = inlineformset_factory(Product, ProductImage, fields=('image',),
extra=3)
Formset template:
<h5 class="text-info">Add Product Metas</h5>
{{ product_image_formset.management_form|crispy }}
{% for form in product_image_formset.forms %}
<tr class="{% cycle 'row1' 'row2' %} formset_row-{{product_image_formset.prefix }}">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field|as_crispy_field }}
</td>
{% endfor %}
</tr>
{% endfor %}
Everything is ok and my product form is saved after submit but the images i select in my form set won't save
Problem solved!
This code will work for other types of data but not for images and files to solve this problem get_context_data function should be like this:
def get_context_data(self, **kwargs):
context = super(ProductCreate, self).get_context_data(**kwargs)
if self.request.POST:
context['product_image_formset']=ProductImageFormset(self.request.POST,
**self.request.FILES**)
else:
context['product_image_formset'] = ProductImageFormset()
return context
In my first code self.request.FILES was missing :) and because of that images couldn't saved