How to upload multiple images using modelformset - Django
Asked Answered
Y

1

1

I am trying to create a system which allows a user to be able to add a new Vegetable object, upload a thumbnail and multiple images and files - all from the AddVegetable page, and be able to output this easily as the vegetable types are filtered to display on different pages

I'm trying to achieve this with the code below but it won't work and I cant figure out why exactly, as it stands I'm getting a KeyError 'image', but I don't know why. I am going about this the right way at all?

I am ignoring image processing for the moment,

models.py

class Vegetable(models.Model):
    title = models.CharField(max_length=0)
    category = models.CharField(max_length=50,
                                choices=CATEGORY_CHOICES)
    description = models.CharField(max_length=1000, null=True)
    thumbnail = models.ImageField(upload_to = 'uploaded_images/')
    attachments = models.FileField(upload_to = uploaded_files/)
    date_added = models.DateTimeField('Date Added', default=datetime.datetime.now)

class VegetableImage(models.Model):
    vegetable = models.ForeignKey(Vegetable, default=None)
    image = models.ImageField(upload_to='images/vegetable',
                             verbose_name='image',)

forms.py

Class AddVegetable(ModelForm):
    class Meta:
          model = Vegetable
          fields = ['title', 'category', 'description', 'thumbnail',
                    'images', 'attachments', 'date_added',]

class ImageForm(ModelForm):
    image = ImageField(label='image')
    class Meta:
        model = VegetableImage
        fields = ['image',]

views.py

def AddVegetable(request):

ImageFormSet = modelformset_factory(VegetableImage,
                                    form=ImageForm, extra=4)

if request.method == 'POST':

    VegetableForm = AddVegetableForm(request.POST)
    formset = ImageFormSet(request.POST, request.FILES,
                           queryset=VegetableImage.objects.none())

    if VegetableForm.is_valid() and formset.is_valid():

        VegetableForm.save()

        for form in formset.cleaned_data:
            image = form['image']
            picture = VegetableImage(vegetable=VegetableForm, image=image)
            picture.save()

        return HttpResponseRedirect('/vegetable/')
    else:
        print (VegetableForm.errors, formset.errors)

else:
    VegetableForm = AddVegetableForm()
    formset = ImageFormSet(queryset=VegetableImage.objects.none())

return render(request, 'vegetable/add.html',
              {'VegetableForm': VegetableForm, 'formset': formset},
              context_instance=RequestContext(request))

template.html

<form action="/vegetable/add/" method="POST" enctypr="multipart/form-data"> 
{% csrf_token %}
    <table>

            <p> {{ VegetableForm.as_ul }}</p>

            {{ formset.management_form }}
            {% for form in formset %}
            {{ form }}
            {% endfor %}

    </table>

    <input type="submit" value="Post">
</form>
Yellowhammer answered 1/10, 2015 at 13:43 Comment(1)
As an aside, your code will be clearer if you use title case e.g. AddVegetableForm for the form class, and lower case e.g. vegetable_form for the form instance. Using VegetableForm = AddVegetableForm(request.POST) is confusing to other users, because VegetableForm looks like a class, but it's an instance.Gardell
G
1

When you call VegetableForm.save(), it returns the instance, so you should do

vegetable = AddVegetableForm.save()

The docs on saving objects in a formset suggest calling formset.save(), which returns a list of instances. You can call save() with commit=False, set the vegetable, then save to the database.

images = formset.save(commit=False)
for image in images:
    image.vegetable = vegetable
    image.save()
Gardell answered 1/10, 2015 at 13:51 Comment(7)
what eactlydoes your second block replace?Yellowhammer
It replaces the code that's dealing with the valid formset, i.e. the loop for form in formset.cleaned_data:Gardell
Thanks, things seem to be working correctly, but the images are not being uploaded.Yellowhammer
You have a typo in your form tag - enctypr should be enctype.Gardell
#I also have a view for displaying the green category to /vegetable/green-vegetable/ - but it needs to display the images uploaded to that vegetable. How should I do this? class GreenView(generic.ListView): model = Vegetable template_name = 'vegetable/green.html' context_object_name = 'green_vegetable' def get_queryset(self): return Vegetable.objects.filter(category__startswith="Green")Yellowhammer
Please don't put long code snippets in the comments, it's very difficult to read, especially for Python as you lose the indentation. I think you'd be better to ask a new question -- displaying the items is a different problem to creating them. Remember to explain what isn't working, as well as showing the code.Gardell
Thanks for the adviceYellowhammer

© 2022 - 2024 — McMap. All rights reserved.