1

I am working on a django web application. the application holds two forms in the same page. The first form is for uploading an image and the second form is a description for the image. After the user uploads an image and clicks on the upload image button, an image classifier should classify the image and autofill some parts of the second form.

This is my code so far

models.py

class Item(models.Model):
    title = models.CharField(max_length=100)
    color = models.CharField(max_length=100)
    img = models.ImageField(upload_to='item/img/', null=False, blank=False)

    def __str__(self):
        return self.title

    def delete(self, *args, **kwargs):
        self.img.delete()
        super().delete(*args, **kwargs)

forms.py

from .models import Item

class ItemImage(forms.ModelForm):
    class Meta:
        model = Item
        fields = ('img',)

class ItemForm(forms.ModelForm):
    class Meta:
        model = Item
        fields = ('title', 'color')

views.py

from .forms import ItemForm, ItemImage
from .models import Item

def upload_item(request):
    if request.method == 'POST':
        form_img = ItemImage(request.POST, request.FILES)
        if form_img.is_valid():
            form_img.save()

        form_des = ItemForm(request.POST, request.FILES)
        if form_des.is_valid():
            form_des.save()
            return redirect('item_list')
    else:
        form_img = ItemImage()
        form_des = ItemForm()

    return render(request, 'upload_item.html', {'form_img': form_img, 'form_des': form_des})

upload_item.html template

{% extends 'base.html' %}

{% load crispy_forms_tags %}

{% block title %} upload_item {% endblock title %}

{% block content %}
<div class="row justify-content-center">
  <div class="col-6">
    <h2>Upload item</h2>

    <div class="card mb-5 mt-1">
      <div class="card-body">
        <form method="post" enctype="multipart/form-data">
          {% csrf_token %}
          {{form_img|crispy}}
          <button type="submit" class='btn btn-primary'>upload img</button>
        </form>
      </div>
    </div>

    <div class="card mb-5 mt-1">
      <div class="card-body">
        <form method="post" enctype="multipart/form-data">
          {% csrf_token %}
          {{form_des|crispy}}
          <button type="submit" class='btn btn-primary'>Save item</button>
        </form>
      </div>
    </div>

  </div>
</div>

{% endblock content %}

The problem I am facing is after uploading an image, when I press the upload image button, the page reloads and I have to start all over again. I guess when I press the button the the page is trying to save the form. how do I rectify this?

[NOTE] I have not written the image classification code yet. I will be writing that code under upload_item function in views.py once I solve this problem

[EDITS]

I made some changes to the template file. Now I am able to upload the image and run the classifier on the image.

These are the changes I made

upload_item.html template

{% block content %}
<div class="row justify-content-center">
  <div class="col-6">
    <h2>Upload item</h2>

    <div class="card mb-5 mt-1">
      <div class="card-body">
        <form action="{{ request.build_absolute_uri }}image_classification/" method="POST" enctype="multipart/form-data">
          {% csrf_token %}
          <input id="search" , type="file" name="file"/>
          <input class='btn btn-primary' , type="submit" value="Upload image" />
        </form>
      </div>
    </div>

    <div class="card mb-5 mt-1">
      <div class="card-body">
        <form method="post" enctype="multipart/form-data">
          {% csrf_token %}
          {{form_des|crispy}}
          <button type="submit" class='btn btn-primary'>Save item</button>
        </form>
      </div>
    </div>

  </div>
</div>

{% endblock content %}

views.py

def handle_uploaded_file(file, filename):
    if not os.path.exists('media/classification/'):
        os.mkdir('media/classification/')

    with open('media/classification/' + filename, 'wb+') as destination:
        for chunk in file.chunks():
            destination.write(chunk)

def image_classification(request):

    form = ItemForm()

    cascade_path = "./classifier.h5"
    classifier = load_model(cascade_path)

    if request.method == 'POST':
        handle_uploaded_file(request.FILES['file'], str(request.FILES['file']))

        img = np.expand_dims(cv2.resize(cv2.imread(os.path.join('./media/classification/', str(request.FILES['file']))), (170, 100)), axis=0)
        pred_class = str(classifier.predict_classes(img)[0])
        print(pred_class)

        form.fields['title'].widget.attrs['value'] = pred_class
        return render(request, 'upload_item.html', {'form': form})

    return HttpResponse("Failed")

I added the code @Alexander Strakhov suggested. This is the result I am getting. enter image description here

What am I doing wrong?

Thanks in advance

3
  • Have you tried using django formsets? Commented Jan 3, 2019 at 6:11
  • you can put both forms inside a single <form> tag with a single submit button. Commented Jan 3, 2019 at 6:34
  • I did some changes to the code. When I press the upload image button the classifier runs on the image and an output is generated. But I am not able to add that output to the form field. Please check the question. I have edited the question with the changes Commented Jan 3, 2019 at 7:32

1 Answer 1

1

When you press "upload image" button , you send a request to upload_item url. There your views:

1) bind the ItemImage form to request.Post, which is a QueryDict with csrf token and to request.FILES with your uploaded file.

2) save the image form (assuming that an image was valid).

3) bind ItemForm to the same request data as ItemImage.

4) render the upload_item.html again with bounded form_img and form_des in context.

Consult Django documentation: https://docs.djangoproject.com/en/2.1/topics/forms/#the-view

The reason why you get an empty page when you press "upload image" is that:

a) both fields Title and Color are bound to an empty value in QueryDict, as pressing "upload image" does not submit that second form, which Title and Color are part of.

b) It seems that Django does not bind a form to a file when rendering it back, leaving a form blank (I would consult community on this). I would suggest using AJAX here to validate you image and run classification later. Using AJAX would keep your image on the page, as there is no page refresh, allowing you to submit both forms later with "Save Item" button.

Edit

After seeing your updated code, I would recommend doing the following for starters:

Replace form.fields['title'].widget.attrs['placeholder'] = pred_class

With

form.fields['title'].widget.attrs['value'] = pred_class
return render(request, 'upload_item.html', {'form_des': form})
Sign up to request clarification or add additional context in comments.

7 Comments

Hi! thanks for the reply. I made a few changes to the code that allows me to upload the image and run the classifier and get a prediction. I dont know how to add this prediction to the text field in the second form. Please look at the edited code I added.
It seems you do not render anything besides "Failed" message in your edited code. I would suggest rendering a page with an updated widget like you already do.
even if I were to render the page again, I dont know how to add the output of the classifier as text to the input field title. Right now, when I render the page after the classifier runs, nothing happens
Give a try to the code in the answer. Hopefully it will work.
Hi. I added the code. But when I upload the image and click the button, the second form disappears. only the save item button remains. Please check the question. I have edited it
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.