[Grok-dev] object relationships

Martijn Faassen faassen at startifact.com
Thu Jan 29 12:37:36 EST 2009


Hey,

Jeroen Michiel wrote:
> OK, I got it working!

Very cool!

> This is how I managed it:
> 
> First of all, the IIntIds utility and a RelationCatalog need to be
> registered, so I made an event subscription to grok.IObjectAddedEvent to do
> this:
> 
> @grok.subscribe(Test, grok.IObjectAddedEvent)
> def added(app, event):
>      app['intids'] = intids = IntIds()
>      grok.getSite().getSiteManager().registerUtility(intids,
> provided=IIntIds)
>      app['catalog'] = catalog = relationfield.RelationCatalog()
>      grok.getSite().getSiteManager().registerUtility(catalog,
> provided=ICatalog)

You can do these registrations with the local_utility() directive on the 
application object. This is nicer as it integrates properly with the 
rest of the configuration system of Grok. I believe I left hints in the 
doctest of z3c.relationfield on how to do that.

> you also need an IObjectPath implementation, so I did this:
> class ObjectPath(grok.GlobalUtility):
>     grok.provides(IObjectPath)
>     def path(self, obj):
>         return path(grok.getSite(), obj)
>     def resolve(self, path):
>         return resolve(grok.getSite(), path)

Yup. What's 'path' in this case? full Zope physical paths? That would 
work or the z3c.objpath impelmentations will help. Perhaps you used 
those looking at it, and that's good.

> Then I have some data I want to refer to:
> class IReferedData(Interface):
>     name = schema.TextLine(title=u'The Name')

> class ReferedData(grok.Model):
>     grok.implements(IReferedData, IHasIncomingRelations)
>     def __init__(self, Name):
>         self.name = Name

You could mix IHasIncomingRelations into the interface as well, but this 
works just fine too.

> The the data that has a reference to ReferedData:
> class ITestData(Interface):
>     rel = relationfield.Relation(title=u'Relation')
> 
> class TestData(grok.Model):
>     grok.implements(ITestData, HasOutgoingRelations)

Yup, looks good.

> and in my add form I do this:
> class Add(grok.AddForm):
>     grok.context(Interface)
>     form_fields = grok.Fields(ITestData,
> select=schema.Choice(title=u'Relation', source=TestSource())).omit('rel')
> 
>     @grok.action('Add')
>     def Add(self, **data):
>         testdata = TestData()
>         intids = component.getUtility(IIntIds)
>         id = intids.getId(data['select'])
>         del data['select']
>         self.applyData(testdata, **data)
>         testdata.rel = relationfield.RelationValue(id)  
>         grok.getSite()['testdata'][str(len(grok.getSite()['testdata']))] =
> testdata
>         self.redirect(self.url(testdata))

This is unfortunately a bit of a workaround, generating the other widget 
and ignoring the widget for 'rel'. I need to look into making this work 
properly so you don't have to do this.

> (the applyData is strictly not necessary, but will be if there would be
> other fields associated to ITestData)
> 
> And that seems to do it! I can search relations and everything.

Awesome! I'm very impressed you got it figured out!

> Anyone who has any remarks about things I should have done differently
> perhaps, let me know, I can only learn from it.

Here you are. I sure hope you'll stick around on grok-dev and figure 
more stuff out! Perhaps with my hints included you could turn it into a 
document we can include on grok.zope.org?

Regards,

Martijn



More information about the Grok-dev mailing list