Reputation: 964
I am working on a django application. This application hold two forms. In the first form (image_form) the user can upload an image. In the second form (image_description) the user can fill some description about the image. when the image is uploaded in the first form, an image classifier runs on the image tries to fill parts of the image_description form. Once the second form is submitted the item is displayed in a new page (item_list). This is the urlpatterns for the application.
urls.py
urlpatterns = [
path('', views.index, name='home'),
path('accounts/', include('django.contrib.auth.urls')),
path('signup/', views.signup, name='signup'),
path('items/', views.item_list, name='item_list'),
path('items/upload/description/', views.upload_item, name='upload_item'),
path('items/<int:pk>/', views.delete_item, name='delete_item'),
path('items/upload/image_classification/', views.image_classification, name='image_classification'),
]
here item_list is the page where all items are displayed. This page is displayed once the image_description form is submitted.
upload_item page contains both the forms.
image_classification runs when upload image button in the first form is clicked. This happens in the upload_item page.
views.py
def item_list(request):
items = Item.objects.all()
return render(request, 'item_list.html', {'items':items})
def upload_item(request):
if request.method == 'POST':
form_des = ItemForm(request.POST, request.FILES)
if form_des.is_valid():
form_des.save()
return redirect('item_list')
else:
form_des = ItemForm()
return render(request, 'upload_item.html', {'form_des': form_des})
def image_classification(request):
form_des = ItemForm()
if request.method == 'POST':
if 'file' in request.FILES:
handle_uploaded_file(request.FILES['file'], str(request.FILES['file']))
img = np.expand_dims(cv2.resize(cv2.imread(os.path.join('./media/item/img/', str(request.FILES['file']))), (170, 100)), axis=0)
cat_prediction = cat_classifier.predict_classes(img)[0]
pattern_prediction = pat_classifier.predict_classes(img)[0]
form_des.fields['title'].widget.attrs['value'] = cat_prediction
form_des.fields['pattern'].widget.attrs['value'] = pattern_prediction
form_des.fields['user'].widget.attrs['value'] = request.user
form_des.fields['user'].widget.attrs['readonly'] = True
return render(request, 'upload_item.html', {'form_des': form_des})
else:
return redirect('upload_item')
upload_item.html template
<div class="container">
<div class="row justify-content-center">
<div class="col-7">
<center>
<h2>Upload image</h2>
<!-- <div class="row justify-content-center"> -->
<div class="upload-btn-wrapper">
<form action="{% url 'image_classification' %}" method="POST" enctype="multipart/form-data" data-url="image_classification/" class="my_form">
{% csrf_token %}
<input type="file" name="file" id="file" class="inputfile" multiple/>
<label for="file" class="btn btn-outline-dark btn-lg mt-5 select">Choose a file</label>
<input class='btn btn-primary btn-lg btn-block upload_image_button' type="submit" value="Upload image" disabled/>
</form>
</div>
<!-- </div> -->
<center>
<p class='font-weight-bold mt-5 mb-5 text-danger'>Step: 1 of 2</p>
</center>
<div class="separation"></div>
<h2>Item description</h2>
</center>
<div class="card mb-2 mt-3">
<div class="card-body">
<form method="post" enctype="multipart/form-data" action="{% url 'upload_item' %}" id='item_des'>
{% csrf_token %}
{{form_des.title|as_crispy_field}}
{{form_des.pattern|as_crispy_field}}
{{form_des.color|as_crispy_field}}
{{form_des.user|as_crispy_field}}
<button type="submit" class='btn btn-primary btn-lg btn-block save_btn'>Save item</button>
</form>
</div>
</div>
<center>
<p class='font-weight-bold mt-2 mb-5 text-danger'>Step: 2 of 2</p>
</center>
</div>
</div>
</div>
My problem is when the upload image button is clicked in the first form the url changes from items/upload/description/
to items/upload/image_classification/
and the page reloads with the auto filled sections in the second form.
I want to use AJAX to auto-fill the second form without reloading the page but am not sure how to do it. I followed a few tutorials but am not able to achieve this.
Please help me
Thank you
[EDIT1] Based on Adeel Siddiqui's answer, I made a few changes to views.py
views.py
def image_classification(request):
form_des = ItemForm()
user =str(request.user)
if request.method == 'POST':
if 'file' in request.FILES:
handle_uploaded_file(request.FILES['file'], str(request.FILES['file']))
img = np.expand_dims(cv2.resize(cv2.imread(os.path.join('./media/item/img/', str(request.FILES['file']))), (170, 100)), axis=0)
cat_prediction = cat_classifier.predict_classes(img)[0]
pattern_prediction = pat_classifier.predict_classes(img)[0]
form_des.fields['title'].widget.attrs['value'] = cat_prediction
form_des.fields['pattern'].widget.attrs['value'] = pattern_prediction
form_des.fields['user'].widget.attrs['value'] = request.user
form_des.fields['user'].widget.attrs['readonly'] = True
context = {
'tops_prediction' :tops_prediction,
'pattern_prediction':pattern_prediction,
'user' :user,
}
return HttpResponse(json.dumps(context))
else:
return redirect('upload_item')
How do I access this context from fillImageDescriptionText in jquery and auto-fill the second form in the upload_item page?
[EDIT-2] screenshot of the output after using the updated answer
Upvotes: 0
Views: 183
Reputation: 706
You could prevent the default event happening when the form is submitted. Suppose your image classification form has the id image-classify-form, then:
$("#image-classify-form").submit(function(event) {
event.preventDefault();
imageClassifyAjax();
}
where your imageClassifyAjax function does this (i previously forgot to create the FormData object on line 2 which was causing the POST request to fail):
function imageClassifyAjax() {
let $form = $("#image-classify-form");
let form_data = new FormData($form[0]);
$.ajax({
url: $form.attr('action'),
type: $form.attr('method'),
data: form_data,
processData: false,
contentType: false,
dataType: 'json',
success: function (data) {
fillImageDescriptionText(data);
},
error: function (xhr) {
console.log("Something went wrong: " + xhr.responseText);
}
});
}
and fillImageDescriptionText uses the json data returned by the view to fill up the form for the image description. So you have to modify what your view does on the POST method. You have to return an HttpResponse instead of doing a render template:
import json
from django.http import HttpResponse
def image_classification(request):
form_des = ItemForm()
if request.method == 'POST':
...
...
return HttpResponse(json.dumps({'title': cat_prediction, 'pattern': pattern_prediction, 'user': request.user.email}))
fillImageDescriptionText receives this json object as input, so you can basically:
function fillImageDescriptionText(data) {
$("#item_des #id_title").val(data.title);
$("#item_des #id_pattern").val(data.pattern);
$("#item_des #id_user").val(data.user);
$("#item_des #id_user").prop('readonly', true);
}
where id_ is the id generated for the elements by django crispy forms or whatever package you are using for the forms. It is mostly prefixed with an "id_". You can check it up by inspecting elements from the browser.
Upvotes: 1