- Generally speaking, commits can either succeed or fail. On failure, we would generally want to roll back the session in order to avoid any potential implicit commits that are done by the database as a result of closing a connection. Because if you were to close your connection, it would implicitly commit ways already pending on your database session even if you didn’t want that to happen.
- Good practice is to close connections at the end of every session used in a controller, to return the connection back to the connection pool.
import sys
try:
todo = Todo(description=description)
db.session.add(todo)
db.session.commit()
except:
db.session.rollback()
error=True
print(sys.exc_info())
finally:
db.session.close()
from flask import Flask, render_template, request, redirect, url_for, jsonify
from flask_sqlalchemy import SQLAlchemy
import sys
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgres://username@localhost:5432/todoapp'
db = SQLAlchemy(app)
class Todo(db.Model):
__tablename__ = 'todos'
id = db.Column(db.Integer, primary_key=True)
description = db.Column(db.String(), nullable=False)
def __repr__(self):
return f'<Todo {
self.id} {
self.description}>'
# Ensure the tables are created for all the models that we've created and they haven't been created.
db.create_all()
@app.route('/todos/create', methods=['POST'])
def create_todo():
error = False
body = {
}
try:
# get_json is that it fetches the JSON body that was sent to an object key description.
description = request.get_json()['description']
todo = Todo(description=description)
db.session.add(todo)
db.session.commit()
body['description'] = todo.description
except:
error = True
db.session.rollback()
# debugging sentence
print(sys.exc_info())
finally:
db.session.close()
if not error:
# return a useful JSON object that includes that description.
# jsonify will return JSON data to the client for us.
# whatever we pass in as our JSON object.
return jsonify(body)
@app.route('/')
def index():
return render_template('index.html', data=Todo.query.all())
<!DOCTYPE html>
<html>
<head>
<title>Todo app</title>
<style>
.hidden {
display:none;
}
</style>
</head>
<body>
<form id="form">
<input type="text" id="description" name="description" />
<input type="submit" value="Create" />
</form>
<div id="error" class="hidden">Something went wrong!</div>
<ul id="todos">
{% for d in data %}
<li>
{
{ d.description }}
</li>
{% endfor %}
</ul>
<script>
// Select on the form
// onsubmit handler to default wound up sending information to the server
// Using the event object, e
document.getElementById('form').onsubmit = function(e) {
// The default behaviro would have done that full page refresh and
// submitted it using the method and action attributes up
e.preventDefault();
// Send the post requests asynchronously using fetch
fetch('/todos/create', {
method: 'POST',
body: JSON.stringify({
// The value of whatever the user has typed into the description field.
'description': document.getElementById('description').value
}),
headers: {
'Content-Type': 'application/json'
}
})
// give back a promise by which we can then use the
// then method
// callback should give us back a response
.then(function(response) {
// parse out the response which will initially be a string as a JSON response
return response.json();
})
// manipulate the JSON response
.then(function(jsonResponse) {
console.log(jsonResponse);
// Append a child Li element here
const liItem = document.createElement('LI');
liItem.innerHTML = jsonResponse['description'];
document.getElementById('todos').appendChild(liItem);
// it did succeed
document.getElementById("error").className = 'hidden';
})
// catch handler
.catch(function() {
// Remove the class name
document.getElementById("error").className = '';
})
}
</script>
</body>
</html>
expire_on_commit - default to True. When True, all instances will be fully expired after each commit(), so that all attribute/object access subsequent to a completed transaction will load from the most recent database state.
db = SQLAlchemy(app,session_options={"expire_on_commit": False})
The route handler should always return something or raise an intentional exception, in the case of an error. To fix this with a simple solution, we can simply import abort from Flask:
from flask import abort
and we can call abort(<status code>)
, e.g. with status code 500, abort(500) to rise an HTTPException for an Internal Server Error, in order to abort a request and prevent it from expecting a returned result.