You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask教程报错求助:登录后触发AttributeError: 'NoneType'无execute属性

Flask登录后触发AttributeError: 'NoneType' object has no attribute 'execute' 排查方案

我在学习Flask基础博客应用开发,完成Blog Blueprint部分后,登录应用时出现AttributeError: 'NoneType' object has no attribute 'execute',错误指向blog.py第14行的db.execute语句。反复检查代码没找到问题,附上blog.py和auth.py代码,求排查建议或解决方案。

# blog.py代码
from flask import (
    Blueprint, flash, g, redirect, render_template, request, url_for
)
from werkzeug.exceptions import abort

from flaskr.auth import login_required
from flaskr.db import get_db

bp = Blueprint('blog', __name__)

@bp.route('/')
def index():
    db = get_db()
    posts = db.execute(
        'SELECT p.id, title, body, created, author_id, username'
        ' FROM post p JOIN user u ON p.author_id = u.id'
        ' ORDER BY created DESC'
    ).fetchall()
    return render_template('blog/index.html', posts=posts)

@bp.route('/create', methods=('GET', 'POST'))
@login_required
def create():
    if request.method == 'POST':
        title = request.form['title']
        body = request.form['body']
        error = None
        
        if not title:
            error = 'Title is required'
            
        if error is not None:
            flash(error)
        else:
            db = get_db()
            db.execute(
                'INSERT INTO post (title, body, author_id)'
                ' VALUES (?, ?, ?)',
                (title, body, g.user['id'])
            )
            db.commit()
            return redirect(url_for('blog.index'))
        
    return render_template('blog/create.html')

def get_post(id, check_author=True):
    post = get_db().execute(
        'SELECT p.id, title, body, created, author_id, username'
        ' FROM post p JOIN user u ON p.author_id = u.id'
        ' WHERE p.id = ?',
        (id,)
    ).fetchone()
    
    if post is None:
        abort(404, f"Post id {id} doesn't exist.")
        
    if check_author and post['author_id'] != g.user['id']:
        abort(403)
        
    return post

@bp.route('/<int:id>/update', methods=('GET', 'POST'))
@login_required
def update(id):
    post = get_post(id)
    
    if request.method == 'POST':
        title = request.form['title']
        body = request.form['body']
        error = None
        
        if not title:
            error = 'Title is required.'
            
        if error is not None:
            flash(error)
        else:
            db = get_db()
            db.execute(
                'UPDATE post SET title = ?, body = ?'
                ' WHERE id = ?',
                (title, body, id)
            )
            db.commit()
            return redirect(url_for('blog.index'))
        
    return render_template('blog/update.html', post=post)

@bp.route('/<int:id>/delete', methods=('POST',))
@login_required
def delete(id):
    get_post(id)
    db = get_db()
    db.execute('DELETE FROM post WHERE id = ?', (id,))
    db.commit()
    return redirect(url_for('blog.index'))
# auth.py代码
import functools

from flask import (
    Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash

from flaskr.db import get_db

bp = Blueprint('auth', __name__, url_prefix='/auth')

@bp.route('/register', methods=('GET', 'POST'))
def register():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        db = get_db()
        error = None
        
        if not username:
            error = 'Username is required.'
        elif not password:
            error = 'Password is required.'
            
        if error is None:
            try:
                db.execute(
                    'INSERT INTO user (username, password) VALUES (?, ?)',
                    (username, generate_password_hash(password)),
                )
                db.commit()
            except db.IntegrityError:
                error = f'Usser {username} is already registered.'
            else:
                return redirect(url_for('auth.login'))
            
        flash(error)
            
    return render_template('auth/register.html')
    
@bp.route('/login', methods=('GET', 'POST'))
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        db = get_db()
        error = None
        user = db.execute(
            'SELECT * FROM user WHERE username = ?', (username,)
        ).fetchone()
        
        if user is None:
            error = 'Incorrect username.'
        elif not check_password_hash(user['password'], password):
            error = 'Incorrect password.'
            
        if error is None:
            session.clear()
            session['user_id'] = user['id']
            return redirect(url_for('index'))
            
        flash(error)
        
    return render_template('auth/login.html')
    
@bp.before_app_request
def load_logged_in_user():
    user_id = session.get('user_id')
    
    if user_id is None:
        g.user = None
    else:
        g.user = get_db().execute(
            'SELECT * FROM user WHERE id = ?', (user_id,)
        ).fetchone()
        
@bp.route('/logout')
def logout():
    session.clear()
    return redirect(url_for('index'))

def login_required(view):
    @functools.wraps(view)
    def wrapped_view(**kwargs):
        if g.user is None:
            return redirect(url_for('auth.login'))
        
        return view(**kwargs)
    
    return wrapped_view

排查思路与解决方案

这个错误的核心是get_db()返回了None,导致调用db.execute时触发属性错误。以下是具体排查方向:

  1. 检查db.py中get_db()的实现
    这是最可能出问题的地方,标准实现应该如下,确保没有遗漏逻辑:

    import sqlite3
    from flask import g
    
    def get_db():
        if 'db' not in g:
            g.db = sqlite3.connect(
                'flaskr.sqlite',
                detect_types=sqlite3.PARSE_DECLTYPES
            )
            g.db.row_factory = sqlite3.Row
        return g.db
    
    def close_db(e=None):
        db = g.pop('db', None)
        if db is not None:
            db.close()
    

    同时要确认在app.py中注册了数据库关闭的钩子:

    from flaskr.db import close_db
    app.teardown_appcontext(close_db)
    
  2. 验证数据库初始化状态

    • 检查项目根目录下是否存在flaskr.sqlite文件,若没有则运行初始化命令:
      flask --app flaskr init-db
      
    • 确认init-db脚本正确创建了user和post两张表。
  3. 调试请求上下文的数据库连接
    在auth.py的load_logged_in_user函数中添加调试打印,确认登录后get_db()的返回值:

    @bp.before_app_request
    def load_logged_in_user():
        user_id = session.get('user_id')
        db = get_db()
        print(f"DB connection status: {db}")  # 查看是否为None
        if user_id is None:
            g.user = None
        else:
            g.user = db.execute(
                'SELECT * FROM user WHERE id = ?', (user_id,)
            ).fetchone()
    

    运行应用后观察控制台输出,若db为None,则说明get_db()的逻辑存在问题。

  4. 检查蓝图注册顺序
    在app.py中确保先注册auth蓝图,再注册blog蓝图:

    from flaskr.auth import bp as auth_bp
    from flaskr.blog import bp as blog_bp
    
    app.register_blueprint(auth_bp)
    app.register_blueprint(blog_bp)
    

快速修复步骤

  1. 确保db.py的代码与官方教程完全一致,无拼写或逻辑错误。
  2. 重新运行flask --app flaskr init-db初始化数据库。
  3. 重启Flask应用,使用隐身模式测试登录流程。

内容的提问来源于stack exchange,提问作者ridgfam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 20:07:46