How to handle relation to content items?

28 views
Skip to first unread message

Marcel Maré

unread,
Apr 20, 2016, 3:59:11 PM4/20/16
to Kotti
I'm getting started with Kotti, so bear with me. 

I've created two types: the first ReadingListis boring, it holds instances of the second type ReadingListEntry. The only purpose of the ReadingListEntry is to hold a reference to a Content item somewhere else. I'd like the user to be a able to select from a list of content items. 

In resources.py I have:
class ReadingList(Content):
implements(IDefaultWorkflow)

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

type_info = Content.type_info.copy(
name=u'ReadingList',
title=u'Leeswijzer',
add_view=u'add_readinglist',
addable_to=[u'Document'],
)

def __init__(self, **kwargs):
super(ReadingList, self).__init__(**kwargs)


class ReadingListEntry(Content):
implements(IDefaultWorkflow)

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

content_item_id = Column(Integer, ForeignKey('nodes.id'))
content_item = relationship('Node', primaryjoin='Node.id==ReadingListEntry.content_item_id')

type_info = Content.type_info.copy(
name=u'ReadingListEntry',
title=u'Leeswiizer item',
add_view=u'add_readinglistitem',
addable_to=[u'ReadingList'],
)


in edit.py I have:
class ReadingListSchema(ContentSchema):
explanation = colander.SchemaNode(
colander.String(),
title=_(u"Explanation")
)


@colander.deferred
def deferred_choices_widget(node, kw):
all_items = Content.query.all()
available_items = [(item.id, item.title.encode('utf-8')) for item in all_items]
return deform.widget.SelectWidget(values=available_items)

class ReadingListEntrySchema(ContentSchema):
tags = colander.SchemaNode(
colander.Integer(),
title=_('Leeswijzer item'),
widget = deferred_choices_widget,
missing=[],
)

I don't really know what I'm doing but the ReadingListEntry add form shows up and I can select a content item. However when saving I get an error (TypeError: 'int' object is not iterable). 
Anyway I really don't know how to handle the form submission, especially since there are two fields (content_item, and content_item_id). It doesn't help that I am not very familiar with deform and sqlalchemy.

Any pointers? 

TIA

Marcel

Andreas Kaiser

unread,
Apr 20, 2016, 5:28:47 PM4/20/16
to Kotti
You're almost right. Only problem results from the schema node (in
ReadingListEntrySchema) being named "tags" instead of "content_item_id".
This leads to Kotti trying to save an integer value to the tags
collection, which causes the TypeError.

> Anyway I really don't know how to handle the form submission,
> especially
> since there are two fields (content_item, and content_item_id). It
> doesn't
> help that I am not very familiar with deform and sqlalchemy.

No, there's only one "field": content_item_id. content_item is "only" a
relationship over that foreign key, allowing convenience access to the
related record. This is what you'd usually use in your code, but rarely
(if ever) in a schema.


HTH,

Andreas

Marcel Maré

unread,
Apr 21, 2016, 12:43:32 PM4/21/16
to Kotti
Hi Andreas

You nailed it! What two new eyeballs can do. It was negligent copy-pasting on my part. Thanks. 

Also, the penny dropped on the "double" field enigma. Obvious once you know the answer ;-)

Might be useful to have a complete example of linking to content in content items. 

Thanks again.

Marcel

Marcel Maré

unread,
Apr 27, 2016, 7:02:55 AM4/27/16
to Kotti
For future reference, this worked for me:

resources.py:

class ReadingList(Content):
'''
Custom content type that implements a reading list. It contains ReadingListEntry's which are essentially links to other content on the site.
'''
    implements(IDefaultWorkflow)

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

    type_info = Content.type_info.copy(
name=u'ReadingList',
title=u'Leeswijzer',
add_view=u'add_readinglist',
addable_to=[u'Document'],
)

def __init__(self, **kwargs):
super(ReadingList, self).__init__(**kwargs)


class ReadingListEntry(Content):
implements(IDefaultWorkflow)

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

content_item_id = Column(Integer, ForeignKey('nodes.id'))
content_item = relationship('Node', primaryjoin='Node.id==ReadingListEntry.content_item_id')

type_info = Content.type_info.copy(
name=u'ReadingListEntry',
title=u'Leeswiizer item',
add_view=u'add_readinglistitem',
addable_to=[u'ReadingList'],
) 

Part of views/edit.py:

### ReadlingListEntry ###

@colander.deferred
def deferred_choices_widget(node, kw):
all_items = DBSession.query(Content) \
.filter(not_(Content.type.in_(['reading_list', 'reading_list_entry']))) \
.order_by(asc(func.lower(Content.title)))

available_items = [(item.id, item.title.encode('utf-8')) for item in all_items]
return deform.widget.SelectWidget(values=available_items)


class ReadingListEntrySchema(colander.MappingSchema):
''' Simplified content schema '''
title = colander.SchemaNode(
colander.String(),
title=_(u'Title'),
validator=colander.Length(
max=Node.title.property.columns[0].type.length),
)

content_item_id = colander.SchemaNode(

colander.Integer(),
title=_('Leeswijzer item'),
widget=deferred_choices_widget,
        default=-1,
)

Reply all
Reply to author
Forward
0 new messages