因数据库事务问题陷入 SQLAlchemy ROLLBACK

问题描述 投票:0回答:1

我有一个使用 API 的 Flask 应用程序,并且有一个包含多个使用 PSQL 的表的架构。用户应该能够通过搜索栏进行浏览,并单击类型来检索图书结果并将其显示在 Bootstraps 卡片上。用户应该能够收藏/保存一本书,然后在注册和登录后被带到他们的个人资料页面。我似乎已成功注册、登录,并且有一个 GET 到 /users/profile,但随后我我收到回滚,我被送回登录页面。

这是我的 app.py 的一部分。我相信我已经导入了所有必要的依赖项。

from flask import Flask, render_template, redirect, flash, session, request, jsonify, url_for
from models import connect_db, db, User, Books
from forms import LoginForm, RegisterForm
from flask_bcrypt import Bcrypt
from flask_login import LoginManager, login_required

app = Flask(__name__, template_folder="templates", static_folder="static")

app.app_context().push()

from flask_login import current_user
from flask_login import login_user, logout_user

bcrypt = Bcrypt()
login_manager = LoginManager()
login_manager.init_app(app)

app.config["SQLALCHEMY_DATABASE_URI"] = "postgresql:///my_books"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config["SQLALCHEMY_ECHO"] = True
app.config["SECRET_KEY"] = ""
app.config['TESTING'] = True

import requests

API_KEY = ""
migrate = Migrate(app, db)

connect_db(app)

@app.route("/")
def home_page():
    """"""
    return render_template("index.html")

这是login_manager.user_loader,我相信我正确导入了user_id。

@login_manager.user_loader
def user_loader(user_id):
    if user_id.isdigit():
        user = User.query.get(int(user_id))
        return user
    return None**

这是我的登录 GET 路由,显示登录表单。

@app.route("/users/login", methods=["GET"])
def login_form():
    """Displays the login form."""
    form = LoginForm()
    return render_template('users/login.html', form=form)

这是我的登录路线,登录后应该将我带到 profile.html 页面。

from flask import current_app

@app.route("/users/login", methods=["POST"])
def login():
    """Displays login form. Logs in the current user by processing the form"""
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        print("Username from form:", form.username.data)
        print("User from the database:", user)
        
        if user:
            if login_user(user):
                print("Login successful")
                flash(f"Hello, {user.username}!", "success")
                db.session.commit()  # Commit the transaction to the database
                return redirect(url_for('profile'))
            else:
                print("Login failed")
                flash("Failed to log in the user.", "danger")
        else:
            print("User not found")
            flash("User not found.", 'danger')
    else:
        flash("Invalid credentials.", 'danger')

    return render_template('users/login.html', form=form)

我做了很多 flash 和 print 语句,我认为由于回滚,这些语句不会出现。

@app.route("/users/logout", methods=["GET"])
def logout():
    """Logout the current user."""
    logout_user()  # Use Flask-Login logout_user function
    return redirect(url_for('home_page'))

@app.route('/users/register', methods=["GET", "POST"])
def handle_registration():
    """Creates a new user and adds user to the DB. Makes sure username & email are unique."""
    form = RegisterForm()

    if form.validate_on_submit():
        # Check if a user with the same username or email already exists
        existing_user = User.query.filter((User.username == form.username.data) | (User.email == form.email.data)).first()

        if existing_user:
            flash("Username or email already taken", 'danger')
            return render_template('users/register.html', form=form)

        user = User.signup(
            first_name=form.first_name.data,
            last_name=form.last_name.data,
            username=form.username.data,
            password=form.password.data,
            email=form.email.data,
        )
        db.session.commit()

        flash("Registration successful! Please log in.", 'success')
        return redirect(url_for('login_form'))

    return render_template('users/register.html', form=form)

from flask_login import current_user

@app.route('/users/profile', methods=["GET"])
def profile():
    if current_user.is_anonymous:
        flash("You need to log in to see your profile.", 'warning')
        return redirect(url_for('login'))
    print("Profile route accessed")

    # Retrieve the current user's favorite books
    favorite_books = current_user.saved_books
    
    return render_template('users/profile.html', favorite_books=favorite_books)

我的 Flask 应用程序已加载,我可以浏览、搜索和注册虚假帐户,因此 WTForms 的注册页面可以正常工作。当我进入登录页面并输入用户名和密码时,我可以看到 HTML 中的登录标签已切换为注销标签,因此这似乎也可以正常工作。我还检查了我的架构中的所有数据库都显示在 PSQL 中。

这是我的模型.py:

from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
from flask_login import UserMixin

db = SQLAlchemy()

saved_books_association = db.Table(
    'saved_books_association',
    db.Column('user_id', db.Integer, db.ForeignKey('users.id')),
    db.Column('book_id', db.Integer, db.ForeignKey('books.id'))
)

这是延续 models.py 的 User 类。我尝试关注 Real Python 网站。

class User(db.Model, UserMixin):

    __tablename__ = "users"

    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(20), nullable=False, unique=True)
    first_name = db.Column(db.String(20), nullable=False)
    last_name = db.Column(db.String(20), nullable=False)
    email = db.Column(db.String(50), nullable=False)
    password = db.Column(db.String(100), nullable=False)
    saved_books = db.relationship('Books', secondary=saved_books_association, backref=db.backref('users', lazy='dynamic'))
    

    def __init__(self, first_name, last_name, username, email, password):
        self.first_name = first_name
        self.last_name = last_name
        self.username = username
        self.email = email
        self.password = bcrypt.generate_password_hash(password).decode('utf-8')

    def check_password(self, password):
        return bcrypt.check_password_hash(self.password, password)
    
    @classmethod
    def signup(cls, first_name, last_name, username, password, email):
        """Sign up a new user and return the user object."""
        user = cls(first_name=first_name, last_name=last_name, username=username, password=password, email=email)
        db.session.add(user)
        return user

    def is_active(self):
        """True, as all users are active."""
        return True

    def get_id(self):
        """Return the user's ID as a string to satisfy Flask-Login's requirements."""
        return str(self.id)

    def is_anonymous(self):
        """Return True if the user is anonymous, or False if authenticated."""
        return not self.is_authenticated()**

我还有其他未包含的数据库模型。这是运行 Flask 应用程序时终端中的消息:

127.0.0.1 - - [01/Nov/2023 19:30:09] "POST /users/login HTTP/1.1" 302 -
2023-11-01 19:30:09,361 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2023-11-01 19:30:09,361 INFO sqlalchemy.engine.Engine SELECT users.id AS users_id, users.username AS users_username, users.first_name AS users_first_name, users.last_name AS users_last_name, users.email AS users_email, users.password AS users_password 
FROM users 
WHERE users.id = %(pk_1)s
2023-11-01 19:30:09,361 INFO sqlalchemy.engine.Engine [cached since 23.39s ago] {'pk_1': 1}
2023-11-01 19:30:09,362 INFO sqlalchemy.engine.Engine ROLLBACK
127.0.0.1 - - [01/Nov/2023 19:30:09] "GET /users/profile HTTP/1.1" 302 -
2023-11-01 19:30:09,367 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2023-11-01 19:30:09,367 INFO sqlalchemy.engine.Engine SELECT users.id AS users_id, users.username AS users_username, users.first_name AS users_first_name, users.last_name AS users_last_name, users.email AS users_email, users.password AS users_password 
FROM users 
WHERE users.id = %(pk_1)s
2023-11-01 19:30:09,367 INFO sqlalchemy.engine.Engine [cached since 23.4s ago] {'pk_1': 1}
2023-11-01 19:30:09,369 INFO sqlalchemy.engine.Engine ROLLBACK
127.0.0.1 - - [01/Nov/2023 19:30:09] "GET /users/login HTTP/1.1" 200 -

我尝试删除数据库并重新创建它,更改为使用 UserMixin,并多次重新设计我的 User 类模型,但我仍然陷入回滚。任何建议都会有很大帮助。

postgresql authentication sqlalchemy rollback flask-login
1个回答
0
投票

每次使用会话时,都会自动启动一个事务,然后回滚。所以这可能不相关。

if current_user.is_anonymous:
应该是像
if current_user.is_anonymous():
这样的函数调用吗?

© www.soinside.com 2019 - 2024. All rights reserved.