I'm working in the Pyramid framework and using the Deform package to render HTML forms given a colander scheme. I'm struggling to get my head wrapped around how to handle a schema with a many to many relationship. For example, my sqlalchemy models look like this:
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'))
)
As you can see, it's a pretty simple model representing a online store where a product can belong to one or more categories. In my rendered form, I would like to have a select multiple field in which I can choose a few different categories in which to place the product into. Here's a simple colander schema:
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),
)
And, sure enough, I do get a proper rendering of all fields, however, the categories field doesn't seem to be 'tied' to anything. If I edit a product that I know to belong to two categories, I would expect the select field to have those two categories already highlighted. Making a change (selecting a third item) should result in a DB change where product_categories has three rows for the given product_id, each with a different category_id. It may be TMI, but I'm also using a method similar to this to read/write the appstruct.
Now, I've seen mention (and again) of using Mapping to handle many to many relationship fields such as this, but there's not a solid example of how to use it.
Thanks in advance to anybody who can lend a hand. It'd be appreciated.