3

每當我使用sessionSqlAlchemy執行更新語句,然後調用commit()時,它幾乎不會更新數據庫。Postgres和SqlAlchemy沒有正確更新行

這是我的環境: 我有兩臺服務器在運行。一個是我的數據庫,另一個是我的python服務器。

數據庫服務器:

  • Postgres v9.6 - 在與Python亞馬遜RDS

服務器

  • Linux 3.13.0-65-generic x86_64 - 在Amazon EC2實例
  • SqlAlchemy v1.1.5
  • Python v3.4.3
  • Flask 0.11.1

還有,我用pgAdmin 4查詢我的表。

重要的文件:

server/models/category.py

from sqlalchemy.orm import backref 
from .. import db 
from flask import jsonify 


class Category(db.Model): 
    __tablename__ = "categories" 

    id = db.Column(db.Integer, primary_key=True) 
    cat_name = db.Column(db.String(80)) 
    includes = db.Column(db.ARRAY(db.String), default=[]) 
    excludes = db.Column(db.ARRAY(db.String), default=[]) 

    parent_id = db.Column(db.ForeignKey('categories.id', ondelete='SET NULL'), nullable=True, default=None) 
    subcategories = db.relationship('Category', backref=backref(
     'categories', 
     remote_side=[id], 
     single_parent=True, 
     cascade="all, delete-orphan" 
    )) 

    assigned_user = db.Column(db.String(80), nullable=True, default=None) 


    def to_dict(self): 
     return dict(
      id=self.id, 
      cat_name=self.cat_name, 
      parent_id=self.parent_id, 
      includes=self.includes, 
      excludes=self.excludes, 
      assigned_user=self.assigned_user, 
     ) 

    def json(self): 
     return jsonify(self.to_dict()) 

    def __repr__(self): 
     return "<%s %r>" % (self.__class__, self.to_dict()) 

class CategoryOperations: 
    ... 
    @staticmethod 
    def update_category(category): 
     return """ 
      UPDATE categories 
      SET cat_name='{0}', 
       parent_id={1}, 
       includes='{2}', 
       excludes='{3}', 
       assigned_user={4} 
      WHERE id={5} 
      RETURNING cat_name, parent_id, includes, excludes, assigned_user 
     """.format(
      category.cat_name, 
      category.parent_id if category.parent_id is not None else 'null', 
      "{" + ",".join(category.includes) + "}", 
      "{" + ",".join(category.excludes) + "}", 
      "'" + category.assigned_user + "'" if category.assigned_user is not None else 'null', 
      category.id 
     ) 

    @staticmethod 
    def update(category, session): 
     print("Updating category with id: " + str(category.id)) 
     stmt = CategoryOperations.update_category(category) 
     print(stmt) 
     row_updated = session.execute(stmt).fetchone() 
     return Category(
      id=category.id, 
      cat_name=row_updated[0], 
      parent_id=row_updated[1], 
      includes=row_updated[2], 
      excludes=row_updated[3], 
      assigned_user=row_updated[4] 
     ) 
    ... 

server/api/category.py

from flask import jsonify, request 
import json 
from .api_utils.utils import valid_request as is_valid_request 
from . import api 
from ..models.category import Category, CategoryOperations 
from ..models.users_categories import UsersCategoriesOperations, UsersCategories 
from ..models.listener_item import ListenerItemOperations, ListenerItem 
from ..models.user import UserOperations 
from ..schemas.category import category_schema 
from .. import get_session 

... 
@api.route('/categories/<int:id>', methods=['PUT']) 
def update_category(id): 
    category_json = request.json 
    if category_json is None: 
     return "Bad Request: Request not sent as json", 400 
    valid_json, json_err = is_valid_request(category_json, ['cat_name', 'parent_id', 'includes', 'excludes', 'assigned_user'], "and") 
    if not valid_json: 
     return json_err, 400 

    category = Category(
     id=id, 
     cat_name=category_json['cat_name'], 
     parent_id=category_json['parent_id'], 
     includes=category_json['includes'], 
     excludes=category_json['excludes'], 
     assigned_user=category_json['assigned_user'], 
    ) 
    session = get_session() 
    try: 
     updated_category = CategoryOperations.update(category, session) 
     session.commit() 
     print(updated_category.to_dict()) 
     return jsonify(updated_category.to_dict()), 200 
    except Exception as e: 
     print("ROLLBACK") 
     print(e) 
     session.rollback() 
     return str(e), 500 
... 

還有一個文件,該文件可能會是有用的,這種情況下:

server/__init__.py

import sqlalchemy as sa 
from flask import Flask 
from flask_marshmallow import Marshmallow 
from flask_sqlalchemy import SQLAlchemy 
from config import config 
from sqlalchemy.orm import scoped_session, sessionmaker 
from sqlalchemy.ext.declarative import declarative_base 
from flask_cors import CORS, cross_origin 
from .db_config import CONFIG 

db = SQLAlchemy() 
ma = Marshmallow() 

Engine = sa.create_engine(
    CONFIG.POSTGRES_URL, 
    client_encoding='utf8', 
    pool_size=20, 
    max_overflow=0 
) 
Session = sessionmaker(bind=Engine) 
conn = Engine.connect() 


def get_session(): 
    return Session(bind=conn) 


def create_app(config_name): 
    app = Flask(__name__, static_url_path="/app", static_folder="static") 
    app_config = config[config_name]() 
    print(app_config) 
    app.config.from_object(app_config) 

    from .api import api as api_blueprint 
    app.register_blueprint(api_blueprint, url_prefix='/api') 

    from .api.routes import routes 
    routes(app) 

    from .auth import authentication 
    authentication(app) 

    db.init_app(app) 
    ma.init_app(app) 
    CORS(app) 
    ... 
    return app 

爲了解釋多一點與我所提供的環境和文件,讓我們說我有我的類別表中的一行,像這樣:

{ 
    "assigned_user": null, 
    "cat_name": "Category Name Before", 
    "excludes": [ 
    "exclude1", 
    "excludeBefore" 
    ], 
    "id": 2, 
    "includes": [ 
    "include1", 
    "include2" 
    ], 
    "parent_id": null 
} 

當我做一個PUT請求/api/categories/2與身體爲:

{ 
    "assigned_user": null, 
    "cat_name": "Category Name 1", 
    "excludes": [ 
    "exclude1", 
    "exclude2" 
    ], 
    "id": 2, 
    "includes": [ 
    "include1", 
    "include2" 
    ], 
    "parent_id": null 
} 

在要求,我打印出來,我的PUT請求創建(測試)的SQL語句,我得到這個:

UPDATE categories 
SET cat_name='Category Name 1', 
    parent_id=null, 
    includes='{include1,include2}', 
    excludes='{exclude1,exclude2}', 
    assigned_user=null 
WHERE id=2 
RETURNING cat_name, parent_id, includes, excludes, assigned_user 

其提交UPDATE語句後,然後返回響應。我得到更新的對象回,像這樣:

{ 
    "assigned_user": null, 
    "cat_name": "Category Name 1", 
    "excludes": [ 
    "exclude1", 
    "exclude2" 
    ], 
    "id": 2, 
    "includes": [ 
    "include1", 
    "include2" 
    ], 
    "parent_id": null 
} 

當我做這個URL GET請求:/api/categories/2,我也得到相同的對象太像這樣:

{ 
    "assigned_user": null, 
    "cat_name": "Category Name 1", 
    "excludes": [ 
    "exclude1", 
    "exclude2" 
    ], 
    "id": 2, 
    "includes": [ 
    "include1", 
    "include2" 
    ], 
    "parent_id": null 
} 

然而,當我運行下面pgAdmin,我得到的是舊版本的SQL命令(它沒有更新數據庫行):

SELECT * FROM categories WHERE id=2 

這裏是我的對象得到:

{ 
    "assigned_user": null, 
    "cat_name": "Category Name Before", 
    "excludes": [ 
    "exclude1", 
    "excludeBefore" 
    ], 
    "id": 2, 
    "includes": [ 
    "include1", 
    "include2" 
    ], 
    "parent_id": null 
} 

這是我在做PUT請求之前的對象。如果我重新啓動我的python服務器並執行GET請求,那麼我會得到舊的對象。感覺就像在會話中一樣,它存儲數據,但由於某種原因它不會傳播到數據庫。

知道如果我在pgAdmin中運行更新命令,它可能會很好地更新行。

更新:我也用這些方法(如談到here)進行更新,但還是同樣的問題:

# using the session to update 
session.query(Category).filter_by(id=category.id).update({ 
    "cat_name": category.id, 
    "assigned_user": category.assigned_user, 
    "includes": category.includes, 
    "excludes": category.excludes, 
    "parent_id": category.parent_id 
}) 

# using the category object to edit, then commit 
category_from_db = session.query(Category).filter_by(id=category.id).first() 
category_from_db.cat_name = category_json['cat_name'] 
category_from_db.assigned_user = category_json['assigned_user'] 
category_from_db.excludes = category_json['excludes'] 
category_from_db.includes = category_json['includes'] 
category_from_db.parent_id = category_json['parent_id'] 
session.commit() 

任何想法?

回答

2

事實證明,每次我打電話給get_session時,我都在創建一個新的會話。每次HTTP請求後我都沒有關閉會話。

這裏是server/api/category.py PUT請求是什麼樣子:

@api.route('/categories/<int:id>', methods=['PUT']) 
def update_category(id): 
    category_json = request.json 
    if category_json is None: 
     return "Bad Request: Request not sent as json", 400 
    valid_json, json_err = is_valid_request(category_json, ['cat_name', 'parent_id', 'includes', 'excludes', 'assigned_user'], "and") 
    if not valid_json: 
     return json_err, 400 

    category = Category(
     id=id, 
     cat_name=category_json['cat_name'], 
     parent_id=category_json['parent_id'], 
     includes=category_json['includes'], 
     excludes=category_json['excludes'], 
     assigned_user=category_json['assigned_user'], 
    ) 
    session = get_session() 
    try: 
     updated_category = CategoryOperations.update(category, session) 
     session.commit() 
     print(updated_category.to_dict()) 
     return jsonify(updated_category.to_dict()), 200 
    except Exception as e: 
     print("ROLLBACK") 
     print(e) 
     session.rollback() 
     return str(e), 500 
    finally:        # 
     session.close()     # <== The fix 

一旦我關閉我每次打開我用它做會議後,問題解決了。

希望這可以幫助別人。