Passing data from HTML form through Flask to an API [duplicate] - python

This question already has answers here:
Get the data received in a Flask request
(23 answers)
Post values from an HTML form and access them in a Flask view
(2 answers)
Closed 3 years ago.
So I'm currently making an application in Python that would show live departures and then show a countdown when there are 0 minutes to departure. What my issue is that I don't want to edit the code to change stops, I want to just be able to input the route_type and stop from the html formand then pass that via Flask to the API.
The API is the PTV Timetable API that includes real time departures.
The API has a Swagger page so I know what to insert.
So what I've done is added the authentication code and then added the forms from bootstrap into the HTML file. I've tried googling the problem but I don't really know how the HTML forms can talk to flask so it can get added to the API.
Thanks
Python Code
def getUrl(request):
DevId = <ID>
apikey = <KEY>
request = request + ('&') if ('?' in request) else '?')
raw = 'request' +'DevId={0}'.format(DevId)
hashkey = hmac.new(key, raw, sha1)
signature = hashkey.hexdigest()
return ''https://timetableapi.ptv.vic.gov.au'+raw+'&signature={1}'.format(devId, signature)
from_zone = tz.gettz('UTC')
to_zone = tz.gettz ('Australia/Melbourne')
#get next departures
url = getUrl('/v3/departures/route_type/{route_type}/stop/{stop_id}')
#app.route('/')
#app.route('home')
def home():
return flask.render_template('home.html')
HTML Code
<body>
<form>
<div class="form-group">
<label for="station-id-input">Station ID</label>
<input type="text" class="form-control" placeholder="Station ID">
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="inline-train" value="route_type0">
<label class="form-check-label" for="inline-train">Train</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="inline-tram" value="route_type1">
<label class="form-check-label" for="inline-train">Tram</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="inline-bus" value="route_type2">
<label class="form-check-label" for="inline-train">Bus</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="inline-vline" value="route_type3">
<label class="form-check-label" for="inline-train">V/Line</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" id="inline-nightbus" value="route_type4" disabled>
<label class="form-check-label" for="inline-train"Night Bus (Not Implemented)</label>
</div>
</body>

So a good and well documented way to communicate with flask from html is via flask wtforms. It helps you essentially to validate your forms in the html and to secure the POST requests from the frontend usually via a CSRF token.
From the documentation you have a minimal example that could be a good starting point for you:
from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired
class MyForm(FlaskForm):
name = StringField('name', validators=[DataRequired()])
HTML
PS: The curling brackets are from jinja2, a templating language for python.
<form method="POST" action="/">
{{ form.hidden_tag() }}
{{ form.name.label }} {{ form.name(size=20) }}
<input type="submit" value="Go">
</form>
Validating
#app.route('/submit', methods=('GET', 'POST'))
def submit():
form = MyForm()
if form.validate_on_submit():
return redirect('/success')
return render_template('submit.html', form=form)
It is fairly straightforward, you create a form, pass it to the frontend together in the routing and then when the user submits it it tries to validate it.
With all that said, if all you want is to simply send a form data and process that in flask, all you really need to do is create / accept POST requests in your endpoint:
#app.route('/', methods=('GET', 'POST'))
#app.route('home')
def home():
if request.method == 'POST':
form = request.form
value1 = form['value1']
# do something with the values from the form.
return flask.render_template('home.html')
I would not recommend due to validation / security concerns, but it could be a starting point if you are new to flask.

Related

Store dynamic choice field form from api call to be able to pass the verification in post request

I'm working on an app that can display the events coming from a google calendar.
To be able to do this, the user need to fill a form with the start date, end date, timezone and select a calendar.
I'm new to Django and it's ok to make a form with date, text, checkbox but regarding the choice field, it fail because the values are not present in the choice list.
Select a valid choice. johndoe#gmail.com is not one of the available choices.
This is normal because the values will change according to the user.
For that I'm calling the google calendar api before showing the page at GET request.
I tried to add it to the form but of course, it doesn't stay while the post method is called.
Is there a way to store the value without using the session or database?
How do you manage dynamic choice field that isn't coming from database?
Here is my code:
form.py
from django import forms
class events_filters(forms.Form):
start = forms.DateField(label='Start date')
end = forms.DateField(label='End date')
calendar = forms.ChoiceField(label='Select calendar')
timezone = forms.BooleanField(label="Show timezone")
view.py
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials
from allauth.socialaccount.models import SocialApp, SocialAccount
import csv
from django.http import HttpResponse
from .forms import events_filters
# Create your views here.
# Main page
#login_required(login_url='/accounts/google/login/')
def index(request):
if request.method == "GET":
creds = create_credentials(request)
calendars = getCalendars(creds) #function to get the calendars
form = events_filters()
form.fields["calendar"].choices = calendars #tried to add the calendars to the form, it work but of course doesn't stay for the post request
return render(request, "ts_index.html", context={"calendars":calendars, 'form': form})
if request.method == "POST":
form = events_filters(request.POST)
if form.is_valid(): # Failed here because the form doesn't know the values to be validated. I would like to be able to validate the data without passing by the client to be sure that the user use an email in the list. I would also like to avoid to call the calendar api again.
parameters = {
"start_date" : form["start"] + "T00:00:00.000Z",
"end_date" : form["end"] + "T00:00:00.000Z",
"calendar_id" : form["calendar"],
}
# Use the access token to authenticate with the Google Calendar API
creds = create_credentials(request)
events = getEvents(creds, parameters["start_date"], parameters["end_date"], parameters["calendar_id"])
return render(request, "ts_input_data.html", context={'events':events, "parameters": parameters})
else :
print("Data not valid")
Html page
{% extends 'head.html' %}
{% load socialaccount %}
<!--Block content goes below-->
{% block content %}
<h1>Input data</h1>
<!-- Select the dates -->
<form method="POST" class="d-flex flex-column justify-content-center align-items-center" >
<!-- Key for CSRF -->
{% csrf_token %}
<!-- Select the agenda -->
<div class="mb-3 w-100">
<label for="calendar" class="form-label">Select calendar</label>
<select name="calendar" id="calendar_id" class="form-control">
{% for c in calendars %}
<option value=" {{ c.0 }} ">{{ c.1 }}</option>
{% endfor%}
</select>
</div>
<!-- Start date -->
<div class="mb-3 w-100">
<label for="start_id" class="form-label">Start date</label>
<input type="date" class="form-control" id="start_id" name="start">
</div>
<!-- End date -->
<div class="mb-3 w-100">
<label for="end" class="form-label">End date</label>
<input type="date" class="form-control" id="end_id" name="end">
</div>
<!-- End date -->
<div class="mb-3 w-100">
<label for="timezone" class="form-label">Show time-zone</label>
<input type="checkbox" class="form-check-input" id="timezone_id" name="timezone" checked>
</div>
<!-- Submit -->
<button type="submit" class="btn btn-primary w-100">Submit</button>
</form>
{% endblock %}

POST Method not allowed for a simple lookup webpage

I have a simple page with a data entry field and a click button, this will run the API to retrieve the coin data
running the code in a python terminal return with success, but when I try to add it to flask and use the webpage, I get the error 405 method not allowed for the POST.
This is the main python/flask file:
crypto.py
# template libraries
from flask import render_template,url_for,flash,request,redirect,Blueprint
# Coingecko API library
from pycoingecko import CoinGeckoAPI
crypto_simulator = Blueprint('crypto_simulator',__name__)
#crypto_simulator.route('/crypto_simulator', methods=['GET','POST'])
#login_required
def crypto_insert():
if request.form.get("ident") == "formCrypto":
print('Hello')
cg = CoinGeckoAPI()
#crypto_token = request.form.get('crypto_name_html', '')
crypto_token = 'bitcoin'
crypto_currency = 'usd'
response = cg.get_price(ids=crypto_token,
vs_currencies='usd',
include_market_cap='true',
include_24hr_vol='true',
include_24hr_change='true',
include_last_updated_at='true')
crypto_result = response.get(crypto_token,'')
print(crypto_result[crypto_currency])
return render_template('crypto_simulator.html',
formCryptoSimulation=form,
crypto_token=crypto_token,
crypto_currency=crypto_currency,
crypto_result=crypto_result
)
This is the Blueprint routing file:
core.py
# crypto section
#core.route('/crypto_simulator')
def crypto_simulator():
return render_template('crypto_simulator.html')
This is the Flask/Bootstrap front-end:
crypto_simulator.html
{% extends "base.html" %}
{% block content %}
<!-- Simulation Code Start -->
<div class="forms">
<div class="formCrypto">
<form method="post" action="{{ url_for('core.crypto_simulator') }}">
<div class="container">
<div class="row g-3">
<div class="col-sm-3">
<label class="form-label"><b>Crypto Name:</b></label>
<input type="text" class="form-control" name="crypto_name_html" placeholder="Enter Crypto Name" 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">Check Token!</button>
</div>
<input type=hidden name="ident" value="formCrypto">
</form>
<br>
<p>Token: <b>{{crypto_token}}</b>
<p>Price: <b>{{crypto_result}}</b>
</div>
</div>
{% endblock %}
I checked for misspelled lines and anything related but still stuck into how to fix it...
Your form has this:
<form method="post" action="{{ url_for('core.crypto_simulator') }}">
So you are calling the function crypto_simulator in blueprint core:
#core.route('/crypto_simulator')
def crypto_simulator():
return render_template('crypto_simulator.html')
Note that your form does a POST request, so you very logically have to enable the POST method on the function being called like this:
#core.route('/crypto_simulator', methods=['GET','POST'])

Django model works on local but not after deploy

I am making a simple personal website. I make a box to input user data (name, email, and message). I want this data is sent to my django admin. I have test it and it works in local. But when i deploy it, i didn't get any data whenever i submit from the box. Note : i do not use django form for some reason. I want to use my own custom form.
this is my views.py
from django.shortcuts import render
from django.http import HttpResponse
from .models import Feedback
from .forms import FeedbackForm
def index(request):
if (request.method == "POST"):
name = request.POST.get("Name")
email = request.POST.get("Email")
message = request.POST.get("Message")
record = Feedback(name=name, email=email, message=message)
record.save()
return render(request, 'main.html')
this is my models.py
from django.db import models
class Feedback(models.Model) :
name = models.CharField(max_length=50)
email = models.EmailField()
message = models.CharField(max_length=200)
def __str__(self):
return self.name
this is my html form
<form action="{% url 'index' %}" method="POST" target="_self">
{% csrf_token %}
<div class="w3-row-padding" style="margin:0 -16px 8px -16px">
<div class="w3-half">
<input class="w3-input w3-border" type="text" placeholder="Name" required name="Name" >
</div>
<div class="w3-half">
<input class="w3-input w3-border" type="email" placeholder="Email" required name="Email">
</div>
</div>
<input class="w3-input w3-border" type="text" placeholder="Message" required name="Message" >
<button class="w3-button w3-black w3-right w3-section" type="submit">
<i class="fa fa-paper-plane"></i> SEND MESSAGE
</button>
</form>
I'm not sure what platform you have deployed the project on, but one of the first things I would try is to make sure you have run database migration. (Which I'm assuming you have done if you have access to the admin area, but it might be worth running again just to make sure.)
python manage.py makemigrations
python manage.py migrate
I made some modifications to your HTML form to more closely follow the Django documentation. Notably, the addition of enctype="multipart/form-data" and an id tag to each input: i.e. id="name". Here is a link to the documentation: https://docs.djangoproject.com/en/3.1/topics/forms/
<form action="{% url 'index' %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<div class="w3-row-padding" style="margin:0 -16px 8px -16px">
<div class="w3-half">
<input class="w3-input w3-border" type="text" placeholder="Name" name="Name" id="name" required>
</div>
<div class="w3-half">
<input class="w3-input w3-border" type="email" placeholder="Email" name="Email" id="email" required>
</div>
</div>
<input class="w3-input w3-border" type="text" placeholder="Message" name="Message" id="message" required>
<button class="w3-button w3-black w3-right w3-section" type="submit"><i class="fa fa-paper-plane"></i>SEND MESSAGE</button>
</form>
It could be an issue with copying/pasting code, but I noticed the fields on your model are over-indented. Make sure you are using a single tab before each field.
# models.py
class Feedback(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField()
message = models.CharField(max_length=200)
def __str__(self):
return self.name
If you are able to see the terminal, are you getting a 200 response with your POST request? It should look something like this:
"POST / HTTP/1.1" 200
One of the other things I'd check is to ensure your database is configured properly in settings.py (This is assuming you are using the sqlite database)
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
If you aren't seeing anything in the admin area, make sure you have registered your model in admin.py
from django.contrib import admin
from .models import Feedback
admin.site.register(Feedback)

Employing Post Method in Flask

Using the following:
from flask import Flask, render_template
import beautiful_soup_tidal
app = Flask(__name__)
#app.route('/')
def form():
return render_template('form_submit.html')
#app.route('/richmond', methods=['POST'])
def richmond():
someTides = beautiful_soup_tidal.getTides()
return render_template('richmond.html',someTides=someTides)
if __name__ == "__main__":
app.run(debug=True)
And attempting to render the following (richmond.html):
<div id="content" class="form-group">
<form method="post" action="/richmond">
<label style="vertical-align: middle;">channel depth at mean low water
<input type="number" step="0.1" value = "34.5" name="channelDepth"/>FEET</label><br><br>
<label style="vertical-align: middle;">required underkeel clearance
<input type="number" step="0.1" value = "2" name="underkeelClearance"/>FEET</label><br><br>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
I get the following error: 'The method is not allowed for the requested URL.'
If I delete ', methods=['POST']' in the first section the template renders.
The question: How do I render the template successfully using the post method?
i believe this line should also include GET so that you can render the html form first time round before you actually click submit to post it.
#app.route('/richmond', methods=['POST'])
so it would change to
#app.route('/richmond', methods=['GET', 'POST'])

How to call a different render after a form submission

I'm diving into Flask for the first time and I'm having some trouble getting something work.
I currently have a template for when my tab values is empty, it contains a form that when submitted should call a specific function using the parameters of the form and return another template. Each call of the form should in, fact call the index.html template with different values.
Relevant parts of code are as follows:
main.py
#app.route('/', methods=['POST','GET'])
def main():
global maxDepth, numberOfRelated
if not values:
return render_template('initial.html')
if request.method=='POST':
url = request.form['url']
maxDepth = request.form['depth']
numberOfRelated = request.form['numberOfRelated']
values = crawling(url,maxDepth,numberOfRelated)
return render_template('index.html',var=values)
The form part of initial.html and index.html are actually the same
<form class="form-inline" action="/" method="POST">
<div class="form-group">
<input name='url' type="text" class="form-control"/>
</div>
<div class="form-group minorForm">
<input name='numberOfRelated' type="text" class="form-control" />
</div>
<div class="form-group minorForm">
<input name='depth' type="text" class="form-control" />
</div>
<div class="form-group navbar-right">
<button class="btn btn-success minorForm generate" type="submit"> Generate</button>
</div>
</form>
In your main method, unless values is global, it won't be defined for if not values.
As to your question, add another render_template call just after the conditional for if the form was submitted:
if request.method=='POST':
url = request.form['url']
maxDepth = request.form['depth']
numberOfRelated = request.form['numberOfRelated']
values = crawling(url,maxDepth,numberOfRelated)
return render_template('index.html',var=values) # 1
return render_template('index.html',var=values) # 2
If the form is submitted, the conditional will be true and the template will be rendered at comment #1. If the user is navigating to the page normally, the conditional will be false and the line with comment #2 will be called.
I'm a bit confused about the question, but you should always redirect after a POST (unless there was an error in the form and no action was taken). That way the same action won't be repeated if the user reloads the page.

Categories

Resources