ANN: New product for managing relations
It is stable and functional, but only used in one project. So I considder it version 0.0.1. http://www.zope.org/Members/maxm/productList/mxmRelations/ ------------------------------------------------ This is the mxmRelations product It is made to solve a few common problems with Zope's object oriented database. Normally in a relational database some tables will be used to show relations between rows. It can be both "one to many", and "many to many". In Zope, doing the same thing is more difficult than it has to be. The usual way of doing relations in Zope is by using the list widget in a zClass, or to store the relations in a home made list or dictionary for the pupose. There is several problems with this approach. Let's take an examle. The students in classes example Imagine a school with several classes and several students. Some of the students wil take some of the classes. So theres is a relation between some students, and some classes. A simple structure for this in Zope would be to have two folders "classes" and "students":: classes/ class_1 class_2 class_3 ... etc. students/ student_1 student_2 student_3 student_4 Ordinarily in zope you would then write two classes something like:: class Klass: # hmmm, stupid example def __init__(self, id, title): self.id = id self.title = title self.students_in_class = [] class Student: # hmmm, stupid example def __init__(self, id, title): self.id = id self.title = title self.taking_classes = [] The problem is that the two classes have to maintain the same relations. So the picture could be like:: class_1.students_in_class = ['student_1', 'student_2'] class_2.students_in_class = ['student_3', 'student_4'] class_3.students_in_class = ['student_1', 'student_4'] Or you might even keep backwards relations so that you can easily see whitch students takes which classes:: student_1.taking_classes = ['class_1','class_3'] student_2.taking_classes = ['class_1'] student_3.taking_classes = ['class_2'] student_4.taking_classes = ['class_2','class_3'] This is the bad situation where you have to keep two otherwise unrelated sets of relations up to date. In some cases the objects might even be spread out in different folders. Then it can get real ugly fast. You get direct paths in the code, duplication of code and functionality. So then you want to make a listwidget where the user can select which classes that the students take:: <select name="students_in_class:list" multiple> <dtml-in "students.objectValues()"> <option value="<dtml-var "getId()">"><dtml-var "title_or_id()"></option> </dtml-in> </select> And you want those selected to be hilited:: <select name="students_in_class:list" multiple> <dtml-in "students.objectValues()"> <option<dtml-if "getId() in taking_classes" > selected</dtml-if > value="<dtml-var "getId()">"><dtml-var "title_or_id()"></option> </dtml-in> </select> Here is another problem. What if a student drops out, and is deleted from Zope, but is still in some of the "students_in_class" list under some of the class objects. How do we remove the dead relations? One way is to make code ignoring objects no longer in existance:: <select name="students_in_class:list" multiple> <dtml-in "students.objectValues()"> <dtml-try> <option<dtml-if "getId() in taking_classes" > selected</dtml-if > value="<dtml-var "getId()">"><dtml-var "title_or_id()"></option> <dtml-except> </dtml-try> </dtml-in> </select> But that's not really a solution is it? And you have to do it for each and every object that reference another object. Again repetition of code and functionality. Another problem is that a student also can be referenced in the student councel, the school mailinglist, the holiday list etc. Suddenly an object can be related to many different objects. And all of then have to have the same code doing the relational-housholding stuff. Boring! So here's my solution The mxmRelations product handles all the relations. Shows only the valid ones, and deletes the dead and rotten ones. It has a very simple API with only five methods:: ######################################################## # Public methods. def relate(objs1, objs2): """ Sets relations between objects. If there allready is a key it appende the relations else it creates a new key with a list of relations """ def unrelate(objs1, objs2): """ Removes relations between objects """ def delete(obj): """ Removes all references to the object. Used ie. if an object is deleted. """ def get(obj, meta_types=None): """ Returns all relations to this object, or an empty list """ def getSmartList(self, obj, objects, meta_types=None): """ returns a list of objects with parameters (id, title, path, selected) Very usable for making selection widgets, or lists of checkboxes. "obj" is the object that we are looking for relations too. "objects" is a list of objects that we want to know whether they are related to "obj". """ So now the site from before will have the following structure:: relations_class_students # instance of the relation product classes/ class_1 class_2 class_3 ... etc. students/ student_1 student_2 student_3 student_4 And the code to make a list widget is:: <select name="students_in_class:list" multiple> <dtml-in "relations_class_students.getSmartList(this(), students.objectValues())" sort=id> <option value="<dtml-var path>"<dtml-if selected > selected</dtml-if>><dtml-var title></option> </dtml-in> </select>
participants (1)
-
maxm