Using Flask and SQLAlchemy on my localhost, I am looking to be able to submit a simple contact form and, using AJAX, pass the submitted data along to my Flask API and then insert it into my local database, named contact.db.
To set up my database, I put together a script named setup.py, which successfully creates a database in my working directory. Its contents look as follows:
from sqlalchemy import create_engine, ForeignKey
from sqlalchemy import Column, Date, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref
engine = create_engine('sqlite:///contact.db', echo=True)
Base = declarative_base()
########################################################################
class Contact(Base):
""""""
__tablename__ = "contact"
id = Column(Integer, primary_key=True)
f_name = Column(String)
l_name = Column(String)
email = Column(String)
message = Column(String)
#----------------------------------------------------------------------
def __init__(self, f_name, l_name, email, message):
""""""
self.f_name = f_name
self.l_name = l_name
self.email = email
self.message = message
# create tables
Base.metadata.create_all(engine)
My simple contact page collects the data and submits it to my flask route /contact/request using AJAX (I have confirmed this to work via the console). For reference, however, here is the code I use in contact.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Contact</title>
<!-- Bootstrap -->
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://code.jquery.com/jquery.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
<script>
$(document).ready(function(){
$("#submitForm").click(function()
{
var firstName = $("#f_name").val();
var lastName = $("#l_name").val();
var email = $("#email").val();
var mess = $("#mess").val();
var nud = {
"f_name" : firstName,
"l_name" : lastName,
"email" : email,
"message" : mess
}
$.ajax({
type: "POST",
url: "/contact/request",
data: JSON.stringify(nud, null, '\t'),
contentType: 'application/json;charset=UTF-8',
success: function(result) {
console.log(result);
}
})
});
})
</script>
</head>
<body>
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-heading">
<h1 style="text-align: center">Contact Me</h1>
</div>
<div class="panel-body">
<form role="form">
<div class="form-horizontal" class="form-group" style="width:50%">
<label for="name has-success">First Name</label>
<input class="form-control input-md" type="text" class="form-control" id="f_name" placeholder="Matthew">
</div><br />
<div class="form-horizontal" class="form-group" style="width:50%">
<label for="email">Last Name</label>
<input class="form-control input-md" type="text" class="form-control" id="l_name" placeholder="Gross">
</div><br />
<div class="form-horizontal" class="form-group" style="width:50%">
<label for="email">Email</label>
<input class="form-control input-md" type="email" class="form-control" id="email" placeholder="mattkgross#gmail.com">
</div><br />
<div class="form-group">
<label for="aboutMe">Message</label>
<textarea class="form-control" id="mess" placeholder="What's up?" rows="3" ></textarea>
</div>
<div>
<button type="button" input type "submit" class="btn btn-success" id="submitForm">Submit</button>
</div>
</form>
</div>
</div>
</div>
<div class="col-md-3"></div>
</div>
</body>
</html>
Finally, I have my actual Flask API script that I run in order to start up my service on localhost. The /contact route works fine. However, when I send the data via my form submission, I get an internal server error. This is undoubtedly being caused by my incorrect attempt at inserting the parsed JSON into my contact database. Below is the code used in my api.py:
from flask import Flask
from flask import request
from flask import render_template
import datetime
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from setup import Contact
app = Flask(__name__)
#app.route('/contact')
def contact():
return render_template('contact.html')
#app.route('/contact/request', methods=["POST"])
def contact_request():
if request.method == "POST":
engine = create_engine('sqlite:///contact.db', echo=True)
# Create a Session
Session = sessionmaker(bind=engine)
session = Session()
new_contact = Contact(request.json['f_name'],
request.json['l_name'],
request.json['email'],
request.json['message'])
# Add the record to the session object
session.add(new_contact)
# commit the record the database
session.commit()
#return str(request.json)
app.debug = True
app.run()
If I comment out the two lines:
session.add(new_contact)
session.commit()
and replace them with return str(request.json), my console successfully returns the JSON I sent. I am just completely lost as to where I am going wrong in inserting my data into the database and why it is throwing an error at me for my attempt.
Any help you can give me would be very much appreciated - hopefully it's something simple I overlooked in being new to this whole thing. Thanks!
In flask you have to return something for your route otherwise it will lead to odd behavior. In your case you could return something as simple as an "OK" to let your AJAX know the function completed successfully.
Related
I try to create my own session middleware and my uauth system using Django. I've got the following code:
class CheckSessionsExistMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
...
response = self.get_response(request)
...
response.status_code = 401
response.reason_phrase = 'Unauthorized'
realm = 'some_app'
response['WWW-Authenticate'] = f'Basic real={realm}'
return response
When i set to respone header 'WWW-Authenticate' the string that contains word 'Basic', Django returns "internal" the sign in form and i do not understand.
Link to example of "internal" sign in form that Django returns: https://psv4.userapi.com/c235131/u2000021998/docs/d23/bc9d828c3755/file.png?extra=0qo4COrPeQh3mwjuRs1WoYsB3GVW8WB-Xn01jjtQz7muPVFWRqKjsm0itRqJFhOqjoYoKQGpAqWbG4xNQlJD3_kcs1u0UNct_76s6b1jv0u9oW76cnH2bGBTWWt_hpSQY-OpgxtRj_h56RPnwMZC73NRHw
Is there a way to disable this behavior?
Example of returning html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form id="login_form" action="http://127.0.0.1:8000/auth/register/" method="post">
<label for="username_input">
Usesrname
</label>
<input type="text" id="username_input" name=""> <br>
<label for="password_input">
Password
</label>
<input type="text" id="password_input" style="margin-left: 10px"> <br>
<input type="submit" value="Sign in">
</form>
Is it first time? Sign up
</body>
</html>
I expected that Django just return to client the header 'WWW-Authenticate' with value 'Basic realm=some_app' and my html form without "internal" sign in form.
I have two input buttons. One is for uploading a file, and the other one is a submit button that adds the uploaded file to the database.
My problem is, after I submit the file, the first button that's used for uploading goes back to "No file chosen" next to the button. However, I want the uploaded file name to "stick" to the UI/html page as the file chosen.
Here is my File class:
class Files(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, unique=True, nullable=False)
data = db.Column(db.LargeBinary)
Here is my HTML code:
<td>
<form class="" action="{{url_for('main.upload_file')}}" method="post" enctype="multipart/form-data">
<input type="hidden" name="id" value="{{row.id}}">
<input style="margin-bottom: 5px;" type="file" accept=".csv" name="csvfile" id="upload" value ="{{row.name}}"> <br>
<input style="margin-bottom: 10px;" type="submit" name="" value="Submit"> <br>
</form>
<a href={{ url_for('main.files') }}>Go to Downloads Page</a>
<br>
</td>
I've tried making the value attribute equal to the file name getting passed in ex. value ="{{row.name}}" for the file type <input> above, but that doesn't keep the file chosen name on the page after submission either. I can't find any videos or posts that deal with this problem, so I would really appreciate any guidance. TIA!
I think setting a default value for an input field of type file is forbidden for security reasons.
However, if you want to keep the name of the selected file, you can aim for a transfer with AJAX. Here you can suppress the standard behavior of the form. The page is not reloaded and the form is not reset.
The example below shows you how it works.
Flask (app.py)
from flask import Flask
from flask import (
render_template,
request,
)
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/upload-file', methods=['POST'])
def upload_file():
if 'csvfile' in request.files:
file = request.files['csvfile']
if file.filename != '':
# handle file here!
return '', 200
return '', 400
HTML (templates/index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<form name="my-form" method="post">
<input type="file" name="csvfile" />
<input type="submit" />
</form>
<script type="text/javascript">
((uri) => {
const elem = document.querySelector('form[name="my-form"]');
elem.addEventListener('submit', evt => {
evt.preventDefault();
const formData = new FormData(evt.target);
fetch(uri, {
method: 'post',
body: formData
}).then(resp => {
if (resp.status === 200) {
console.log('Submit successful.');
}
});
})
})({{ url_for('.upload_file') | tojson }});
</script>
</body>
</html>
I don't know if it is possible to default the value the file-input field shows but what you could do is just have a row above the input field showing the currently uploaded/chosen file (if there is any). So something like this:
<td>
<form class="" action="{{url_for('main.upload_file')}}" method="post" enctype="multipart/form-data">
<input type="hidden" name="id" value="{{row.id}}">
{% if row.name %}
<p>Currently chosen file: {{row.name}}</p>
<p>You can select a new file below</p>
{% endif %}
<input style="margin-bottom: 5px;" type="file" accept=".csv" name="csvfile" id="upload"> <br>
<input style="margin-bottom: 10px;" type="submit" name="" value="Submit"> <br>
</form>
<a href={{ url_for('main.files') }}>Go to Downloads Page</a>
<br>
</td>
I know this question has been asked before but I have been through all the posts and none of the solutions seem to work for me.
Please bear with me. I am new to Flask and html and trying to build my first web app.
It's supposed to work as follows: The user uploads an Excel workbook and the workbook headers are displayed in a dropdown list using the html "select" tag. The user should then select one of the headers. I would then like to pass the selected header into a function.
I am able display the workbook headers in the dropdown list, but when I select a header, nothing happens. Does anyone have any idea what I'm doing wrong?
Please see python code below:
import flask
from flask import Flask
from flask import request
import pandas as pd
app = Flask(__name__)
#app.route("/", methods=["GET", "POST"])
def index():
global headers_list
headers_list=[]
if request.method == "POST":
df=request.files["file"]
if df:
df=pd.read_excel(df)
headers_list=get_headers(df)
selected_header = request.form.get("header")
print(str(selected_header)) #to test the code
else:
print("No file selected")
return (flask.render_template("./index.html", headers_list=headers_list))
def get_headers(dataframe):
headers=list(dataframe.columns)
return headers
if __name__ == "__main__":
app.run(host="127.0.0.1", port=8080, debug=True)
HTML below:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="description" content=""
<title><Contra Tool</title>
</head>
<body>
<h1>Contra Tool</h1>
<p>Describe functionality</p>
<br/>
</body>
<form action="" method="post" enctype=multipart/form-data>
<label for ="myfile" > Select a file:</label>
<input type="file" id="myfile" name="file" EnableViewState=True>
<input type="submit" value="Upload">
</form>
<br><br>
<div class="dropdown">
<button class="dropbtn">Dropdown</button>
<div>
<form action="" method="POST">
<SELECT class="dropdown-content" name="header" method="POST" action="/">
<ul>
<option value="{{headers_list[0]}}" selected>{{headers_list[0]}}</option>
{% for h in headers_list[1:]%}
<option value="{{h}}">{{h}}</option>
{% endfor %}
</ul>
</SELECT>
</form>
</div>
</div>
<br/>
<input type="submit">
</html>
Since I am assuming that you do not want to save the excel file on the server, in my opinion there remains a variant in which the file is transferred twice.
If the user selects a file, it is transferred in the background to query the header columns. The select element is filled with the information received.
From now on a column can be selected and the form can be transferred.
In my example there are two routes. One to display and process the form and another which on request returns the header columns in JSON format.
from flask import Flask
from flask import abort, jsonify, render_template, request
import pandas as pd
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def upload():
if request.method == 'POST' and 'file' in request.files:
file = request.files['file']
df = pd.read_excel(file)
head = request.form.get('head');
print(f'selected "{head}"')
return render_template('index.html')
#app.route('/headers', methods=['POST'])
def headers():
if 'file' in request.files:
file = request.files['file']
df = pd.read_excel(file)
headers = list(df.columns)
return jsonify(headers=headers)
abort(400)
If the user selects a file, it is sent to the second route via AJAX. The select element is emptied and refilled and all necessary further elements are made available after the response from the server has been received.
If the user presses submit, the completed form is sent with the file and the selected column.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<form method="POST" enctype="multipart/form-data">
<input type="file" name="file" id="file" />
<select name="head" id="head" disabled>
<option>Choose Header</option>
</select>
<input type="submit" id="submit" disabled />
</form>
<script type="text/javascript">
(() => {
const fileElem = document.getElementById('file');
fileElem.addEventListener('change', evt => {
const formData = new FormData();
formData.append('file', evt.target.files[0]);
fetch('/headers', { method: 'POST', body: formData })
.then(resp => resp.json())
.then(data => {
// clear select options
const selectElem = document.getElementById('head');
for (let i=selectElem.options.length-1; i >= 0; --i) {
selectElem.remove(i);
}
// populate select options
const headers = data['headers'];
for (const head of headers) {
const optElem = document.createElement('option');
optElem.value = head;
optElem.innerHTML = head;
selectElem.append(optElem);
}
selectElem.disabled = false;
const elem = document.getElementById('submit');
elem.disabled = false;
});
});
})();
</script>
</body>
</html>
Remember, this is a minimal example and may need to be adapted and revised to meet your requirements.
When I try to add a new task on my website I get the message "POST /templates/todo" Error (404): "Not found" and cannot find what is wrong with the code. Can anyone help me figure it out? It was done on cs50ide software. If anyone could inform me whether I am able to create a functioning link for my website (I use python, flask, html and css) I would be very grateful! Thank you so much
add.html code:
<!DOCTYPE html>
<html lang="en">
<head>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/css/styles.css" rel="stylesheet">
<title>
Add a New Task :)
</title>
</head>
<body>
<h1 class = "aligncenter">
<img class = "img1" src = "/static/images/logocarol.jpg" alt = "Logo" height = "200" width = "550"/>
</h1>
<h1 class="centergeneral fontsize">
Add any goals, dreams and aspirations you might have here:
</h1>
<form class="aligncenter" action="todo" method="POST">
<input id="task" name="task" type="text" placeholder="New Task :)">
<input id="submit" type="submit" disabled>
</form>
<script>
document.querySelector('#task').onkeyup = function(){
if (document.querySelector('#task').value === ''){
document.querySelector('#submit').disabled = true;
} else {
document.querySelector('#submit').disabled = false;
}
}
</script>
<form action="/">
<button type="submit" id = "back" class="btn btn-info margin"> BACK TO HOMEPAGE </button> <br> <br>
</form>
<form action="todo">
<button type="submit" id = "back" class="btn btn-outline-info margin"> BACK TO TO DO LIST </button> <br> <br>
</form>
</body>
todo.html code:
<!DOCTYPE html>
<html lang="en">
<head>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/css/styles.css" rel="stylesheet">
<title>
To Do List! :)
</title>
</head>
<body>
<h1 class = "aligncenter">
<img class = "img1" src = "/static/images/logocarol.jpg" alt = "Logo" height = "200" width = "550"/>
</h1>
<h1 class="fonts centergeneral"> To Do List </h1>
<h2 class="fs-4 centergeneral"> What I Want to Achieve: </h2> <br>
<ul class="listcenter">
<script>
{%for todo in todos%}
<li> {{ todo }} <input type="checkbox" id="checkbox1"> </li>
{%endfor%}
</script>
</ul>
<a class="btn btn-outline-info margin" href = "add"> Add a New Task</a>
<a class="btn btn-outline-info" href = "clear"> Clear Tasks </a> <br>
<div class="backbuttons">
<form action="/">
<button type="submit" id = "back" class="btn btn-info"> BACK TO HOMEPAGE </button> <br> <br>
</form>
</div>
application.py code:
from flask import Flask, render_template, send_from_directory, request, redirect, session
from flask_session import Session
from cs50 import SQL
app = Flask(__name__)
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/f1inschools')
def f1():
return render_template('f1inschools.html')
#app.route('/pdwt')
def pdwt():
return render_template('pdwt.html')
#app.route('/pros')
def pros():
return render_template('pros.html')
#app.route('/bookrecommendations')
def books():
return render_template('bookrecs.html')
#app.route('/todolist')
def todo():
if "todos" not in session:
session["todos"] = []
return render_template('todo.html', todos=session["todos"])
#app.route('/clear')
def clear():
return redirect("/todolist")
session["todos"] = []
#app.route('/add', methods=["GET", "POST"])
def add():
if request.method == "GET":
return render_template("add.html")
else:
todo = request.form.get("task")
session["todos"].append(todo)
return redirect("/todolist")
The function for adding tasks is at app.route("/add",methods=["GET","POST"]), but your form in the HTML has action="todo", so your form tries to send data to /todo which is nonexistent. To fix, simply change action="todo" to action="add".
I'm using Python on Google App Engine as the backend for a Desktop/Mobile web app for video sharing. I'm having an issue uploading from an iPhone to the blobstore. Usually the page redirects after the upload URL is created, but this doesn't happen on the phone. Instead the browser navigates to the upload URL, but nothing is uploaded.
I can select a video to upload just fine, and if it's a long video, the phone will take awhile to navigate to the next page which seems to imply that something is being transferred, but nothing ends up in the blobstore.
Videos are uploaded with the following Python code.
class UploadPage(webapp2.RequestHandler):
def get(self):
upload_url = blobstore.create_upload_url('/uploadvideo')
template_values = {
'upload_url': upload_url,
}
template = JINJA_ENVIRONMENT.get_template('upload.html')
self.response.write(template.render(template_values))
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
def post(self):
upload = self.get_uploads()[0]
video = Videos(content=upload.key())
video.title = self.request.get('title')
video.description = self.request.get('description')
video.ratingDown = 0
video.ratingUp = 0
video.creator = users.get_current_user().nickname()
uniuqeIDFound = False
newID = random.randint(1000,9999)
while(uniuqeIDFound == False):
vids = db.GqlQuery("SELECT * "
"FROM Videos ")
uniuqeIDFound = True
for v in vids:
if (v.videoID == newID):
newID = random.randint(1,10000)
uniuqeIDFound = False
video.videoID = newID
db.put(video)
self.redirect('/home')
The upload page itself looks like this.
<!DOCTYPE html>
<html>
<head>
<title> Upload </title>
<meta charset="utf-8">
<link type="text/css" rel="stylesheet" href="/stylesheets/bootstrap.css" />
<link type="text/css" rel="stylesheet" href="/stylesheets/main.css" />
</head>
<body>
<div class="fill">
<div class="container">
<ul class="nav nav-tabs">
<li>Home</li>
<li class="active">Upload Video</li>
<li>Logout</li>
<li>Contact Us</li>
</ul>
<h2 class="addColor">Tribal Knowledge</h2><h3 class="addColor">Upload a Video</h3>
<form class="form-horizontal" action="{{ upload_url }}" method="POST" enctype="multipart/form-data">
<textarea placeholder="Title..." name="title" cols="50" rows="1" autofocus="autofocus" required></textarea><br>
<textarea placeholder="Description..." name="description" cols="50" rows="4" autofocus="autofocus"></textarea><br>
Upload File: <input type="file" name="file"><br> <input class="btn btn-success" type="submit" name="submit" value="Upload Video">
</form>
</div>
</div>
</body>
</html>