【问题标题】:Working with Many to Many Relationships in Deform/Colander HTML Select Field在变形/滤锅 HTML 选择字段中处理多对多关系
【发布时间】:2014-08-21 22:58:30
【问题描述】:

我在 Pyramid 框架中工作,并使用 Deform 包在给定滤锅方案的情况下呈现 HTML 表单。我正在努力思考如何处理具有多对多关系的模式。例如,我的 sqlalchemy 模型如下所示:

class Product(Base):
    """ The SQLAlchemy declarative model class for a Product object. """
    __tablename__ = 'products'

    id = Column(Integer, primary_key=True)
    name = Column(String(80), nullable=False)
    description = Column(String(2000), nullable=False)
    categories = relationship('Category', secondary=product_categories,
                               backref=backref('categories', lazy='dynamic'))


class Category(Base):                                                                                
    """ The SQLAlchemy declarative model class for a Category object. """                            
    __tablename__ = 'categories'

    id = Column(Integer, primary_key=True)                                                                                            
    name = Column(String(80), nullable=False)                                                                                                                                 
    products = relationship('Product', secondary=product_categories,                                 
                               backref=backref('products', lazy='dynamic'))


product_categories = Table('product_categories', Base.metadata,
    Column('products_id', Integer, ForeignKey('products.id')),
    Column('categories_id', Integer, ForeignKey('categories.id'))
)

如您所见,这是一个非常简单的模型,表示产品可以属于一个或多个类别的在线商店。在我呈现的表单中,我希望有一个选择多个字段,我可以在其中选择几个不同的类别来放置产品。这是一个简单的滤锅架构:

def get_category_choices():

    all_categories = DBSession.query(Category).all()

    choices = []
    for category in all_categories:
        choices.append((category.id, category.name))

    return choices


class ProductForm(colander.Schema):
    """ The class which constructs a PropertyForm form for add/edit pages. """

    name = colander.SchemaNode(colander.String(), title = "Name",
                               validator=colander.Length(max=80),
                              )

    description = colander.SchemaNode(colander.String(), title="Description",
                                  validator=colander.Length(max=2000),
                                  widget=deform.widget.TextAreaWidget(rows=10, cols=60),
                                 )

    categories = colander.SchemaNode(
                colander.Set(),
                widget=deform.widget.SelectWidget(values=get_category_choices(), multiple=True),
                validator=colander.Length(min=1),
                )

而且,果然,我确实得到了所有字段的正确呈现,但是,类别字段似乎没有“绑定”到任何东西。如果我编辑我知道属于两个类别的产品,我希望选择字段已经突出显示这两个类别。进行更改(选择第三项)应导致数据库更改,其中 product_categories 具有给定 product_id 的三行,每行具有不同的 category_id。可能是TMI,但我也在使用类似于this的方法来读/写appstruct。

现在,我已经看到mention(和again)使用映射来处理诸如此类的多对多关系字段,但没有一个可靠的示例来说明如何使用它。

提前感谢任何可以伸出援助之手的人。不胜感激。

【问题讨论】:

  • 我深入挖掘了一下,发现对于初学者来说,'categories' 对象甚至不在我的 appstruct 中,所以我添加了它:appstruct['categories'] = [{'id':c.id, 'name':c.name} for c in self.categories] 现在,它一定是将 'selected' 属性添加到生成的 HTML 以便选择 appstruct 中的项目的问题: 所以,“Name 1”和“Name 3”被选中。想法?

标签: python pyramid deform colander


【解决方案1】:

我在这个问题上处于左侧,甚至没有为正确的区域提出正确的问题。我真正想要的是在多选滤锅 SchemaNode 中选择一些默认值。我将我的问题提交给pylons-discuss Google Group,他们能够帮助我。当我在我的 Product 类中构造 appstruct 时,它归结为使用 'set()',如下所示:

def appstruct(self):
    """ Returns the appstruct model for use with deform. """

    appstruct = {}
    for k in sorted(self.__dict__):
        if k[:4] == "_sa_":
            continue

        appstruct[k] = self.__dict__[k]

    # Special case for the categories
    appstruct['categories'] = set([str(c.id) for c in self.categories])

    return appstruct

然后,我将它(连同 appstruct 中的其他项)传递给表单,它正确呈现了 HTML,并选择了所有类别。提交后应用appstruct,代码最终看起来像:

def apply_appstruct(self, appstruct):
    """ Set the product with appstruct from the submitted form. """

    for kw, arg in appstruct.items():

        if kw == "categories":
            categories = []
            for id in arg:
                categories.append(DBSession.query(Category).filter(Category.id == id).first())
            arg = categories

        setattr(self, kw, arg)

滤锅架构最终看起来像:

def get_category_choices():
    all_categories = DBSession.query(Category).all()
    return [(str(c.id), c.name) for c in all_categories]

categories = get_category_choices()

class ProductForm(colander.Schema):
    """ The class which constructs a ProductForm form for add/edit pages. """

    name = colander.SchemaNode(colander.String(), title = "Name",
                               validator=colander.Length(max=80),
                              )

    description = colander.SchemaNode(colander.String(), title="Description",
                                  validator=colander.Length(max=2000),
                                  widget=deform.widget.TextAreaWidget(rows=10, cols=60),
                                 )

    categories = colander.SchemaNode(
                colander.Set(),
                widget=deform.widget.SelectWidget(
                    values=categories,
                    multiple=True,
                ),
                validator=colander.Length(min=1),
                )

感谢所有观看的人。我很抱歉,我问了错误的问题并且没有保持简单。 :-)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-26
    • 2016-07-12
    相关资源
    最近更新 更多