【问题标题】:How to populate lists of objects with WTForms?如何使用 WTForms 填充对象列表?
【发布时间】:2014-01-10 06:01:37
【问题描述】:

用例:使用表格为学生注册的每门课程输入成绩。

型号:

使用 SQLAlchemy,我定义了一个 Student 对象、一个 Course 对象和一个 StudentCourse 关联对象,用于存储每个学生在每门课程中的成绩。

class Student(Base):
    __tablename__ = 'students'
    id = Column(Integer, primary_key=True)
    name = Column(Text)
    courses = association_proxy('student_courses', 'grade',
        creator=lambda k, v: StudentCourse(course_title=k, grade=v))
    ...

class Course(Base):
    __tablename__ = 'courses'
    id = Column(Integer, primary_key=True)
    title = Column(Text, unique=True)
    ...

# Link students to courses and store grades
class StudentCourse(Base):
    __tablename__ = 'student_courses'
    student_id = Column(Integer, ForeignKey(Student.id), primary_key=True)
    course_id = Column(Integer, ForeignKey(Course.id), primary_key=True)
    grade = Column(Integer)
    student = relationship(Student,backref=backref(
            'student_courses',
            collection_class=attribute_mapped_collection('course_title'),
            cascade='all, delete-orphan'))
    course = relationship(Course)

    @property
    def course_title(self):
        if self.course is not None:
            return self.course.title
        else:
            return self._course_title
    ...

查看:

我可以查询 StudentCourses 模型并构造一个相关的表单,但我不知道如何将查询中的数据作为对象传递/检索

def view(request):
    student = Student.from_request(request)
    student_courses = DBSession.query(StudentCourse).\
        filter(StudentCourse.student_id == student.id).\
        all()

    class GradesForm(Form):
        pass

    # Add form field for each course the student is enrolled in
    for c in student_courses:
        setattr(GradesForm,
                c.course_title,
                IntegerField()
                )

    form = GradesForm(request.POST, obj=student_courses) # this doesn't populate the form

    return {'form': form}

这会生成一个空白表单,因此我显然无法直接使用来自Query 对象的数据填充表单。但是我用任何类型的对象填充表单都没有成功,即使为每门课程创建一个带有 FormField 类型的表单:

class StudentCourseForm(Form):
    course_title = StringField()
    grade = IntegerField()

def view(request):
    ...
    class GradesForm(Form):
        pass

    # Add form field for each course
    for c in student_courses:
        setattr(GradesForm,
                c.course_title,
                FormField(StudentCourseForm)
                )

    form = GradesForm(request.POST, obj=student_courses)

    return {'form': form}

如果可能,使用查询将是最简单的。根据SQLAlchemy docs,在会话上使用query() 方法会创建一个Query 对象。当像我在控制器中那样进行迭代时,这个对象是StudentCourse 对象的列表。

[<app.models.StudentCourse object at 0x10875bd50>, <app.models.StudentCourse object at 0x10875bed0>]

...我的进步到此结束。 任何帮助表示赞赏!

【问题讨论】:

  • class GradesForm(Form): pass 然后以for course in courses: setattr(GradesForm, course.name, TextField(course.name, validators=[Required()])) 为例:-)
  • @SeanVieira:感谢您的意见。我读过那篇文章(以及演示该技术的section in the docs),但我不能以那种方式设置课程grades......至少不是那么容易。我可以使用这种技术来填充正确的 number 个字段,甚至可以使用 WTForms 验证,但我仍然需要在控制器中再次循环遍历课程以将表单数据分配给正确的对象在数据库中(而不是使用form.populate_obj(user))。
  • 我有一个没有分辨率的related issue。简而言之,如何将字符串变量传递给表单,以便可以使用数据预填充字段。一个示例用例:存储在多对多数据库中的以逗号分隔的标签列表。我认为正确的方法是运行查询将视图中的值传递给表单,但是我发现的每个示例都使用 populate_obj(post) 将整个 post 对象传递给表单。如果我需要在通过之前对帖子的属性之一做一些特殊的事情怎么办。
  • @bfin agghhh... 您能否发布您的解决方案。最初的问题是几个月前发布的,所以我想你已经知道了。

标签: python sqlalchemy wtforms


【解决方案1】:

我能够填充这些动态创建的表单的唯一方法是传递 **kwargs,因此我将发布此方法作为答案,直到其他人能够找出基于对象的解决方案。

填充表单:

def view(request):
    ...
    data = {}
    for c in student_courses:
        data[c.course_title] = c.grade

    # Populate form
    form = GradesForm(request.POST, **data)
    ...

通过这种方式,我可以通过遍历字段在模板中呈现表单,提交后,我将获得一个字典列表,然后我可以验证并使用它来更新我的数据库记录。

表单验证需要同样的方法:

def view(request):
    ...
    # Validate and persist form data
    if request.method == 'POST' and form.validate():
        for c in student_courses:
            student.courses[c.title] = form[c.title].data

这可行,但如果我可以使用 WTForms populate_obj() 方法那就太好了:

def view(request):
    ...
    if request.method == 'POST' and form.validate():
        form.populate_obj(student_courses)

【讨论】:

  • 我不清楚我们的问题有多密切相关,但我认为我们正在/正在以错误的方式看待这个问题。 populate_obj 意味着我们正在将数据传递给另一个对象。我们没有用另一个对象填充对象,因此名称填充对象......在我的例子中,我的 PostForm 映射到我的模型。这意味着该值附加到该字段。无需赋值。价值已经存在。这是我的answered question,以防万一。
  • @jwogrady:我的用例面临的挑战是我没有对象可以传递给 populate_obj 方法,因为这个特定的表单是动态生成的。如果每个学生都注册了同一组课程,那么我可以创建一个静态表单并将这些特定课程的成绩设置为用户类的属性,并传递该类的一个实例以进行数据填充/验证/持久性。不幸的是,这不是我的情况......
猜你喜欢
  • 1970-01-01
  • 2012-11-06
  • 1970-01-01
  • 1970-01-01
  • 2013-09-10
  • 1970-01-01
  • 2011-10-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多