A Cool New Dejavu Feature
So I was working on something for Amor and came up against a limitation in Dejavu. I was working with a Unit setup similar to this:
import datetime
from dejavu import Unit, UnitProperty
class Event(Unit):
Name = UnitProperty(unicode)
Start = UnitProperty(datetime.datetime)
End = UnitProperty(datetime.datetime)
# a far Contact.ID
ContactID = UnitProperty(int)
# also a far Contact.ID
HostID = UnitProperty(int)
class Contact(Unit):
FirstName = UnitProperty(unicode)
LastName = UnitProperty(unicode)
BirthDate = UnitProperty(datetime.date)
Event.many_to_one('ContactID', Contact, 'ID')
Conceptually, Events are associated to two Contacts; a general “contact” for the event and a “host”. Dejavu allowed rigging up the first association easily (see Event.many_to_one(...) above). So I had Events and Contacts, and a many-to-one association between them based on the Event.ContactID and the Contact.ID.
That allows some nice recalling (querying) possibilities:
from myapp import arena box = arena.new_sandbox() expr = lambda evt, con: con.LastName == 'Smith' # recall (Event, Contact) pairs where Contact.LastName == 'Smith' results = box.recall(Event & Contact, expr)
But what if I wanted to recall all Events and Contacts based on some attribute of the host Contact? I could create a custom UnitAssociation, which Dejavu provides for:
class EventHostAssociation(UnitAssociation):
to_many = False
register = False
def related(self, unit, expr=None):
'''Define a magic recaller method...'''
host = unit.sandbox.unit(Contact, ID=unit.HostID)
return host
descriptor = EventHostAssociation('HostID', Contact, 'ID')
descriptor.nearClass = Event
Event._associations['Event Host'] = Event.Host = descriptor
Once I had recalled some Event units, I could call Event.Host() on them to get the related Contact. That is convenient, but for doing large scale operations based on some attribute of the host Contact, it would mean many potential hits on the underlying storage mechanism (probably a DB).
The problem was that even though Dejavu allows for custom UnitAssociations, there is no way to define what association path to prefer when doing a multirecall (JOIN). Since the Event>–Contact association was already defined using Event.ContactID and not Event.HostID, I was stuck.
I brought this situation to the attention of Dejavu’s author, Robert Brewer. After a short while, he came up with a solution and an idea on how to implement it. I set off to work, and the result is that in the latest Dejavu trunk code, you can do this:
#create a UnitJoin object from two related Units join = Event & Contact #set a non-default custom UnitJoin path join.path = 'Event Host' # setup an expression that will yield Events and (host) Contacts that are # older than a certain demographic expr = lambda evt, hst: hst.BirthDate < datetime.date(1978, 8, 17) results = box.recall(join, expr)
By setting the join.path to ‘Event Host’, I instructed Dejavu to use the custom UnitAssociation that I created above when performing that particular recall of Events and Contacts. Since the underlying storage mechanism I was using was a database that supports JOINs, I was able to recall all the Events and (host) Contacts that met my criteria in one hit on the DB.
So now Dejavu rocks just a little bit more*.
cw
* This implementation of this functionality as I described it is present in the trunk as of now, but might change somewhat before the next official release of Dejavu (1.5).
November 18th, 2006 at 5:04 pm
Great writeup! And thanks for the contribution.
Incidentally, in your (stripped-down) example, you don’t need the EventHostAssociation subclass at all–just use ToOne. You only need a custom subclass if you’re using some kind of derivative value to do the mapping (as opposed to the normal nearKey -> farKey comparison).
November 18th, 2006 at 8:36 pm
Ah, thanks for that tip. So, in my example above, the descriptor created with EventHostAssociation would be replaced with:
descriptor = dejavu.ToOne(’HostID’, Contact, ‘ID’)
descriptor.nearClass = Event
Event._associations['Event Host'] = Event.Host = descriptor
Does that look right?
November 18th, 2006 at 10:00 pm
Yep, assuming you’re happy with an association in one direction only.