Работа со связями "многие ко многим" в поле "Деформировать / Дуршлаг" Выбрать поле HTML

Я работаю в среде 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, но я также использую метод, подобный этому, для чтения / записи appstruct.

Теперь я видел упоминаниеснова) об использовании Mapping для обработки многих и многих полей отношений, таких как эта, но нет твердого примера того, как его использовать.

Заранее спасибо всем, кто может протянуть руку. Это будет оценено.

1 ответ

Решение

Я был в левом поле на этом, даже не задавая правильный вопрос для правильной области. Что я действительно хотел, так это выбрать несколько значений по умолчанию в множественном выбранном дуршлаге SchemaNode. Я перенес свой вопрос на пилоны, чтобы обсудить группу Google, и они смогли мне помочь. Все сводилось к использованию 'set()' при создании appstruct в моем классе Product, как показано ниже:

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),
                )

Спасибо всем, кто заглянул. Мои извинения я задавал неправильные вопросы и не делал это простым.:-)

Другие вопросы по тегам