How to use multiselect widget in content type's forms for many to many relation?

45 views
Skip to first unread message

Lukas Zdych

unread,
May 22, 2013, 5:21:46 AM5/22/13
to ko...@googlegroups.com
Hi,

I would like to store a list of user ids in content type's model and use them in some update subscriber to set some security settings based o the list of usernames. After some investigation how to implement it in relational database I came out with the following (inspired by kotti's tags and local groups) - see example code bellow.

My issue is that I can see list of principals in the add/edit form of the content type, I can select and save it .. all is saved to db and seems to by ok except that if I go to content type's edit form again nothing is selected inthe widget even if it's saved in db.. can somebody help me find what I'm doing wrong?

Thank you

Lukas

# === Example content model with field storing list of assigned users (assignees) ===

class Task(Content):

    id = Column(
        Integer,
        ForeignKey('contents.id'),
        primary_key=True
    )

    ...

    _assignees = relation(
        TaskAssignees,
        backref=backref('tasks'),
        collection_class=list,
        )

    assignees = association_proxy(
        '_assignees',
        'assignee_id',
        creator=TaskAssignees._manager_find,
        )

# === Task assignees mapping model to store list of users assigned to task ===

class TaskAssignees(Base):
    __tablename__ = 'task_assignees'

    principal_id = Column(Integer, ForeignKey('principals.id'), primary_key=True)
    task_id = Column(Integer, ForeignKey('tasks.id'), primary_key=True)
    assignee = relation(Principal, backref=backref('assignee_of_tasks'))
    assignee_id = association_proxy('assignee', 'id')

    @classmethod
    def _assignee_find(cls, assignee_id):
        with DBSession.no_autoflush:
            principal = DBSession.query(Principal).get(assignee_id)
        return cls(assignee=principal)


# === Task schema definition for Add/Edit forms ===

class TaskSchema(ContentSchema):

    assignees = colander.SchemaNode(
        colander.Set(),
        title=_('Assignees'),
        widget=deferred_task_assignees_widget,
        missing=[],
        )


# === Task::assignees widget to let Editor (or whoever now)
#     select users assigned to task ===

@colander.deferred
def deferred_task_assignees_widget(node, kw):
    principals = Principal.query.all()
    users = [(user.id, user.title.encode('utf-8')) for user in principals]
    widget = deform.widget.SelectWidget(values=tuple(users), multiple=True)
    return widget

Andreas Kaiser

unread,
May 22, 2013, 5:32:02 AM5/22/13
to ko...@googlegroups.com
Hi Lukas,
I've done something similar recently, but with a CheckboxChoiceWidget instead of a SelectWidget. Every YPCompany instance can be associated to multiple YPBranch instances within the same YellowPages instance (their common container).

https://github.com/disko/kotti_yellow_pages/blob/master/kotti_yellow_pages/views/company.py#L82

https://github.com/disko/kotti_yellow_pages/blob/master/kotti_yellow_pages/views/branch.py#L21


HTH,

Andreas

Lukas Zdych

unread,
May 23, 2013, 3:45:33 PM5/23/13
to ko...@googlegroups.com
Hi Andreas,

thank you for your response and the example which helped me to:

1. Understand how to make such a relations

2. Fix my issue which was about that I was trying to use Principal.id as the relation key which was in form's cstruct as integer but in available values in selection widget as strings .. so I now use Principal.name which is unique too and all works like a charm.

Thanks again

Lukas
Reply all
Reply to author
Forward
0 new messages