There are multiple ways of doing that...
...depends to a large extent on how the designer puts things together. If we had, for example, a Grade Unit and a Student Unit:
class Grade(dejavu.Unit):\n pass\nGrade.set_properties({'Value': float,\n 'StudentID': unicode})\n\nclass Student(dejavu.Unit):\n def GPA(self):\n grades = [x.Value for x in self.Grade()]\n if grades:\n return sum(grades) / len(grades)\n else:\n return 0.0 # This may not be what you want\n\nnamespace.associate(Student, 'ID', Grade, 'StudentID')
That .associate function binds a new .Grade() method to the Student class*; said method looks up all Grade objects which have a StudentID equal to our Student.ID; storage managers are free to optimize that call with a JOIN if they manage both classes**. Our Student.GPA() function uses this (as "self.Grade()") to recall associated units.
So developer code to calc the GPA for multiple students would then look like this (assuming you want all students, all years, etc.):
gpas = [x.GPA() for x in namespace.recall(Student)]\nif gpas:\n avg_gpa = sum(gpas) / len(gpas)\nelse:\n avg_gpas = 0.0 # Again, this may not satisfy requirements...
Is it as fast as straight SQL? Guaranteed not. But is it fast enough? Probably. If it isn't, there's nothing stopping the developer from hand-crafting an SQL statement and bypassing some of the layers, if he wants to completely sacrifice maintainability and portability. But I've built three apps on top of this framework now, and haven't run into a speed conflict yet.
Fu xiansheng
* And, incidentally, makes a reciprocal Student() method for the Grade class.
** However, one of the design points of Dejavu is that the framework does not assume all data is managed by the same storage manager unless explicitly told so by the deployer. At my company, for example, I have expense Transactions in one store and income in another, and have to mix them to show, say, amount paid on an invoice. This is done transparently from the point of view of application code--it's handled by a mixing Unit Server in the middle layer. When the day comes that we decide to stop using the second store (which is 3rd party code), all I have to do is change a line in a .conf file, and roll on. I could even build a tool very quickly to migrate the data for me; it's on my list of things to write for version 4.1.