与 wtforms_alchemy.ModelFieldList 的一对多关系不会更新子项
One-to-many relationship with wtforms_alchemy.ModelFieldList does not update children
下面是填充空父对象并创建其子对象的表单。必须手动调用 ModelFormField,这是一个小麻烦。它很好用。但是,当我使用表单执行 update 时,只有对象被更新——子对象是新鲜创建的。
在此框架中向子项传播更新的正确方法是什么?实际上,我想使用下面的两个打印语句来打印相同的内容。如果表单数据有额外的(丢失的)子数据,如果表单会创建(删除)子数据,我将特别感激。
from multidict import MultiDict
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from wtforms_alchemy import ModelFieldList, ModelForm, ModelFormField
Base = declarative_base()
class Child(Base):
__tablename__ = "child"
id = Column(Integer, primary_key=True)
name = Column(String)
parent_id = Column(Integer, ForeignKey("parent.id"))
class Parent(Base):
__tablename__ = "parent"
id = Column(Integer, primary_key=True)
name = Column(String)
children = relationship(Child)
class ChildForm(ModelForm):
class Meta:
model = Child
class ParentForm(ModelForm):
class Meta:
model = Parent
children = ModelFieldList(ModelFormField(ChildForm)) # annoyed!
engine = create_engine("sqlite://")
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
# Form data
formdata = MultiDict({"name": "Foo", "children-0-name": "hotdog"})
# Create initial object
question = Parent()
form = ParentForm(formdata, obj=question)
form.populate_obj(question)
if not form.validate():
raise RuntimeError(form.errors)
session.add(question)
session.commit()
# prints: "(1, 1)"
print((question.id, question.children[0].id))
# Retrieve object and update with same data
question_get = session.query(Parent).get(question.id)
form = ParentForm(formdata, obj=question_get)
form.populate_obj(question_get)
if not form.validate():
raise RuntimeError(form.errors)
session.add(question_get)
session.commit()
# prints: "(1, 2)", want it to print the same as above
print((question_get.id, question_get.children[0].id))
在深入研究代码时,似乎需要对象 ID 进行更新。因此,必须在更新时使用一种包含 ID 并传递 ID 的表单。
--- orig.py 2018-05-01 20:51:09.000000000 -0700
+++ fixed.py 2018-05-01 20:56:16.000000000 -0700
@@ -4,6 +4,7 @@
from sqlalchemy.orm import relationship
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
+from wtforms import IntegerField
from wtforms_alchemy import ModelFieldList, ModelForm, ModelFormField
@@ -24,6 +25,7 @@
class ChildForm(ModelForm):
class Meta:
model = Child
+ id = IntegerField() # Optional field used in update operations.
class ParentForm(ModelForm):
class Meta:
@@ -53,7 +55,11 @@
# Retrieve object and update with same data
question_get = session.query(Parent).get(question.id)
-form = ParentForm(formdata, obj=question_get)
+child_id = question_get.children[0].id
+formdata_update = MultiDict({"name": "Foo",
+ "children-0-id": child_id,
+ "children-0-name": "pizza"})
+form = ParentForm(formdata_update, obj=question_get)
form.populate_obj(question_get)
if not form.validate():
raise RuntimeError(form.errors)
不错的侦查。这仍然看起来很麻烦。您随后是否找到了一种更简单的方法来更新具有一对多关系的表单? WTForms 应该可以节省开发人员的时间。但这似乎比没有 WTForms 时的开销更大。
下面是填充空父对象并创建其子对象的表单。必须手动调用 ModelFormField,这是一个小麻烦。它很好用。但是,当我使用表单执行 update 时,只有对象被更新——子对象是新鲜创建的。
在此框架中向子项传播更新的正确方法是什么?实际上,我想使用下面的两个打印语句来打印相同的内容。如果表单数据有额外的(丢失的)子数据,如果表单会创建(删除)子数据,我将特别感激。
from multidict import MultiDict
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from wtforms_alchemy import ModelFieldList, ModelForm, ModelFormField
Base = declarative_base()
class Child(Base):
__tablename__ = "child"
id = Column(Integer, primary_key=True)
name = Column(String)
parent_id = Column(Integer, ForeignKey("parent.id"))
class Parent(Base):
__tablename__ = "parent"
id = Column(Integer, primary_key=True)
name = Column(String)
children = relationship(Child)
class ChildForm(ModelForm):
class Meta:
model = Child
class ParentForm(ModelForm):
class Meta:
model = Parent
children = ModelFieldList(ModelFormField(ChildForm)) # annoyed!
engine = create_engine("sqlite://")
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
# Form data
formdata = MultiDict({"name": "Foo", "children-0-name": "hotdog"})
# Create initial object
question = Parent()
form = ParentForm(formdata, obj=question)
form.populate_obj(question)
if not form.validate():
raise RuntimeError(form.errors)
session.add(question)
session.commit()
# prints: "(1, 1)"
print((question.id, question.children[0].id))
# Retrieve object and update with same data
question_get = session.query(Parent).get(question.id)
form = ParentForm(formdata, obj=question_get)
form.populate_obj(question_get)
if not form.validate():
raise RuntimeError(form.errors)
session.add(question_get)
session.commit()
# prints: "(1, 2)", want it to print the same as above
print((question_get.id, question_get.children[0].id))
在深入研究代码时,似乎需要对象 ID 进行更新。因此,必须在更新时使用一种包含 ID 并传递 ID 的表单。
--- orig.py 2018-05-01 20:51:09.000000000 -0700
+++ fixed.py 2018-05-01 20:56:16.000000000 -0700
@@ -4,6 +4,7 @@
from sqlalchemy.orm import relationship
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
+from wtforms import IntegerField
from wtforms_alchemy import ModelFieldList, ModelForm, ModelFormField
@@ -24,6 +25,7 @@
class ChildForm(ModelForm):
class Meta:
model = Child
+ id = IntegerField() # Optional field used in update operations.
class ParentForm(ModelForm):
class Meta:
@@ -53,7 +55,11 @@
# Retrieve object and update with same data
question_get = session.query(Parent).get(question.id)
-form = ParentForm(formdata, obj=question_get)
+child_id = question_get.children[0].id
+formdata_update = MultiDict({"name": "Foo",
+ "children-0-id": child_id,
+ "children-0-name": "pizza"})
+form = ParentForm(formdata_update, obj=question_get)
form.populate_obj(question_get)
if not form.validate():
raise RuntimeError(form.errors)
不错的侦查。这仍然看起来很麻烦。您随后是否找到了一种更简单的方法来更新具有一对多关系的表单? WTForms 应该可以节省开发人员的时间。但这似乎比没有 WTForms 时的开销更大。