Build a CRUD App with SQLAlchemy - Using sessions in controllers

  • 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.

猜你喜欢

转载自blog.csdn.net/BSCHN123/article/details/121392082