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>
tim
  • 379
  • 2
  • 5
  • 14
  • 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. – Alasdair Oct 01 '15 at 13:53

1 Answers1

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()
Alasdair
  • 298,606
  • 55
  • 578
  • 516
  • what eactlydoes your second block replace? – tim Oct 01 '15 at 14:39
  • It replaces the code that's dealing with the valid formset, i.e. the loop `for form in formset.cleaned_data:` – Alasdair Oct 01 '15 at 14:49
  • Thanks, things seem to be working correctly, but the images are not being uploaded. – tim Oct 01 '15 at 15:44
  • 2
    You have a typo in your form tag - `enctypr` should be `enctype`. – Alasdair Oct 01 '15 at 15:50
  • #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")` – tim Oct 02 '15 at 09:33
  • 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. – Alasdair Oct 02 '15 at 09:42
  • Thanks for the advice – tim Oct 02 '15 at 11:30