Online waiting: django submit form and display result in same page - html

I have an emergent task to make a web page which allow user to input some data and the backend do some calculation and the result needs to be displayed in the same page just below the input field (like air ticket online price check).
I am new to django and html. below is my first test web page of a simple online calculator to try to figure out how to make such web service.
I found a problem that when clicking the "submit" button, it tends to jump to a new web page or a new web tab. this is not what I want. Once the user input the data and click "submit" button, I want the "result" field on the page directly show the result (i.e. partially update only this field) without refresh/jump to the new page. Also I want the user input data kept in the same page after clicking "submit".
I saw there might be several different ways to do this work, iframe/AJAX. However, I have been searching/trying for answers and solutions for several days and none of the answers really work for this very basic simple question!!
html:
<form method="POST">
{% csrf_token %}
<div>
<label>num_1:</label>
<input type="text" name="num_1" value="1" placeholder="Enter value" />
</div>
<div>
<label>num_2:</label>
<input type="text" name="num_2" value="2" placeholder="Enter value" />
</div>
<br />
<div>{{ result }}</div>
<button type="submit">Submit</button>
</form>
view.py
def post_list(request):
result = 0
if request.method == "POST":
num1 = request.POST.get('num_1')
num2 = request.POST.get('num_2')
result = int(num1) + int(num2)
print(request.POST)
print(result)
context = {
'result': result
}
return render(request, 'blog/post_list.html', context)

I would suggest taking a look at htmx.org which makes this really simple without having to write any actual JS.
For your example:
(1) You add the htmx JS (which is only about 10k) to your HTML, and use hx-post and hx-target to trigger the ajax calls on your form. With these the form will fire an AJAX request, and the hx-target tells htmx to take the response (which you want to be only the result of your calculation) and put it in the div without refreshing the whole page.
See docs for more details on this.
Note also I gave an id to the div containing the result.
You will need to replace hx-post="{% url 'blog:post_list' %}" with the correct name to your view (which we don't know as you didn't post your urls.py).
<html>
<body>
<form method="POST" hx-post="{% url 'blog:post_list' %}" hx-target="#result">
{% csrf_token %}
<div>
<label>num_1:</label>
<input type="text" name="num_1" value="1" placeholder="Enter value" />
</div>
<div>
<label>num_2:</label>
<input type="text" name="num_2" value="2" placeholder="Enter value" />
</div>
<br />
<div id="result">{{ result }}</div>
<button type="submit">Submit</button>
</form>
<script src="https://unpkg.com/htmx.org#1.6.1"></script>
</body>
</html>
(2) In your view then you determine if the request is an AJAX request from htmx by checking the headers, in which case you want to only return the result. There are easier or elegant ways to do this (eg. check django-htmx, but just to keep it simple:
from django.http.response import HttpResponse
from django.shortcuts import render
# Create your views here.
def post_list(request):
result = 0
if request.method == "POST":
num1 = request.POST.get('num_1')
num2 = request.POST.get('num_2')
result = int(num1) + int(num2)
if request.headers.get('Hx-Request') == 'true':
# return only the result to be replaced
return HttpResponse(str(result))
else:
return render(request, 'blog/post_list.html', {'result': result})

Related

Django, How to pass HTML form values to URL upon form submission?

The project have url as follows,
path('post/<str:state>/',SearchView.as_view(),name='search-data')
I have a HTML form, upon filling and submitting it supposed to pass filled form data to URL.
<form action={% url 'search-data'%} method="get" >
{% csrf_token %}
<input type="text" name="fname">
But it does not work as supposed to be.
When form submitted it gives below URL
http://127.0.0.1:8000/%7Burl?csrfmiddlewaretoken=2RZfZ4cxLB...
You don't have to pass <str:state> argument in your urlpatterns just pass path('post/search',SearchView.as_view(),name='search-data') or whatever you want but problem is when you pass an argument like this post/<str:state>/ than you have to specify that in your form action also
like this {% url 'search-data' state %} initialy you don't have any state so that's why you have to get the state name from your form so finnaly your code look like this
<form action={% url 'search-data'%} method="get" >
{% csrf_token %}
<input type="text" name="fname">
<input type="submit" value="search">
</form>
and than in your views you've to get it from request.GET method like this
def search(request):
state = request.GET.get('fname', None)
.... do whatever you want
return response_or_data

django: html submit form and display result in smae page without jumping/rereshing into new page

I am new to django and html. below is my first test web page of a simple online calculator.
I found a problem that when clicking the "submit" button, it tends to jump to a new web page or a new web tab. this is not what I want. Once the user input the data and click "submit" button, I want the "result" field on the page directly show the result (i.e. partially update only this field) without refresh/jump to the new page. Also I want the user input data kept in the same page after clicking "submit".
I saw there might be several different ways to do this work, iframe/AJAX. However, I have been searching for answers for 5 days and none of the answers really work for this very basic simple question!!
html:
<form method="POST">
{% csrf_token %}
<div>
<label>num_1:</label>
<input type="text" name="num_1" value="1" placeholder="Enter value" />
</div>
<div>
<label>num_2:</label>
<input type="text" name="num_2" value="2" placeholder="Enter value" />
</div>
<br />
<div>{{ result }}</div>
<button type="submit">Submit</button>
</form>
view.py
def post_list(request):
result = 0
if request.method == "POST":
num1 = request.POST.get('num_1')
num2 = request.POST.get('num_2')
result = int(num1) + int(num2)
print(request.POST)
print(result)
context = {
'result': result
}
return render(request, 'blog/post_list.html', context)

CSRF verification failed. Request aborted [New] 2021

I am completely tired with the csrf issue. I have created a sign in form and register form.
I am able to login and logout, even register a user.
The main problem I am facing is the refresh-after-signin.
After signing in, if I refresh the page it simply gives a csrf verification failed error.
I have literally searched for it since past two days with no solution, all the answers are almost 4-5 years older, which are not helping.
This is the views.py signin function.
def signin(request):
if request.method=="POST":
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(username=username,password=password)
if user is not None:
login(request,user)
messages.success(request,"Logged in Successfully!")
return render(request,'authtest/index.html')
else:
messages.error(request,"Bad Credentials")
return redirect('index')
return render(request,'authtest/signin.html')
This is the HTML form that is returning POST request
<form action="{% url 'signin' %}" method="POST">
<!-- I have no idea what this thing does -->
{% csrf_token %}
<!-- I have no idea what this thing does end -->
<input type="hidden" id="csrf_token" value='{"csrfmiddlewaretoken": "{{ csrf_token }}"}'>
<label for="username">UserName</label>
<input type="text" name="username" id="username" required>
<label for="password">Password</label>
<input type="password" name="password" id="password" required>
<button type="submit">SignIn</button>
</form>
Due to some security issues to keep each user's session safe, it is not possible to authenticate and render the url in the same view functions.
Therefore, you must perform the rendering operation in another URL after redirecting
something like this
def signin(request):
if request.method=="POST":
........
if user is not None:
..........
return redirect ('dashboard')\
and that dashboard func is like this
def dashboard(request):
.......
return redirect ('dashboard')

Contact Us button/smtplib forward details

I am using pythonanywhere to make a website. I have set up a contact us page, and I am attempting to take whatever a user submits as feedback and then forward the information to myself with smtplib. I asked about this on their forums, but they for some reason just deleted my post.
Here is my HTML code:
<title>Contact us!</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/contact.css') }}">
<div class="container">
<form action="contact">
<label for="report">Reason</label>
<select id="report" name="report">
<option value="Bug">Bug</option>
<option value="Suggestion">Suggestion</option>
<option value="Other">Other</option>
</select>
<label for="Subject">Subject</label>
<textarea id="Subject" name="Subject" placeholder="Write something.." style="height:200px"></textarea>
<input type="submit" value="Submit">
</form>
</div>
And here is the python code:
#app.route("/contact", methods=["GET", "POST"])
def feedback():
if request.method == 'GET':
return render_template("contact.html")
else:
result = "Thanks for the feedback!"
report = request.form['report']
Subject = request.form['Subject']
from email.mime.text import MIMEText
import smtplib
gmail_user = 'email#gmail.com'
gmail_password = 'password'
message = MIMEText(report)
message["Subject"] = Subject
message["From"] = gmail_user
message["To"] = gmail_user
server = smtplib.SMTP_SSL('smtp.gmail.com', 465)
server.ehlo()
server.login(gmail_user, gmail_password)
server.sendmail(gmail_user, gmail_user, message.as_string())
server.close()
return render_template('settlement_return.html',result = result)
EDIT: If I manually set report and subject to some misc text string it sends fine. But trying to get the information that someone submits is not giving any results.
As discussed in the comments above -- it looks like the problem was that you were missing the method="POST" in your form tag. That meant that the form was being submitted with the GET method, so the code in the first branch of the if statement in your view was being executed, which meant that no email was being sent.

Passing variable through response object in Flask framework

I need to pass a variable along with response object while complete signup process and display the success message on the same page. I tried so many ways but I couldn't find any idea how to do that. Please look at this scenario and help me out.
#app.route('/signup', methods=['POST', 'GET'])
def signup():
message = ''
email = ''
password = ''
resp = app.make_response(render_template('signup.html'))
if request.method == 'POST':
import datetime
email = request.form['emailInput']
password = request.form['pswdInput']
if len(password) < 3:
message = Markup("<p style='color:red;'> Password Length Should Be More Than 3 Character </p> ")
return render_template('signup.html', message = message)
expire_date = datetime.datetime.now()
expire_date = expire_date + datetime.timedelta(hours=1)
resp.set_cookie('userEmail', email, expires=expire_date)
resp.set_cookie('userPassword', password, expires=expire_date)
message = Markup("<h1> Registration successfull! </h1>")
resp.headers.set('message', message)
return resp
return render_template('signup.html', message = message)
HTML :
<div class="container">
Home
<form id="signup" method="POST" action="{{url_for('signup')}}">
<div class="header">
<h3>Sign Up</h3>
<p>You want to fill out this form</p>
</div>
<div class="sep"></div>
<div class="">
<input type="email" name="emailInput" placeholder="e-mail" autofocus />
<input type="password" name="pswdInput" placeholder="Password" />
<div class="">
<input name="joinCheck" name="joinCheck" value="1" type="checkbox" /><label class="terms">I accept the terms of use</label>
</div>
<input type="submit" id="submitBtn" value="Submit">SIGN UP FOR INVITE NOW</a>
</div>
<div>
{{message}}
</div>
</form>
</div>
resp = app.make_response(render_template('signup.html'))
This is missing your message keyword. Change it to:
resp = app.make_response(render_template('signup.html', message=message))
You need to move this code down also, because to it, it sees the message variable directly above as the empty string and assigns that to its keyword. I would place it right above your resp.set_cookie() code.
Finally, move this:
message = Markup("<h1> Registration successfull! </h1>")
Above the app.make_response() code you just moved. Your message variable needs to be set before the response code is initialized.
Just a small tip: I would change message = " " to message = None. Otherwise, in the compiled HTML, it just looks like there's an empty div. This won't be too detrimental in this small case, but I'd definitely make it a rule of thumb in future, larger projects. Then, in your HTML:
{% if message %}
<div>
{{message}}
</div>
{% endif %}
Cheers.