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

Flask问题:如何从QuerySelectField中获取对象的主键?

问题描述

我正尝试从自引用关系的对象中获取ID,怀疑关系定义存在限制,试过各类Stack Overflow方案都无效:

  • 使用form.parent_id.data.parent_id会抛出AttributeError: 'NoneType' object has no attribute 'parent_id'
  • 直接用form.parent_id.data触发sqlalchemy.exc.InterfaceError,原因是QuerySelectField返回的是对象而非ID
  • 在表单中设置get_pk=lambda x: x.parent_id也会出现相同错误

routes.py

def edit_user(id):
    form = UserForm()
    user = User.query.get_or_404(id)
    selected_date_ids = []
    upcoming_dates = TestDate.query.order_by(TestDate.date).filter(TestDate.status != 'past')
    tests = sorted(set(TestDate.test for TestDate in TestDate.query.all()), reverse=True)
    if form.validate_on_submit():
        if request.method == "POST":
            ...
            user.tutor_id=form.tutor_id.data
            user.parent_id=form.parent_id.data

models.py

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    ...
    tutor_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    students = db.relationship('User', backref=db.backref('tutor'), 
        foreign_keys=[tutor_id], remote_side=[id])
    parent_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    children = db.relationship('User', backref=db.backref('parent'), 
        foreign_keys=[parent_id], remote_side=[id])

forms.py

def get_tutors():
    return User.query.filter_by(role='tutor')

def get_parents():
    return User.query.filter_by(role='parent')

def full_name(User):
    return User.first_name + " " + User.last_name


class UserForm(FlaskForm):
    ...
    tutor_id = QuerySelectField('Tutor', default=1, query_factory=get_tutors, get_label=full_name, allow_blank=True)
    parent_id = QuerySelectField('Parent', query_factory=get_parents, get_label=full_name, allow_blank=True)
    is_admin = BooleanField('Admin')
    submit = SubmitField('Save')

解决方法
  1. 修正字段赋值逻辑
    QuerySelectField返回的是完整的User对象,需提取对象的id属性赋值给外键字段,同时处理空白选项的空值情况:
user.tutor_id = form.tutor_id.data.id if form.tutor_id.data else None
user.parent_id = form.parent_id.data.id if form.parent_id.data else None
  1. 优化自引用关系定义
    当前关系定义逻辑正确,可显式添加lazy参数提升可读性(默认值即为select):
students = db.relationship('User', backref=db.backref('tutor', lazy='select'), 
    foreign_keys=[tutor_id], remote_side=[id], lazy='select')
children = db.relationship('User', backref=db.backref('parent', lazy='select'), 
    foreign_keys=[parent_id], remote_side=[id], lazy='select')
  1. 移除错误的get_pk设置
    get_pk默认会取对象的id作为提交值,手动设置为x.parent_id会导致提交数据错误,直接删除该参数即可。

  2. 初始化表单时填充默认值
    编辑页面需将现有用户的关联数据自动填充到表单,使用obj参数即可实现:

def edit_user(id):
    user = User.query.get_or_404(id)
    form = UserForm(obj=user)  # 自动填充现有用户数据
    selected_date_ids = []
    upcoming_dates = TestDate.query.order_by(TestDate.date).filter(TestDate.status != 'past')
    tests = sorted(set(TestDate.test for TestDate in TestDate.query.all()), reverse=True)
    if form.validate_on_submit():
        if request.method == "POST":
            ...
            user.tutor_id = form.tutor_id.data.id if form.tutor_id.data else None
            user.parent_id = form.parent_id.data.id if form.parent_id.data else None
            db.session.commit()  # 务必提交数据库会话

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 19:55:17