3

Всякий раз, когда я выполняю оператор обновления с помощью session с SqlAlchemy, а затем вызывает commit(), он редко обновляет базу данных.Postgres и SqlAlchemy не обновляют строки должным образом

Это моя окружение: У меня два сервера работает. Один для моей базы данных - другой для моего сервера python.

Сервер базы данных:

  • Postgres v9.6 - На Амазонки RDS

сервер с Python

  • Linux 3.13.0-65-generic x86_64 - На Amazon EC2 Instance
  • 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 
} 

Во время запроса, я распечатать заявление SQL, что мой запрос PUT созданный (для тестирования), и я получаю это:

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 
} 

Когда я запрос GET с этим URL: /api/categories/2 и я получаю один и тот же объект тоже нравится так:

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

Однако, когда я бегу команда SQL ниже в pgAdmin, я получаю старую версию (она не обновить строку в базе данных):

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, я получу старый объект. Похоже, что в сеансе он хранит данные, но по какой-то причине он не распространяется на базу данных.

Возможно, было бы хорошо знать, что если я запустил команду update в pgAdmin, она обновит строку просто отлично.

UPDATE: Я также использовал эти методы (как говорили о 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 

Когда я закрывал каждый сеанс я открыл после того, как я сделал с ним, проблема была решена.

Надеюсь, это поможет кому-то.

Смежные вопросы