Include radio buttons on python flask webpage - python

I'm trying to build a webpage as part of a bigger application, where the user is able to upload csv files. There are three different types of csv's. On uploading I want to perform some checks on these files. Some of the checks are general, like file name exists and the .csv extension. And some are csv type specific, for which I have written some code with the CSVValidator package. These checks include column name check and data type check.
On the webpage I would like the user to toggle a radio button to make a choice which of the three types he/she is going to upload. This choice can then be used to select the write validation for that specific type of file.
Now I'm struggling with 'request'. Since I already have a request for the upload button, how do I include one for the radio buttons. I don't get any response from the radio buttons yet. Please help me out.
The code I have written so far:
#app.route("/upload", methods=['GET', 'POST'])
def upload():
if request.method == "POST":
if request.files:
csv = request.files['csv']
if csv.filename == '':
print('CSV file must have a name')
return redirect(request.url)
if not csv_extention(csv.filename):
print('This is not a correct CSV file')
return redirect(request.url)
button = request.form['choice_csv']
if button == '1':
if check_csv_type1(filename) is False:
print('Check the errors')
print(result)
else:
filename = secure_filename(csv.filename)
csv.save(os.path.join(app.config['CSV_UPLOADS'] + '/realised', filename))
print('CSV file saved')
return redirect(request.url)
elif button == '2':
if check_csv_type2(filename) is False:
print('Check the errors')
print(result)
else:
filename = secure_filename(csv.filename)
csv.save(os.path.join(app.config['CSV_UPLOADS'] + '/contracts', filename))
print('CSV file saved')
return redirect(request.url)
elif button == '3':
if check_csv_type3(filename) is False:
print('Check the errors')
print(result)
else:
filename = secure_filename(csv.filename)
csv.save(os.path.join(app.config['CSV_UPLOADS'] + '/pipeline', filename))
print('CSV file saved')
return redirect(request.url)
return render_template('public/upload_csv.html')
And this is the HTML code:
{% extends "public/templates/public_template.html" %}
{% block title %}Upload csv{% endblock title %}
{% block main %}
<div class="container">
<!-- First row -->
<div class="row mb-4">
<h2>Upload csv files</h2>
<label>Tick the box for the kind of file you want to upload, select the month and year of the first period of data and then browse and upload.</label>
</div>
<!-- Second row, first column -->
<div class="row">
<div class="col-sm-3 col-md-4 col-lg-4 mb-4">
<form action="/upload-csv" method="POST" type="radio">
<div class="form-group">
<div class="custom-control custom-radio mb-2">
<input type="radio" class="custom-control-input" value="1" id="realised" name="choice_csv">
<label class="custom-control-label" for="realised">Realised</label>
</div>
<div class="custom-control custom-radio mb-2">
<input type="radio" class="custom-control-input" value="2" id="contracts" name="choice_csv">
<label class="custom-control-label" for="contracts">Contracts</label>
</div>
<div class="custom-control custom-radio mb-2">
<input type="radio" class="custom-control-input" value="3" id="pipeline" name="choice_csv">
<label class="custom-control-label" for="pipeline">Pipeline</label>
</div>
</div>
</form>
</div>
<!-- Second row, second column -->
<div class="col-sm-9 col-md-6 col-lg-8 mb-4">
<div class="input-append date" id="datepicker" data-date="12-02-2012" data-date-format="mm-yyyy">
<input size="12" type="text" value="02-2012">
<span class="add-on"><i class="icon-th"></i></span>
</div>
</div>
</div>
</div>
<div class="container">
<form action="/upload-csv" method="POST", enctype="multipart/form-data">
<div class="form-group">
<div class="custom-file">
<input type="file" class="custom-file-input" name="csv" id="csv">
<label class="custom-file-label" for="csv">Select csv file</label>
</div>
</div>
<button type="submit" class="btn btn-primary">Upload csv file</button>
</form>
</div>
{% endblock main %}

You currently have two forms on your page, unless you're gathering all your fields/items on the client's side with some custom JS code all the fields you want to be included in the request should be in the same form.
Take for instance this form:
<form action="/action_page" method="post">
<label for="fname">First name:</label>
<input type="text" id="fname" name="first_name"><br><br>
<label for="lname">Last name:</label>
<input type="text" id="lname" name="last_name"><br><br>
<input type="submit" value="Submit">
</form>
This, when submitted, will perform a POST request to the /action_page route and will include two fields: first_name and last_name.
Additionally, while it won't have any effect, your first form has an incorrect type=radio, this kind of attribute is allowed on input elements.
I suggest you take a look at these two pages:
MDN Web Docs - : The Input (Form Input) element
MDN Web Docs - Web forms — Working with user data

Related

Is there anyway to know at which card "add comment" has clicked using flask

I'm trying to add a comment section to my web app however, I want to know which book its "add comment" has clicked I'm thinking about getting the book's ID.
Here is my python
#app.route("/comment", methods=["GET", "POST"])
def comment():
if request.method == "POST":
if not request.form.get("comment"):
return apology("must provide comment", 400)
book = request.form.get("book_id")
print(session["user_id"])
print(book)
id = session["user_id"]
print(id)
user = db.execute("SELECT username FROM users WHERE id=?", id)
db.execute("INSERT INTO comment (comment, user,book_id) VALUES(?, ?,?)", comment, user,book)
return render_template ("index.html")
else:
return render_template ("comment.html")
Here is my html code
<form action="/comment" method="get">
<div class="container">
<div class="row g-3">
{% for book in books %}
<div class="col-12 col-md-6 col-lg-4">
<div class="card">
<img src="{{book.volumeInfo.imageLinks.smallThumbnail}}">
<div class="card-body">
<h5 style="color:red" class="card-title">{{book.volumeInfo.title}}</h5>
<p style="color:blue" class="card-text">{{book.volumeInfo.authors}}</p>
<p style="visibility:hidden" class="card-text">{{book.id}}</p>
<input name="book" style="visibility:hidden" class="form-control mx-auto w-auto" name="book_id" value="{{book.id}}" type="text">
More Info
<button value="{{book.id}}" class="btn btn-primary">add comment</button>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
This HTML element
<input name="book" style="visibility:hidden" class="form-control mx-auto w-auto" name="book_id" value="{{book.id}}" type="text">
has two different values for attribute name, please try removing one of them, that is
<input name="book_id" style="visibility:hidden" class="form-control mx-auto w-auto" value="{{book.id}}" type="text">
and then write if you can access book_id properly in your Flask application. As side if you need to have input invisble to user you might use type="hidden" rather than tinker with styling, in this case
<input name="book_id" value="{{book.id}}" type="hidden">

Contained submit button validating other forms

I have two forms on my page, each with its own fields and submit button, but whenever I use any of them, they always check the overall page fields instead of just the form they're contained in...
The simulator.html has two forms, the first one is this:
<div class="forms">
<div class="form-1">
<form method="post" action="{{ url_for('core.simulator') }}">
<div class="container">
<div class="row g-3">
<div class="col-sm-3">
<label class="form-label"><b>Capital:</b></label>
<input type="text" class="form-control" name="capital_html" required>
</div>
<div class="col-sm-3">
<label class="form-label"><b>Price:</b></label>
<input type="text" class="form-control" name="price_html" required>
</div>
</div>
</div>
<br>
<div class="d-grid gap-2 d-md-flex justify-content-md-start">
<button id="btn" type="submit" class="btn btn-info">Launch simulation!</button>
</div>
</form>
<br>
<p>Units to purchase: <b>{{simulation_units}}</b>
</div>
</div>
And the second one is this:
<h3> Screener</h3>
<div class="forms">
<div class="form-2">
<form method="post" action="{{ url_for('core.simulator') }}">
<div class="container">
<div class="row g-3">
<div class="col-sm-3">
<label class="form-label"><b>Ticker:</b></label>
<input type="text" class="form-control" name="ticker_symbol_html" placeholder="Enter Ticker Symbol" required>
</div>
</div>
</div>
<br>
<div class="d-grid gap-2 d-md-flex justify-content-md-start">
<button id="btn" type="submit" class="btn btn-info">Launch!</button>
</div>
<p>Symbol <b>{{simulation_symbol}}.
</form>
</div>
</div>
The views.py file has the back-end code for each, the first one is this:
def capital_simulator():
if request.method == 'POST':
simulation_capital = request.form.get('capital_html', '')
simulation_price = request.form.get('price_html', '')
try:
simulation_units = math.floor(float(simulation_capital) / float(simulation_price))
except KeyError:
simulation_units == 0
return render_template('simulator.html',form1=form,capital_html=simulation_capital,price_html=simulation_price,simulation_units=simulation_units)
And the second form back-end script is this:
def screener():
if request.method == 'POST':
ticker_symbol = request.form.get('ticker_symbol_html', '')
return render_template('simulator.html',ticker_symbol=ticker_symbol_html,ticker_symbol=simulation_symbol)
I thought by specifically calling the mentioned fields and containing them into forms classes, I would avoid an overall validation, but right now I'm failing to make the submit button focus on their own forms. How can I fix this, please?
You have two forms. One is within the 'form-1' div. One is within the 'form-2' div. Each one has its own <form> and </form> tags, which means they are separate forms. The first form contains two <input> fields: one called capital_html, one called price_html, and a button called btn. The second form has one <input> field called ticker_symbol_html and a button called btn.
Let's say the user fills in the first two fields and clicks the first button. That's going to send a request to whatever the URL is in the <form> tag. In the data, it will send three things:
capital_html=xxx
price_html=yyy
btn=submit
That's it. That's all you get. You don't get any fields from the other form. If the user clicks the other form, all you will get is
ticker_symbol_html=xxx
btn=submit
The problem, as you can see, is there's no easy way for you to tell which form was submitted. If you have to use the same URL for both, the usual way to solve this is to add a hidden field that gets sent with the data, like:
<input type=hidden name="ident" text="form1">
and in the second one:
<input type=hidden name="ident" text="form2">
Now, your handler can say
if request.form.get("ident") == "form1":
# Go handle first form.
else:
# Go handle second form.

How to get data from form and show in dropdown in django

What I m trying to do:
Upload a CSV file(Form #1) and show header of a CSV file in the dropdown (Form #2).
and these forms are on the same page.
What I have tried:
for now I able to upload CSV file and display header in a webpage.
index.html
<form action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
<label for="file1">Upload Files</label>
<div class="custom-file">
<input type="file" accept=".csv" id="file1" name="file" required="True" class="form-control custom-file-input">
<label class="custom-file-label" for="file1"></label>
</div>
</div>
<div class="form-group d-flex justify-content-center">
<button type="submit" class="btn text-white w-50" value="Upload">Upload</button>
</div>
</form>
views.py
def read_csv(request):
csv_file = request.FILES['file']
data = pd.read_csv(csv_file)
i = list(data.head(0))
context = {'loaded_data': i}
return render(request, "WebApp/index.html", context)
You can use AJAX request to handle this. You can check this answer, which has a sample AJAX request. In the views.py file, you can handle the request by checking request.is_ajax() and return the context you produce from CSV file via JsonResponse. At the success part of the AJAX call, you can manipulate the dropdown DOM element. There is a good tutorial here. If you are not familiar with these, feel free to ask about the parts you are confused.
So data was in a list I just have to loop through <option>:
<select class="custom-select" id="inputGroupSelect01">
{% for x in loaded_data %}
<option value="{{ x }}">
{{ x }}
</option>
{% endfor %}
</select>

Why request.method is not getting invoked?

I have a read-only textbox which preloaded value from database which upon a button click sends it's value to a method present in backend to perform DELETE query of sql. The problem is occuring when I am click on the button the method is invoked but the request.method condition is not invoked. It is directly going to the end return statement of the method.
#app.route('/home/delete_reminder/<string:id_val>',methods=['GET','POST'])
#is_logged_in
def delete_reminder(id_val):
if request.method=='POST':
desc = request.form['description']
x = desc.split(',')
cur = mysql.connection.cursor()
cur.execute('DELETE FROM set_reminder WHERE DATE=%s,SUBJECT=%s,DESCRIPTION=%s',[x[0],x[1],x[2]])
cur.execute('DELETE FROM recur WHERE RECUR_NEXT=%s',[id_val])
flash('Reminder Deleted','danger')
mysql.connection.commit()
cur.close()
return redirect(url_for('search_reminder_to_delete'))
This is my backend code.
<form method="POST">
{% for data in value %}
<div class="form-group">
<label>Description</label>
<input type="text" name="description" class="form-control" readonly="true" value="{{data.DATE}},{{data.SUBJECT}},{{data.DESCRIPTION}}">
</div>
Delete Reminder
{% endfor %}
</form>
This is the html part.
Your button isn't a button, it's a link. You aren't submitting your form.
If you want to fo that then you need to make you form tag:
<form method="POST" action="/home/delete_reminder/{{data.RECUR_NEXT}}">
and switch your button to be a real button that submits the form:
<div class="button">
<button type="submit" class="btn btn-warning">Send your message</button>
</div>
EDIT: Seeing that you want to have multiple possible routes for your form based on the loop.
You could try and use the formaction attribute, although it isn't going to be supported by every browser version.
<form method="POST">
{% for data in value %}
<div class="form-group">
<label>Description</label>
<input type="text" name="description" class="form-control" readonly="true" value="{{data.DATE}},{{data.SUBJECT}},{{data.DESCRIPTION}}">
</div>
<div class="button">
<button formaction="/home/delete_reminder/{{data.RECUR_NEXT}}" class="btn btn-warning" type="submit" class="btn btn-warning">Delete Reminder</button>
</div>
{% endfor %}
However this will still result in your description field that is passed to the request having every single description from the whole form in a list or possibly just the last one as a single value (I can't quite remember the behaviour of multiple inputs with the same name), which I don't think is what you're expecting to happen.
It may just be easiest to create a separate form for each link in a loop to be honest:
{% for data in value %}
<form method="POST" action="/home/delete_reminder/{{data.RECUR_NEXT}}">
<div class="form-group">
<label>Description</label>
<input type="text" name="description" class="form-control" readonly="true" value="{{data.DATE}},{{data.SUBJECT}},{{data.DESCRIPTION}}">
</div>
<div class="button">
<button class="btn btn-warning" type="submit" class="btn btn-warning">Delete Reminder</button>
</div>
</form>
{% endfor %}

Receiving HTML request from Bootstrap <button>

<form method="post" class="form-horizontal" role="form">
<div class="btn-group col-sm-3">
<button id="typeSelect" type="button" name="reportType" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
People <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li>
...
</li>
...
</ul>
</div>
<div class="form-group">
<label for="inputName" class="col-sm-2 control-label">Name</label>
<div class="col-sm-3">
<input type="text" class="form-control" id="inputName" placeholder="Name" name="name" value="{{name}}" onblur="isEmpty(this)">
</div>
</div>
</form>
I have two inputs in my form and I am trying to get the input in my Python code.
report_type = self.request.get('reportType')
name = self.request.get('name')
name is working correctly, but report_type will always be None.
What is the correct way to retrieve the selection in a button (Bootstrap)?
According to the button element specifications:
A button (and its value) is only included in the form submission if the button itself was used to initiate the form submission.
You will have to find another way to pass the value of the button. You could, for example, store the value in a hidden field.

Categories