How to show html text beside a form button with flask - javascript

I would like to show a small text confirming successful registration next to the "submit" button of the registration form. However it returns my html text on another page. I would like it to be shown on the same page, inside the "form" next to the "button". How do I do that. I'm using python flask. Here goes my code:
from flask import Flask, render_template, request
from flask_mysqldb import MySQL
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['MYSQL_HOST'] = 'localhost'
app.config['MYSQL_USER'] = 'root'
app.config['MYSQL_PASSWORD'] = '104041122'
app.config['MYSQL_DB'] = 'PAGINA10'
mysql = MySQL(app)
#app.route('/', methods=['GET', 'POST'])
def form():
if request.method == 'POST':
digitado = request.form
nome = digitado['nome']
cpf = digitado['cpf']
email = digitado['email']
birth = digitado['birth']
cursor = mysql.connection.cursor()
cursor.execute("CREATE TABLE IF NOT EXISTS pagina10 (nome VARCHAR(50) NOT NULL, cpf VARCHAR(11) NOT NULL, email VARCHAR(20) NOT NULL, birth DATE NOT NULL)")
cursor.execute("INSERT INTO pagina10 (nome, cpf, email, birth) VALUES (%s, %s, %s, %s)", (nome, cpf, email, birth))
mysql.connection.commit()
cursor.close()
return '<h1> Dados cadastrados com sucesso </h1>'
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)
And here is my html page:
<form method="POST" action="">
<div>
<i class="fa-solid fa-pen"></i>
<input type="text" required name="nome" autofocus placeholder="Nome" data-ls-module="charCounter" maxlength="50" minlength="3"/>
</div>
<div>
<i class="fa-solid fa-id-card"></i>
<input type="text" required name="cpf" autofocus placeholder="CPF" minlength="11" maxlength="11"/>
</div>
<div>
<i class="fa-solid fa-at"></i>
<input type="email" required name="email" autofocus placeholder="E-mail" data-ls-module="charCounter" minlength="5" pattern="[UTF-8]">
</div>
<div>
<i class="fa-solid fa-cake-candles"></i>
<input type="date" required name="birth" autofocus placeholder="Nascimento">
</div>
<button type="submit">Cadastrar</button>
</form>

You can use the package flash from flask to show such messages on your website.
Your python code might look something like:
from flask import flash
#app.route('/registrierung', methods=['GET', 'POST'])
#app.route('/Registrierung', methods=['GET', 'POST'])
def registration_page(): # put application's code here
registration_form = RegistrationForm()
# ---------------------------------------------------
if registration_form.validate_on_submit():
print(registration_form.is_submitted())
# ----------------- Database Stuff -------------
flash(f'Account was created!', 'success')
return redirect(url_for('login_page'))
return render_template("registrierung.html", img_var_path=get_background_img_path(), registration_form=registration_form)
And you have to use something like this in your html code:
<!-- flash messages here -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="alert" id="hideMe">
{% for category, message in messages %}
<div class=" alert_{{ category }}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}

Related

How to get additional Django Inline Formsets to POST to database

I'm currently building out a Django ModelForm with an adjoining inline_formset. I'm also using a button with javascript to allow the user to add in more instances of the formset in the front-end. However, the issue im running into is that the extra instances of the formset that are added by the user, don't get sent to the database when the form is submitted.
I'm unsure as to whether it is an issue with the javascript itself. Or something more fundamental to do with the Django inline formsets. Any help would be much appreciated. Everything else is working fine; heres my HTML template for reference
{% extends 'main.html' %}
{% load static %}
{% block content %}
<section id="form">
<form class="form" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
<div class="input">
<h1>{{field.label}}:</h1>
<p>{{field}}</p>
</div>
{% endfor %}
<div id="form-container">
{{formset.management_form}}
{% for field in formset %}
<div class="input">
<h1>Parts Required:</h1>
<p>{{field}}</p>
</div>
<div id="empty-form" class="input" style="display: none;">
<p>{{formset}}</p>
</div>
{% endfor %}
</div>
<button class="btn btn-primary" id="add-more" type="button">Add More</button>
<input class="submit btn btn-primary" type="submit"></input>
</form>
</section>
<script>
document.getElementById("add-more").onclick = function () {
var emptyForm = document.getElementById("empty-form");
var newForm = emptyForm.cloneNode(true);
newForm.style.display = "block";
document.getElementById("form-container").appendChild(newForm);
}
</script>
{% endblock content %}
Here's my view for the warranty form:
# Create Warranty Form:
#login_required(login_url='login')
def createWarranty(request):
warranty = WarrantyClaim.objects.all()
form = WarrantyClaimForm()
warrantyformset = inlineformset_factory(WarrantyClaim, PartsRequired, form=WarrantyClaimForm, extra=1)
if request.user.is_authenticated:
if request.method == 'POST':
form = WarrantyClaimForm(request.POST, request.FILES)
formset = warrantyformset(request.POST, request.FILES, instance=WarrantyClaim())
if form.is_valid() and formset.is_valid():
post = form.save(commit=False)
post.owner = request.user
post.save()
formset.instance = post
formset.save()
return redirect('spare-parts')
context = {'warranty': warranty, 'form': form, 'formset': warrantyformset}
return render(request, 'spareparts/parts_form.html', context)

Why do I get the path error to view the contents of a single email in the below code.?

In the inbox.js file I am trying to listen for a click event for each email single_email_div and send it to the email view in views.py
inbox.js
function load_mailbox(mailbox) {
// Show the mailbox and hide other views
document.querySelector("#emails-view").style.display = "block";
document.querySelector("#compose-view").style.display = "none";
// Show the mailbox name
document.querySelector("#emails-view").innerHTML = `<h3>${
mailbox.charAt(0).toUpperCase() + mailbox.slice(1)
}</h3>`;
// Show the emails of that particular mailbox
fetch(`/emails/${mailbox}`)
.then(response => response.json())
.then(emails => {
// Print emails
console.log(emails);
// ... do something else with emails ...
emails.forEach(email => {
const single_email_div = document.createElement('div');
single_email_div.innerHTML = `<a href="{%url 'email' email.id %}">
<br> ${email.id} <br>
From: ${email.sender} <br>
Subject: ${email.subject} <br>
TimeStamp: ${email.timestamp} <br>
Read: ${email.read} <br><br>
</a>`
if(`${email.read}` === false)
{single_email_div.style.backgroundColor = "white";}
else
{single_email_div.style.backgroundColor = "grey";}
const emails_div = document.querySelector('#emails-view');
emails_div.append(single_email_div);
// When a user clicks on an email, the user should be taken to a view where they see the content of that email
document.querySelector("single_email_div").addEventListener('click', () => {
fetch(`/emails/${id}`)
.then(response => response.json())
.then(email => {
// show email and hide other views
document.querySelector("#emails-view").style.display = "none";
document.querySelector("#compose-view").style.display = "none";
document.querySelector("#email-view").style.display = "block";
// display email
const view = document.querySelector("#email-view");
view.innerHTML =
`<ul>
<li> From: ${email.sender} </li>
<li> To: ${email.recipients} </li>
<li> Subject: ${email.subject} </li>
<li> TimeStamp: ${email.timestamp} </li>
<li> Body: ${email.body} <br><br>
</ul>`
});
})
});
})
return false;
}
inbox.html
{% block body %}
<h2>{{ request.user.email }}</h2>
<button class="btn btn-sm btn-outline-primary" id="inbox">Inbox</button>
<button class="btn btn-sm btn-outline-primary" id="compose">Compose</button>
<button class="btn btn-sm btn-outline-primary" id="sent">Sent</button>
<button class="btn btn-sm btn-outline-primary" id="archived">Archived</button>
<a class="btn btn-sm btn-outline-primary" href="{% url 'logout' %}">Log Out</a>
<hr>
<div id="emails-view"> </div>
<div id="email-view"> </div>
<div id="compose-view">
<h3>New Email</h3>
<form id="compose-form">
<div class="form-group">
From: <input disabled class="form-control" value="{{ request.user.email }}">
</div>
<div class="form-group">
To: <input id="compose-recipients" class="form-control">
</div>
<div class="form-group">
<input class="form-control" id="compose-subject" placeholder="Subject">
</div>
<textarea class="form-control" id="compose-body" placeholder="Body"></textarea>
<input type="submit" class="btn btn-primary"/>
</form>
</div>
{% endblock %}
{% block script %}
<script src="{% static 'mail/inbox.js' %}"></script>
{% endblock %}
urls.py
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
path("login", views.login_view, name="login"),
path("logout", views.logout_view, name="logout"),
path("register", views.register, name="register"),
# API Routes
path("emails", views.compose, name="compose"),
path("emails/<int:email_id>", views.email, name="email"),
path("emails/<str:mailbox>", views.mailbox, name="mailbox"),
]
views.py
#login_required
def mailbox(request, mailbox):
# Filter emails returned based on mailbox
if mailbox == "inbox":
emails = Email.objects.filter(
user=request.user, recipients=request.user, archived=False
)
elif mailbox == "sent":
emails = Email.objects.filter(
user=request.user, sender=request.user
)
elif mailbox == "archive":
emails = Email.objects.filter(
user=request.user, recipients=request.user, archived=True
)
else:
return JsonResponse({"error": "Invalid mailbox."}, status=400)
# Return emails in reverse chronologial order
emails = emails.order_by("-timestamp").all()
return JsonResponse([email.serialize() for email in emails], safe=False)
#csrf_exempt
#login_required
def email(request, email_id):
# Query for requested email
try:
email = Email.objects.get(user=request.user, pk=email_id)
except Email.DoesNotExist:
return JsonResponse({"error": "Email not found."}, status=404)
# Return email contents
if request.method == "GET":
return JsonResponse(email.serialize())
# Update whether email is read or should be archived
elif request.method == "PUT":
data = json.loads(request.body)
if data.get("read") is not None:
email.read = data["read"]
if data.get("archived") is not None:
email.archived = data["archived"]
email.save()
return HttpResponse(status=204)
# Email must be via GET or PUT
else:
return JsonResponse({
"error": "GET or PUT request required."
}, status=400)
Below is the error that I keep getting:
Page not found (404)
Request Method: GET
Request URL: http://127.0.0.1:8000/%7B%25url%20'email'%20email.id%20%25%7D
Using the URLconf defined in project3.urls, Django tried these URL patterns, in this order:
admin/
[name='index']
login [name='login']
logout [name='logout']
register [name='register']
emails [name='compose']
emails/<int:email_id> [name='email']
emails/<str:mailbox> [name='mailbox']
The current path, {%url 'email' email.id %}, didn’t match any of these.
you are can not use django template tags "{% %}" in .js:
single_email_div.innerHTML = `<a href="{%url 'email' email.id %}">
Here you already should have
single_email_div.innerHTML = `<a href="/emails/${email.id}/">

How to loging to django with login form? With or without Ajax

Problem:
If user is loggedin, I want to show greet user else show the form
and when the valid data is submitted, log in the user and
show greet message.
I am trying to pass the data on submit and update the user status to login. I tried with and without ajax but with both ways I could not figure out solution. Could you please help me why its failing? And how I can solve this?
HTML Form:
{% if user.is_authenticated %}
<h5 class="title billing-title ls-10 pt-1 pb-3 mb-0">
Welcome {{ user.username }}!
</h5>
{% else%}
<div class="login-toggle">
Returning customer? <a href="#"
class="show-login font-weight-bold text-uppercase text-dark">Login</a>
</div>
<form class="login-content" name="ajaxLogin" method="POST" action="{% url 'ecommerce:ajaxlogin' %}">
{% csrf_token %}
<p>If you have shopped with us before, please enter your details below.
If you are a new customer, please proceed to the Billing section.</p>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label>Username or email *</label>
<input type="text" class="form-control form-control-md" name="username" id="id_email"
required>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label>Password *</label>
<input type="password" class="form-control form-control-md" name="password" id="id_password"
required>
</div>
</div>
</div>
<div class="form-group checkbox">
<input type="checkbox" class="custom-checkbox" id="remember" name="remember">
<label for="remember" class="mb-0 lh-2">Remember me</label>
Lost your password?
</div>
<button class="btn btn-rounded btn-login" type="submit" >Login</button>
</form>
{% endif%}
Views.py :
def ajaxlogin(request):
is_ajax = request.headers.get('X-Requested-With') == 'XMLHttpRequest'
if is_ajax and request.method == "POST":
username = request.POST['username']
password = request.POST['password']
user = authenticate(user=username, password=password)
if User.is_active and not None :
login(request)
else:
pass
return render('./ecommerce/checkout.html')
AJAX:
$(document).ready(function(){
$('.login-content').on('submit', function(event){
event.preventDefault();
var action_url = $(this).attr('action')
$.ajax({
url: action_url,
type:"POST",
data: $('.login-content').serialize(),
headers: { "X-CSRFToken": $.cookie("csrftoken") },
success: function (data) {
console.log("login Successful");
},
});
});
});
Django authenticate takes keyword arguments username and password for the default case with request as optional parameter.
In your views.py you pass user as parameter in authenticate change it to username.
The default authentication returns None if user is in-active so you don't have to check for is_active if you are using default authentication.
Change your views.py as
def ajaxlogin(request):
is_ajax = request.headers.get('X-Requested-With') == 'XMLHttpRequest'
if is_ajax and request.method == "POST":
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None :
login(request, user)
else:
pass
return render('./ecommerce/checkout.html')
Also set contentType as application/x-www-form-urlencoded or leave it at the default by omitting the contentType key.

Django/Js: how to post a form without reloading whole page

My application currently flows through 3 pages:
User selects question in index page
User submits answer in answer page
User is presented with result in results page.
I want to compress that down to a single page where the user submits an answer to the question and result is shown on the same page.
The following django-template code separates questions with Bootstrap accordion. How do I post the form without refreshing the whole page? I want to be able to display the result on the page, update CSS styling with Javascript etc.
<h2>{{ category.title }}</h2>
<div class="accordion" id="accordion{{category.title}}">
{% for challenge in category.challenge_set.all %}
<div class="card">
<div class="card-header" id="heading{{challenge.id}}">
<h2 class="mb-0">
<button class="btn btn-link btn-block text-left" type="button" data-toggle="collapse" data-target="#collapse{{challenge.id}}" aria-expanded="true" aria-controls="collapse{{challenge.id}}">
{{ challenge.question_text }} - {{ challenge.point_value }} points
</button>
</h2>
</div>
<div id="collapse{{challenge.id}}" class="collapse in" aria-labelledby="heading{{challenge.id}}" data-parent="#accordion{{category.title}}">
<div class="card-body">
<p>{{ challenge.description }}</p>
<form action="{% url 'challenges:answer' challenge.id %}" method="post">
{% if challenge|is_answered:request %}
<label for="answered">Answer</label>
<input type="text" name="answered" id="answered" value="{{ challenge.answer_text }}" readonly>
{% else %}
{% csrf_token %}
<label for="answer">Answer</label>
<input type="text" name="answer" id="answer">
<input type="submit" value="Submit">
{% endif %}
</form>
</div>
</div>
{% endfor %}
</div>
Here is the view:
def index(request):
context = {'challenges_by_category_list': Category.objects.all()}
return render(request, 'challenges/index.html', context)
def detail(request, challenge_id):
challenge = get_object_or_404(Challenge, pk=challenge_id)
return render(request, 'challenges/detail.html', {'challenge': challenge})
def results(request, challenge_id, result):
challenge = get_object_or_404(Challenge, pk=challenge_id)
return render(request, 'challenges/results.html', {'challenge':challenge, 'result':result})
def answer(request, challenge_id):
challenge = get_object_or_404(Challenge, pk=challenge_id)
result = "Incorrect, try again!"
if challenge.answer_text.lower() == request.POST['answer'].lower():
current_user = request.user
session = User_Challenge(user=current_user, challenge=challenge, answered=True)
session.save()
points = Profile(user=current_user, points=challenge.point_value)
points.save()
result = "Correct!"
return HttpResponseRedirect(reverse('challenges:results', args=(challenge.id, result)))
You can try this:
Add the below script in your template:
<script src="https://code.jquery.com/jquery-3.1.0.min.js"></script>
write a script and a function inside it to submit the form data.
<script type="text/javascript">
function submitData( challenge_id ){
// Get answer from the input element
var answer = document.getElementById("answer").value;
// add the url over here where you want to submit form & challenge_id is also taken as a parameter.
var url = "<your_url>";
$.ajax({
url: url,
data: {
'answer': answer,
},
dataType: 'JSON',
success: function(data){
// show an alert message when form is submitted and it gets a response from the view where result is provided and if url is provided then redirect the user to that url.
alert(data.result);
if (data.url){
window.open(data.url, '_self');
}
}
});
}
</script>
Change type of the submit button and add an onclick event to call the submitData() function and pass the challenge id to it. And remove the action attr from the form.
see below:
<form method="post">
{% csrf_token %}
{% if challenge|is_answered:request %}
<label for="answered">Answer</label>
<input type="text" name="answered" id="answered" value="{{ challenge.answer_text }}" readonly>
{% else %}
<label for="answer">Answer</label>
<input type="text" name="answer" id="answer">
// over here
<button type="button" onclick="submitData({{ challenge.id }})">
Submit
</button>
{% endif %}
</form>
Return a JsonReponse to the ajax call from the views.
views.py
def answer(request, challenge_id):
answer = request.GET.get('answer', False)
url = False
if challenge.objects.filter(id=challenge_id).exists() and answer:
challenge = Challenge.objects.get(id=challenge_id)
if challenge.answer_text.lower() == answer.lower():
current_user = request.user
session = User_Challenge(user=current_user, challenge=challenge, answered=True)
session.save()
points = Profile(user=current_user, points=challenge.point_value)
points.save()
result = "Correct!"
# specify the url where you want to redirect the user after correct answer
url = ""
else:
result = "Incorrect, try again!"
data = {
'result': result,
'url': url
}
return JsonResponse(data)

Extracting input from all input boxes into a list using flask

I am trying to use my flask backend to extract my input. However, in my html file, I use javascript so that I can dynamically arrange any number of input boxes I want.
<!DOCTYPE html>
<script>
function add_field()
{
var total_text=document.getElementsByClassName("input_text");
total_text=total_text.length+1;
document.getElementById("field_div").innerHTML=document.getElementById("field_div").innerHTML+
"<li id='input_text"+total_text+"_wrapper'><input type='text' class='input_text' id='input_text"+total_text+"' placeholder='Enter Text'></li>";
}
function remove_field()
{
var total_text=document.getElementsByClassName("input_text");
document.getElementById("input_text"+total_text.length+"_wrapper").remove();
}
</script>
{% extends "bootstrap/base.html" %}
{% block content %}
<div class = "container">
<h1>Give the words</h1>
<form action='/results' method="post">
<div id="wrapper">
<input type="button" value="Add TextBox" onclick="add_field();"><input type="button" value="Remove TextBox" onclick="remove_field();">
<ol id="field_div">
</ol>
</div>
<input type='submit' value='Select'>
</form>
</div>
{% endblock %}
My views.py is as follows:
from flask import render_template, request
from app import app
from .translit import *
#app.route('/')
def search():
return render_template('index.html')
#app.route('/results', methods=['POST'])
def results():
words = getwds(request.form['input_text1'])
return render_template('results.html', words=words)
How do I change the code in such a way that all the input boxes are
extracted from in a list?
The square bracket syntax in name attribute of input elements converts form inputs into an array. So, when you use name="input_text[]" you will get an array. This array can be handled in Flask routing using request.form.getlist method. Let's see this in action.
app.py:
from flask import Flask, render_template, url_for, request
app = Flask(__name__)
#app.route('/')
def search():
return render_template('dynamic_input.html')
#app.route('/results', methods = ['GET', 'POST'])
def results():
if request.method == 'GET':
return redirect(url_for('/'))
else:
values = request.form.getlist('input_text[]')
return render_template('dynamic_input_results.html',
values = values)
if __name__ == '__main__':
app.run(debug = True)
dynamic_input.html contains:
<!DOCTYPE html>
<script>
function add_field()
{
var total_text=document.getElementsByClassName("input_text");
total_text=total_text.length+1;
field_div = document.getElementById("field_div");
new_input = "<li id='input_text"+total_text+
"_wrapper'><input type='text' class='input_text' name='input_text[]' id='input_text"+
total_text+"' placeholder='Enter Text'></li>";
field_div.insertAdjacentHTML('beforeend',new_input);
}
function remove_field()
{
var total_text=document.getElementsByClassName("input_text");
document.getElementById("input_text"+total_text.length+"_wrapper").remove();
}
</script>
<div class = "container">
<h1>Give the words</h1>
<form action='/results' method="post">
<div id="wrapper">
<input type="button" value="Add TextBox" onclick="add_field();">
<input type="button" value="Remove TextBox" onclick="remove_field();">
<ol id="field_div">
</ol>
</div>
<input type='submit' value='Select'>
</form>
</div>
dynamic_input_results.html contains:
<ul>
{% for value in values %}
<li>{{value}}</li>
{% endfor %}
</ul>
Output
Figure 1: Dynamic input elements
Figure 2: Result is showing as a list after submitting the previous form
N.B.:
I have modified your JS code to prevent overriding of text input values after adding a new text box.
Updated:
Added checkbox with each textbox.
app.py:
from flask import Flask, render_template, url_for, request
app = Flask(__name__)
#app.route('/')
def search():
return render_template('dynamic_input.html')
#app.route('/results', methods = ['GET', 'POST'])
def results():
if request.method == 'GET':
return redirect(url_for('search'))
else:
input_values = request.form.getlist('input_text[]')
checkbox_values = request.form.getlist('input_checkbox')
return render_template('dynamic_input_results.html',
input_values = input_values,
checkbox_values = checkbox_values)
if __name__ == '__main__':
app.run(debug = True)
dynamic_input.html:
<!DOCTYPE html>
<script>
function add_field()
{
var total_text=document.getElementsByClassName("input_text");
total_text=total_text.length+1;
field_div = document.getElementById("field_div");
new_input = "<li id='input_text"+total_text+"_wrapper'>";
new_input += "<input type='text' class='input_text' name='input_text[]' id='input_text"+
total_text+"' placeholder='Enter Text'>";
new_input += "<input type='checkbox' name='input_checkbox' value='"+total_text+"' id='input_checkbox"+
total_text+"'";
new_input += "</li>";
field_div.insertAdjacentHTML('beforeend',new_input);
}
function remove_field()
{
var total_text=document.getElementsByClassName("input_text");
document.getElementById("input_text"+total_text.length+"_wrapper").remove();
}
</script>
<div class = "container">
<h1>Give the words</h1>
<form action='/results' method="post">
<div id="wrapper">
<input type="button" value="Add TextBox" onclick="add_field();">
<input type="button" value="Remove TextBox" onclick="remove_field();">
<ol id="field_div">
</ol>
</div>
<input type='submit' value='Select'>
</form>
</div>
dynamic_input_results.html:
<ul>
{% for value in input_values %}
<li>{{value}}</li>
{% endfor %}
<hr>
{% for value in checkbox_values %}
<li>{{value}}</li>
{% endfor %}
</ul>
Output:

Categories

Resources