Take user input from multiple forms in the same page - python

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.
What am I doing wrong?
Thanks in advance

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})

Related

in Django : Why nothing happens when I try to Create new note with the html form in the notes.html page

I've just started my first app with Django by following a video on YouTube.
The app is a students dashboard with 8 tools and features to help the student to make note, search for help, save books, etc.
When I follow the steps I get stuck in the creation of a new note but note from the admin side but from the actual notes template that has a crispy form and a create button.
The program is supposed for writing a title and description (the content of the note) and then press the create button. When I try to click, nothing is happening.
#This is the view.py page in the dashboard app :
from django.shortcuts import render
from . forms import *
from django.contrib import messages
# Create your views here.
def home(request):
return render(request, 'dashboard/home.html')
def notes(request):
if request.method == "POST":
form = NotesForm(request.POST)
if form.is_valid():
notes = Notes(user=request.user,title=request.POST['title'],description=request.POST['description'])
notes.save()
messages.success(request,f"Notes Added from {request.user.username} Successfully")
else:
form = NotesForm()
notes = Notes.objects.filter(user=request.user)
context = {'notes':notes,'form':form}
return render(request,'dashboard/notes.html',context)
and this is the notes.html page in the dashboard app > template folder > dashboard folder :
{% extends 'dashboard/base.html' %}
<!-- Load the static files here -->
{% load static %} {% load crispy_forms_tags %}
<!-- loading the crispy files must be here so now i write anything to
create some space and lines here we go baby lets add more -->
{% block content %}
<div class="container">
<div class="row">
{% for note in notes %}
<div class="col-md-3">
<a href="#">
<div class="card">
<div class="card-header">{{note.title}}</div>
<div class="card-body">{{note.description|slice:"0:100"}}</div>
<div class="card-footer mt-auto">
<i class="fa fa-trash fa-2x"></i>
</div>
</div>
</a>
</div>
{% endfor %}
<br /><br />
</div>
</div>
<br /><br />
<div class="container">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Create Notes</legend>
</fieldset>
{% crispy form %}
<div class="form-group">
<button href="" class="btn btn-outline-info" type="submit">Create</button>
</div>
</form>
</div>
{% endblock content %}
so, this the models.py file too
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Notes(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
description = models.TextField()
def __str__(self):
return self.title
class Meta:
verbose_name = "notes"
verbose_name_plural = "notes"
in case the problem is not here, please take a look on the inside this forms.py
from dataclasses import fields
from django import forms
from . models import *
class NotesForm(forms.ModelForm):
class Meta:
model = Notes
fields = ['title', 'description']
and .. urls.py
from django.urls import path
from . import views
urlpatterns = [
path('',views.home, name='home'),
path('notes',views.notes, name="notes")
]
There are many minor mistakes in the code:
It is {{form|crispy}} to display the form not {% crispy form %}.
You are also not using HttpResponseRedirect[django-doc] for redirecting, after saving form from POST data, you should always return an HttpResponse, its a good practice.
Try below code:
Notes.html
{% block content %}
{% load crispy_forms_tags %}
<div class="container">
<div class="row">
{% for note in notes %}
<div class="col-md-3">
<a href="#">
<div class="card">
<div class="card-header">{{note.title}}</div>
<div class="card-body">{{note.description|slice:"0:100"}}</div>
<div class="card-footer mt-auto">
<i class="fa fa-trash fa-2x"></i>
</div>
</div>
</a>
</div>
{% endfor %}
<br /><br />
</div>
</div>
<br /><br />
<div class="container">
<form method="POST" action="{% url 'notes' %}" novalidate>
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Create Notes</legend>
</fieldset>
{{form|crispy}}
<input type="submit" value="Create">
</form>
</div>
{% endblock content %}
views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.shortcuts import render
from . forms import *
from django.contrib import messages
from django.urls import reverse
def home(request):
return render(request, 'dashboard/home.html')
def notes(request):
if request.method == "POST":
form = NotesForm(request.POST)
if form.is_valid():
title = form.cleaned_data['title']
descrip = form.cleaned_data['description']
notes = Notes(
user=request.user, title=title, description=descrip)
notes.save()
messages.success(
request, f"Notes Added from {request.user.username} Successfully")
return HttpResponseRedirect(reverse('thanks'))
else:
form = NotesForm()
notes = Notes.objects.filter(user=request.user)
context = {'notes': notes, 'form': form}
return render(request, 'dashboard/notes.html', context)
def thanks(request):
return render(request, 'dashboard/thanks.html')
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='home'),
path('notes/', views.notes, name="notes"),
path('thanks/', views.thanks, name='thanks')
]
thanks.html
<body>
<h2>The form successfully submitted</h2>
</body>
Your forms.py can be remain same as it is.

go back to previous page in django

I want to save the data in the form and go back to previous page when I click on 'Add' button. It's showing some errors. Can anyone suggest the correct way to do it?
template.html
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Add your own Category </h3>
</div>
<div class="panel-body">
<form method="POST" class="post-form">
{% csrf_token %}
{{ form.as_p }}
</div>
</div>
</div>
<input type="hidden" name="next" value="{{ request.path }}">
<button type="submit" class="save btn btn-default" >Add</button>
</form>
views.py
class CustomCategory(LoginRequiredMixin,CreateView):
model = Category
form_class = CategoryForm
def form_valid(self, form):
obj = form.save(commit=False)
def category(request):
next = request.POST.get('next', '/')
return render (request,HttpResponseRedirect(next))
Instead of adding the redirect as an element of the form, why don't you use the success_url field on the view, or if the URL you need to go to depends on an object, use the get_success_url method on the view?
https://docs.djangoproject.com/en/2.0/ref/class-based-views/mixins-editing/

Django - Simple User Register not working [Not saving to DB]

I have simple user registration page, but its not saving to the database.
Issue: Register form displays, upon clicking submit button, doesn't do anything! Stays there (it should show a HttpResponse('User Registered') And save to db
I've know I'm missing something, but what? ideas please.
Views.py
from django.shortcuts import render
from django.views.generic import CreateView
from website.forms import RegisterUserForm
from django.http import HttpResponseForbidden
# Create your views here.
class RegisterUserView(CreateView):
form_class = RegisterUserForm
template_name = "account/register.html"
# Overide dispatch func - check if the user is authenticated or return forbidden
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated():
return HttpResponseForbidden()
return super(RegisterUserView, self).dispatch(request, *args, **kwargs)
# Overide form valid method behaviour
def form_valid(self, form):
# False commit - don't save to db, locally cleaning users password first
user = form.save(commit=False)
user.set_password(form.cleaned_data['password'])
user.save();
# Change to redirect - later one!
return HttpResponse('User Registered')
def Home(request):
return render(request, 'account/home.html')
Register.html
{% extends 'base.html' %}
{% block title %}
Register
{% endblock %}
{% block head %}
{% load static %}
<link href="{% static 'forms.css' %}" rel="stylesheet">
{% endblock %}
{% block heading %}
<h1>Register</h1>
{% endblock %}
{% block body %}
<div class="bg-contact2" style="background-image: url('images/bg-01.jpg');">
<div class="container-contact2">
<div class="wrap-contact2">
<form class="contact2-form validate-form">
<span class="contact2-form-title">
<h2> Teacher's Register </h2>
</span>
{% csrf_token %}
{{ form.as_p }}
<div class="container-contact2-form-btn">
<div class="wrap-contact2-form-btn">
<div class="contact2-form-bgbtn"></div>
<button class="contact2-form-btn">
Send Your Message
</button>
</div>
</div>
</form>
</div>
</div>
</div>
<!--
<h2>FORM</h2>
<form action="{% url 'register' %}" class="contact2-form validate-form" method="post ">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Register">
</form> -->
{% endblock %}
forms.py
from django import forms
from django.contrib.auth.models import User
class RegisterUserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
password2 = forms.CharField(widget=forms.PasswordInput)
class Meta:
# Connect to the user model and select which fields we want input for user model
model = User
fields = ['username', 'email']
# Validate password matched / and clean them
def clean_password2(self):
cd = self.cleaned_data
if cd['password2'] != cd['password']:
raise ValidationError("Password don't match")
return cd['password2']
I think you are missing the type in your button tag:
<form method="POST">
<span class="contact2-form-title">
<h2> Teacher's Register </h2>
</span>
{% csrf_token %}
{{ form.as_p }}
<div class="container-contact2-form-btn">
<div class="wrap-contact2-form-btn">
<div class="contact2-form-bgbtn"></div>
<button type="submit" class="contact2-form-btn">
Send Your Message
</button>
</div>
</div>
</form>
form tag should have an attribute action to tell where to take the data of your form to. So in Register.html you should add
action="{% url 'nameofyourviewgiveninurls.py'%}"
in your form tag

In Django, form isn't rendered on browser

I have started learning django, but I can't understand how to render form.
This is form.py I have made.
from django import forms
class TweetForm(forms.Form):
text = forms.CharField(widget = forms.Textarea(attrs = {'rows' : 1, 'cols' : 85}), max_length = 160)
country = forms.CharField(widget = forms.HiddenInput())
This is code snippet in views.py.
from .forms import TweetForm
class Profile(View):
def get(self, request, username):
params = dict()
user = User.objects.get(username = username)
tweets = Tweet.objects.filter(user = user)
params["user"] = user
params["tweets"] = tweets
form = TweetForm
return render(request, 'profile.html', {'params' : params, 'form' : form})
This is html file and it must render form I have made.
{% extends "base.html" %}
{% block content %}
<div class="row clearfix">
<div class="col-md-12 column">
<form method="post" action="post/">
{% csrf_token %}
<div class="col-md-8 col-md-offset-2 fieldWrapper">
{{ form.text.errors }}
{{ form.text }}
</div>
{{ form.country.as_hidden }}
<div>
<input type="submit" value="post">
</div>
</form>
</div>
<h3> </h3>
<div class="col-md-12 column">
{% for tweet in tweets %}
<div class="well">
<span>{{ tweet.text }}</span>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
When I issue command(run server), browser don't render form without any exception.
I think there is a problem in views.py file, But I can't find it.
How can I send form parameter to Template(.html file) and render form in result?
You didn't instantiate the form.
You need to instantiate the form, for rendering in a template, like this,
form = TweetForm()
Note:
The params variable is already a dict(), you could just add form into the params like this,
params['form'] = form
Then, render the template with context as params,
render(request, 'profile.html', params)

Django: How to use FilePathField to get file size

I work in a postproduction company, we have our media files on a server. Through the site running on a second server, the user would point to a file, perform some operations (calculating checksums for example) and save the results in the database.
I'm looking for a "best practices" example on how to use FilePathField to get the size of a file. I've read tutorials and searched in the docs, but I'm having trouble putting the pieces together for my needs.
Some relevant code (EDIT: corrected the views, #1 and #3 are printed):
models.py
class AssetTest(models.Model):
file_path = models.FilePathField(path=r"", default="")
file_name = models.CharField(max_length=250, default="")
file_size = models.IntegerField(default=0)
def __str__(self):
return self.file_path
forms.py
class AssetTestForm(forms.ModelForm):
class Meta:
model = AssetTest
fields = ("file_name", "file_size")
views.py
def asset_select(request):
if request.method == 'POST':
print("1")
form = AssetTestForm(request.POST)
if form.is_valid():
print("2")
form.save(commit=False)
form.file_name = request.FILES['file'].name
form.file_size = request.FILES['file'].size
form.save()
return HttpResponseRedirect('/assetmanage/assets/')
print("3")
else:
print("4")
form = AssetTestForm()
return render(request, 'assetmanage/asset_select.html', {'form': form})
asset_select.html
{% extends "assetmanage/base.html" %}
{% block title %}Add Asset{% endblock %}
{% block body %}
<div class="container-fluid">
<div class="row">
<div class="col-sm-12 col-md-7">
<div class="panel panel-default">
<div class="panel-body">
<form class="form-horizontal" name="asset_select" action="/assetmanage/asset/test/" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
<label class="control-label col-sm-2">Select a file:</label>
<input type="file" name="asset_file">
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
In your FilePathField give the correct path name
FilePathField(path="/home/simon/",..)
cleaned_data of FilePathField will give you the exact path so using that to get the file name and file size of it...
form = AssetTestForm(request.POST)
if form.is_valid():
form.save(commit=False)
temp_file_obj = TemporaryFileUploadHandler(form.cleaned_data['file_path'])
form.instance.file_size = temp_file_obj.chunk_size
form.instance.file_name = form.cleaned_data['file_path'].split("/")[-1]
form.save()

Categories

Resources