Thread: [SQLObject] Inheritance branch
SQLObject is a Python ORM.
Brought to you by:
ianbicking,
phd
From: Ian B. <ia...@co...> - 2005-01-14 22:56:26
|
First, sorry I've taken so long to respond to this. I've had a long reluctance when it comes to the object modeling in a relational database; I'm pretty comfortable with modeling a relational database as objects, but inheritance switches that around, and I feel like it's opening a can of worms. But I haven't looked at it closely enough to decide quite why, so I've held off of responding. Probably it would have been easier to simply give an incomplete answer earlier. But anyway, better late than never. And this will still be an incomplete answer. One specific worry I have is that this will trigger all sorts of questions about how SQLObject can be used to model certain inheritance relationships. And I don't even really like inheritance with normal objects, where there isn't any mismatch. Using the Person/Employee example, it's unclear to me from the documentation what Person.get(n) vs. Employee.get(n) does. Does Person.get(n) where childName == 'Employee' return an Employee instance? (At least with .get that looks more sane) What about select results and join results? What about results where there's a heterogeneous list? Can you change types, e.g., set childName? What does it mean when you have deeper inheritance hierarchies? How does this relate to caching, are super and subclass instances cached separately? And if an object changes type? All these questions make me feel more reluctant still, as there's more than one possible, valid answer for them. I like questions where there's one Right answer, because that means a good interface; if the decisions are implementation-driven it usually means an arbitrary interface. _inheritable isn't a good name, because it's really a specific kind of inheritance, where (in an RDBMS) there's several possible kinds of inheritance. It's more like polymorphism than just inheritance. What are the use cases that people have for using this functionality? Can they be met some other way? I'd be happier adding more relational concepts instead of more OO concepts. E.g., something like: class Person(SQLObject): firstName = StringCol() lastName = StringCol() class Employee(JoinedSQLObject): _parent = 'Person' position = StringCol() Then when you select from Person, it does a LEFT JOIN on Employee, and folds in those attributes (NULL if not applicable). Or maybe there's other ways of modeling it, but however, I think it's a more solid interface if it's thought about relationally instead of in terms of inheritance. -- Ian Bicking / ia...@co... / http://blog.ianbicking.org |
From: Carlos R. <car...@gm...> - 2005-01-14 23:32:58
|
On Fri, 14 Jan 2005 16:55:54 -0600, Ian Bicking <ia...@co...> wrote: > First, sorry I've taken so long to respond to this. I've had a long > reluctance when it comes to the object modeling in a relational > database; I'm pretty comfortable with modeling a relational database as > objects, but inheritance switches that around, and I feel like it's > opening a can of worms. Good point. It's funny because SQLObject is often referred to as a ORM - Object Relational Mapper; and that definition implies for many people that it should map a object-oriented design to a relational representation. But in fact, it is structured as the opposite; for the lack of a better word, it's a 'ROM', a 'relational-to-object mapper'. One immediate consequence is that relational model is constrained by a much simpler set of rules than the ones that apply for a object-oriented design. Many of the issues simply disappear if you follow the relational road. > What are the use cases that people have for using this functionality? > Can they be met some other way? I'd be happier adding more relational > concepts instead of more OO concepts. E.g., something like: > > class Person(SQLObject): > firstName = StringCol() > lastName = StringCol() > > class Employee(JoinedSQLObject): > _parent = 'Person' > position = StringCol() > > Then when you select from Person, it does a LEFT JOIN on Employee, and > folds in those attributes (NULL if not applicable). Or maybe there's > other ways of modeling it, but however, I think it's a more solid > interface if it's thought about relationally instead of in terms of > inheritance. I like this. It makes relational-style inheritance very convenient, and avoids some difficult questions. +1 (IMHO!). -- Carlos Ribeiro Consultoria em Projetos blog: http://rascunhosrotos.blogspot.com blog: http://pythonnotes.blogspot.com mail: car...@gm... mail: car...@ya... |
From: alexander s. <al...@an...> - 2005-01-17 08:23:38
|
Carlos Ribeiro wrote, at 15.01.2005 1:32: > >>First, sorry I've taken so long to respond to this. I've had a long >>reluctance when it comes to the object modeling in a relational >>database; I'm pretty comfortable with modeling a relational database as >>objects, but inheritance switches that around, and I feel like it's >>opening a can of worms. > > Good point. It's funny because SQLObject is often referred to as a ORM > - Object Relational Mapper; and that definition implies for many > people that it should map a object-oriented design to a relational > representation. But in fact, it is structured as the opposite; for the > lack of a better word, it's a 'ROM', a 'relational-to-object mapper'. > One immediate consequence is that relational model is constrained by a > much simpler set of rules than the ones that apply for a > object-oriented design. Many of the issues simply disappear if you > follow the relational road. ironically, SQLObject serves quite well as ORM, but has very little use as ROM: you cannot take a legacy database and use SQLObject to access it because of the restrictions SQLObject places on the database schema. best wishes, alex. |
From: Ian B. <ia...@co...> - 2005-01-18 17:11:32
|
alexander smishlajev wrote: > ironically, SQLObject serves quite well as ORM, but has very little use > as ROM: you cannot take a legacy database and use SQLObject to access it > because of the restrictions SQLObject places on the database schema. That hasn't been my experience, but most of the databases I've been accessing follow reasonable standards and aren't too terribly large. The only real sticking point that I can think of is the presence of a single, immutable primary key. Compound keys would be a useful feature. Support for tables without primary keys (as SQLObject instances; we already have limited support for them as joins) and support for mutable primary keys is unlikely to happen. -- Ian Bicking / ia...@co... / http://blog.ianbicking.org |
From: Carlos R. <car...@gm...> - 2005-01-18 17:34:50
|
On Tue, 18 Jan 2005 11:10:43 -0600, Ian Bicking <ia...@co...> wrote: > alexander smishlajev wrote: > > ironically, SQLObject serves quite well as ORM, but has very little use > > as ROM: you cannot take a legacy database and use SQLObject to access it > > because of the restrictions SQLObject places on the database schema. > > That hasn't been my experience, but most of the databases I've been > accessing follow reasonable standards and aren't too terribly large. > The only real sticking point that I can think of is the presence of a > single, immutable primary key. Compound keys would be a useful feature. > Support for tables without primary keys (as SQLObject instances; we > already have limited support for them as joins) and support for mutable > primary keys is unlikely to happen. When I need compound keys, I usually end up resorting to the selectBy() method. Couldn't it be optimized a little bit? A composite key method could be 'registered' in the class declaration, hinting at the need for extra indexes for that columns. The example below may seem a little bit forced, but it shows the basic idea: class Person(SQLObject): firstName = StrCol() lastName = StrCol() byName = CompositeKey('firstName', 'lastName') The CompositeKey function would return a method, searching for the firstName & lastName. The appropriate indexes can also be created automatically. Is it a workable solution? -- Carlos Ribeiro Consultoria em Projetos blog: http://rascunhosrotos.blogspot.com blog: http://pythonnotes.blogspot.com mail: car...@gm... mail: car...@ya... |
From: alexander s. <al...@an...> - 2005-01-18 19:45:46
|
Ian Bicking wrote, at 18.01.2005 19:10: > >> ironically, SQLObject serves quite well as ORM, but has very little >> use as ROM: you cannot take a legacy database and use SQLObject to >> access it because of the restrictions SQLObject places on the database >> schema. > > That hasn't been my experience, but most of the databases I've been > accessing follow reasonable standards and aren't too terribly large. well, i may be just wrong. i cannot say i have much experience in reverse engineering with SQLObject. > The only real sticking point that I can think of is the presence of a > single, immutable primary key. Compound keys would be a useful feature. > Support for tables without primary keys (as SQLObject instances; we > already have limited support for them as joins) and support for mutable > primary keys is unlikely to happen. you are right, the lack of compound keys is one of the main problems. another strict requirement is requisition for auto-incremented integer primary key. legacy databases (especially ones designed with "natural" rather than "artifical" keys) sometimes happen to have character key values. i also vaguely remember something about many-to-many relationships, but i cannot recall the details - it was more than a year ago. yesterday i thought it was impossible to use non-standard table names, but i was wrong about that, so it must be something else. and finally, it seems to me that SQLObject cannot create table classes by loading existing schema (but again, i may be wrong here). that means that the database schema definitions must be maintained in several applications simultaneously. such maintenance is rather annoying affair, and results easily get out of sync. with all that, by no means i wanted to say that SQLObject is somehow "bad" or "wrong"; if i made such impression, it's probably because of my poor english, and i am really sorry about that. i like SQLObject very much. thanks a lot, Ian! best wishes, alex. |
From: Ian B. <ia...@co...> - 2005-01-18 23:08:37
|
alexander smishlajev wrote: >> The only real sticking point that I can think of is the presence of a >> single, immutable primary key. Compound keys would be a useful >> feature. Support for tables without primary keys (as SQLObject >> instances; we already have limited support for them as joins) and >> support for mutable primary keys is unlikely to happen. > > > you are right, the lack of compound keys is one of the main problems. > > another strict requirement is requisition for auto-incremented integer > primary key. legacy databases (especially ones designed with "natural" > rather than "artifical" keys) sometimes happen to have character key > values. You can give the id explicitly when you create a row. There's no built-in support for providing a id generation function or anything, but given an id you can use it. Generally if you are using non-integer ids this will be necessary. > i also vaguely remember something about many-to-many relationships, but > i cannot recall the details - it was more than a year ago. yesterday i > thought it was impossible to use non-standard table names, but i was > wrong about that, so it must be something else. It's very possible you'll have places where you can't use MultipleJoin or RelatedJoin. You may have to write some SQL on your own, provide a primary key and introduce another class, or implement your own join (which shouldn't be too hard). If it's just a naming issue, there are options to change the names of tables and columns that those joins use. > and finally, it seems to me that SQLObject cannot create table classes > by loading existing schema (but again, i may be wrong here). that means > that the database schema definitions must be maintained in several > applications simultaneously. such maintenance is rather annoying > affair, and results easily get out of sync. Use "_fromDatabase = True" in your class definition, and it'll try its best. Using that you can still explicitly specify some columns, while letting any unspecified columns get automatically created. > with all that, by no means i wanted to say that SQLObject is somehow > "bad" or "wrong"; if i made such impression, it's probably because of my > poor english, and i am really sorry about that. > > i like SQLObject very much. thanks a lot, Ian! Now maybe you can like it more! ;) -- Ian Bicking / ia...@co... / http://blog.ianbicking.org |
From: Ksenia M. <kse...@gm...> - 2005-01-15 00:47:19
|
> class Person(SQLObject): > firstName = StringCol() > lastName = StringCol() > > class Employee(JoinedSQLObject): > _parent = 'Person' > position = StringCol() > > Then when you select from Person, it does a LEFT JOIN on Employee, and > folds in those attributes (NULL if not applicable). Or maybe there's > other ways of modeling it, but however, I think it's a more solid > interface if it's thought about relationally instead of in terms of > inheritance. Do I understand it correctly, that it represents a one-to-one relationship, and table employee has person_id field? And if you select from Employee, it does a plain old INNER JOIN to Person? I think I like it a lot, also because the database integrity can be enforced (e.g. person_id in the table employee cannot be null and must be unique). In the current inheritance implementation the integrity is not enforced, which makes it less reliable and modifying the database without SQLObject not easy. I thought about my usecase and can't think of anything that will be impossible with this approach. Using current inheritance branche, I have some difficulties with searching, when two superclasses are joined and you want to find something based on the information from the base class. But I think with the new approach it will be easier. +1 :-) -- Ksenia |
From: Ian B. <ia...@co...> - 2005-01-15 02:04:44
|
Ksenia Marasanova wrote: >>class Person(SQLObject): >> firstName = StringCol() >> lastName = StringCol() >> >>class Employee(JoinedSQLObject): >> _parent = 'Person' >> position = StringCol() >> >>Then when you select from Person, it does a LEFT JOIN on Employee, and >>folds in those attributes (NULL if not applicable). Or maybe there's >>other ways of modeling it, but however, I think it's a more solid >>interface if it's thought about relationally instead of in terms of >>inheritance. > > > Do I understand it correctly, that it represents a one-to-one > relationship, and table employee has person_id field? I was thinking it would be a LEFT JOIN, and there would one or zero employee rows for every person. Then it's closer to subclassing, in that every person isn't an employee, but every employee is a person. I'm actually not psyched about this particular syntax, I just made it up as an example. I think the way to figure it out would be with a couple use cases, then figure out what the database model would be, then figure out how to express that in SQLObject, i.e., start with the relational side. But I haven't thought about it enough to figure out where that is likely to lead. -- Ian Bicking / ia...@co... / http://blog.ianbicking.org |
From: Oleg B. <ph...@ph...> - 2005-01-15 09:25:30
|
Hello, Ian! Thank you for the answer. Well, let's discuss it... On Fri, Jan 14, 2005 at 04:55:54PM -0600, Ian Bicking wrote: > First, sorry I've taken so long to respond to this. I've had a long > reluctance when it comes to the object modeling in a relational > database; I'm pretty comfortable with modeling a relational database as > objects, but inheritance switches that around, and I feel like it's > opening a can of worms. But I haven't looked at it closely enough to > decide quite why Because of the Impedance Mismatch, of course! http://c2.com/cgi/wiki?ObjectRelationalImpedanceMismatch !!! > Using the Person/Employee example, it's unclear to me from the > documentation what Person.get(n) vs. Employee.get(n) does. Does There is no difference (in current implementation.) > Person.get(n) where childName == 'Employee' return an Employee instance? Yes. > (At least with .get that looks more sane) What about select results > and join results? What about results where there's a heterogeneous There is nothing magical in current implementation. All selects finally return a list of IDs, and by the every ID in the list SQLObject creates object of an appropriate (child) class. Heterogeneous lists? No problem - the result will be a list of children, not parents. > list? Can you change types, e.g., set childName? What does it mean No, you canot. > when you have deeper inheritance hierarchies? Why not? You can create objects of any class in the hierarchy, and SQLObject will store their childNames correctly. No problem here. > All these questions make me feel more reluctant still, as there's more > than one possible, valid answer for them. I like questions where > there's one Right answer, because that means a good interface; if the > decisions are implementation-driven it usually means an arbitrary interface. Alas, there is no One Rule for mapping inheritance unto relational algebra. There is a number of stratigies: http://www.objectmatter.com/vbsf/docs/maptool/ormapping.html What Daniel Savard implemented and I'm maintainig is similar to "vertical inheritance", where every table contains only its own data, and a reference to a child class, if any. > What are the use cases that people have for using this functionality? Ah, that's a big question. Let me dive into the many subtle details. Our comercial program works with a database based on Health Level Seven Reference Information Model: http://www.hl7.org/library/data-model/RIM/C30204/rim.htm (warning: the page is 1 megagbyte in size + images). There is a forest of deep hierarchy, based on a number of foundation classes. Let me give a few examples from our application, using three foundation classes - Entity (all kind of objects), Role (that plays an Entity), and Act (that is being planned, performed, and so on). A Person is an Entity. A Person can play a number of Roles - a Patient and a Employee, e.g. A Person can play multiple Roles - in the scope of one Organization (s)he is just a Patient, in another Organization - an Employee. An Organisation is an Entity, and it can play its Roles, too. For example, a Hospital, or a Subcontractor. An instance of Entity (Person, for example) has an attribute playedRole = MultipleJoin("Role"). But I dont want to list instances of Role - I want to list instances of the real descendants of Role (Patient, Employee). This is exactly what Savard's inheritance does! Roles can be linked together using RoleLink. class RoleLink(SQLObject): source = ForeignKey("Role") target = ForeignKey("Role") If I link a Patient and a Hospital link = RoleLink(source=patient, target=hospital) and then dereference the link.source - should I get a Role or a concrete Patient? Now it is a Patient, and that's what I need. Our application is full of such connections and links. Acts are tied together using ActRelationship. But in dereferencing act_rel.target I want an Observation, not an Act. Acts are linked to Roles using Participations, and Act, of course, has the attribute participation = MultipleJoin("Participation"). And in dereferencing observation.participation[0].target I want not an Act, but whatever child class should be there - Job, Observation, etc... Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Oleg B. <ph...@ma...> - 2005-01-15 10:13:14
|
On Sat, Jan 15, 2005 at 12:25:11PM +0300, Oleg Broytmann wrote: > Acts are linked to Roles using Participations, and Act, of course, > has the attribute participation = MultipleJoin("Participation"). And in > dereferencing observation.participation[0].target I want not an Act, but > whatever child class should be there - Job, Observation, etc... Errm... not Acts, but Roles observation.participation[0].role ...but you got the idea. I need MultipleJoin to a parent table, but I want to receive children classes. Even if the list of children is heterogeneous. Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Ian B. <ia...@co...> - 2005-01-18 18:01:11
|
Oleg Broytmann wrote: >>Using the Person/Employee example, it's unclear to me from the >>documentation what Person.get(n) vs. Employee.get(n) does. Does > > > There is no difference (in current implementation.) > > >>Person.get(n) where childName == 'Employee' return an Employee instance? > > > Yes. > > >> (At least with .get that looks more sane) What about select results >>and join results? What about results where there's a heterogeneous > > > There is nothing magical in current implementation. All selects > finally return a list of IDs, and by the every ID in the list SQLObject > creates object of an appropriate (child) class. Heterogeneous lists? No > problem - the result will be a list of children, not parents. Does this mean that a select that returns a heterogeneous list will result in a large number of queries? I.e., are the subclass values fetched through individual selects, or some sort of left join? >>list? Can you change types, e.g., set childName? What does it mean > > > No, you canot. > > >>when you have deeper inheritance hierarchies? > > > Why not? You can create objects of any class in the hierarchy, and > SQLObject will store their childNames correctly. No problem here. Does this mean there's a recursive set of queries to retrieve all data? I.e., if you have an inheritance like: Person Employee Manager And person.child_name = 'employee', and employee.child_name = 'manager', then you'll get a manager? Does that require two extra queries? >>All these questions make me feel more reluctant still, as there's more >>than one possible, valid answer for them. I like questions where >>there's one Right answer, because that means a good interface; if the >>decisions are implementation-driven it usually means an arbitrary interface. > > > Alas, there is no One Rule for mapping inheritance unto relational > algebra. There is a number of stratigies: > http://www.objectmatter.com/vbsf/docs/maptool/ormapping.html > What Daniel Savard implemented and I'm maintainig is similar to > "vertical inheritance", where every table contains only its own data, > and a reference to a child class, if any. Yes, similar but somewhat different, due to the presence of childName; filtered vertical mapping, I guess? >>What are the use cases that people have for using this functionality? > > > Ah, that's a big question. Let me dive into the many subtle details. > Our comercial program works with a database based on Health Level Seven > Reference Information Model: > http://www.hl7.org/library/data-model/RIM/C30204/rim.htm > (warning: the page is 1 megagbyte in size + images). There is a forest > of deep hierarchy, based on a number of foundation classes. Let me give > a few examples from our application, using three foundation classes - > Entity (all kind of objects), Role (that plays an Entity), and Act (that > is being planned, performed, and so on). > > A Person is an Entity. A Person can play a number of Roles - a > Patient and a Employee, e.g. A Person can play multiple Roles - in the > scope of one Organization (s)he is just a Patient, in another > Organization - an Employee. > An Organisation is an Entity, and it can play its Roles, too. For > example, a Hospital, or a Subcontractor. Relationally, I'd say that there is a many-to-many relationship between entities and roles, and a one-to-zero-or-one (is there a short name for that?) relation to person, organization, etc. And I guess these relationships are exclusive. There there's a similar situation with roles, though maybe not... The relationship with roles is a many-to-many typed relationship, like entity 1 is a subcontractor to entity 2, and entity 3 is a patient to entity 2. I'd represent this as a three-column join, (source_entity_id, target_entity_id, relation_type). The query/join methods would return lists of tuples of (relationship, other_entity), or something like that. At the same time, to get the person, organization, etc., or entity, I'd create another accessor, and fetch the subtype explicitly. > An instance of Entity (Person, for example) has an attribute > playedRole = MultipleJoin("Role"). But I dont want to list instances > of Role - I want to list instances of the real descendants of Role > (Patient, Employee). This is exactly what Savard's inheritance does! > > Roles can be linked together using RoleLink. > class RoleLink(SQLObject): > source = ForeignKey("Role") > target = ForeignKey("Role") > If I link a Patient and a Hospital > link = RoleLink(source=patient, target=hospital) > and then dereference the link.source - should I get a Role or a concrete > Patient? Now it is a Patient, and that's what I need. I'd say: entity = Entity(type='patient') patient = Patient(entity=entity, fname='John', lname='Doe') patient.entity.addLink('patient', target=hospital.entity) hospital.entity.linksOfType('patient') == [patient.entity] Maybe explicitly dereferencing the .entity wouldn't be necessary, but it would happen implicitly. I think the result is easier to reason about relationally, while still having all the same functionality. > Our application is full of such connections and links. Acts are tied > together using ActRelationship. But in dereferencing act_rel.target I > want an Observation, not an Act. > Acts are linked to Roles using Participations, and Act, of course, > has the attribute participation = MultipleJoin("Participation"). And in > dereferencing observation.participation[0].target I want not an Act, but > whatever child class should be there - Job, Observation, etc... -- Ian Bicking / ia...@co... / http://blog.ianbicking.org |
From: Oleg B. <ph...@ph...> - 2005-01-24 12:01:09
|
Hello! Well, I have seen http://blog.ianbicking.org/rom-vs-orm.html. Is it the last pronouncement? On Tue, Jan 18, 2005 at 12:00:34PM -0600, Ian Bicking wrote: > Oleg Broytmann wrote: > > There is nothing magical in current implementation. All selects > >finally return a list of IDs, and by the every ID in the list SQLObject > >creates object of an appropriate (child) class. Heterogeneous lists? No > >problem - the result will be a list of children, not parents. > > Does this mean that a select that returns a heterogeneous list will > result in a large number of queries? I.e., are the subclass values > fetched through individual selects, or some sort of left join? Currently, yes. I have a patch that optimize this. I'll commit it soon. > Does this mean there's a recursive set of queries to retrieve all data? > I.e., if you have an inheritance like: > > Person > Employee > Manager > > And person.child_name = 'employee', and employee.child_name = 'manager', > then you'll get a manager? Does that require two extra queries? No. childName always points to the leaf. I.e. person.childName == "Manager" and employee.childName == "Manager". The chain of pointers linking from the leaf to its parent to grandparent and up are constructed at runtime using the chain of Python classes. > > http://www.objectmatter.com/vbsf/docs/maptool/ormapping.html > >What Daniel Savard implemented and I'm maintainig is similar to > >"vertical inheritance", where every table contains only its own data, > >and a reference to a child class, if any. > > Yes, similar but somewhat different, due to the presence of childName; > filtered vertical mapping, I guess? "Inverse vertical mapping", I'd say. childName instead of parent link. > >Our comercial program works with a database based on Health Level Seven > >Reference Information Model: > > http://www.hl7.org/library/data-model/RIM/C30204/rim.htm > >(warning: the page is 1 megagbyte in size + images). [skip] > >class RoleLink(SQLObject): > > source = ForeignKey("Role") > > target = ForeignKey("Role") > >If I link a Patient and a Hospital > > link = RoleLink(source=patient, target=hospital) > >and then dereference the link.source - should I get a Role or a concrete > >Patient? Now it is a Patient, and that's what I need. > > I'd say: > > entity = Entity(type='patient') > patient = Patient(entity=entity, fname='John', lname='Doe') > patient.entity.addLink('patient', target=hospital.entity) > hospital.entity.linksOfType('patient') == [patient.entity] I don't have a choice here. I have the HL7 RIM, and I must implement it, and I must implement it on top of SQL. There have to be a class RoleLink, and the class must have attributes "source" and "target". Not only due to the RIM, but because the class carries additional information, e.g. "this patient was in that hospital from 21 Jan to 12 Feb due to the following reason..." An Entity must have an attribute "playedRoles", that lists all instances of Patients, Organizations and all that. The only choice I have is how to implement this. Daniel Savard's inheritance patch solves all my current needs. If I cannot push it into the mainline SQLObject, I will continue to maintain it separately (in the colorstudy.com repository or in a repository in our company) until I can find an easier way to select all played roles (for example) without patching SQLObject and without creating a hundred of complex accessors (there are really about 30 MultipleJoins currently in our program that are handled with inheritance, and the number will be increased as I'll implement more and more classes from HL7 RIM). Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Oleg B. <ph...@ma...> - 2005-01-24 12:15:27
|
On Mon, Jan 24, 2005 at 03:00:49PM +0300, Oleg Broytmann wrote: > On Tue, Jan 18, 2005 at 12:00:34PM -0600, Ian Bicking wrote: > > Oleg Broytmann wrote: > > > There is nothing magical in current implementation. All selects > > >finally return a list of IDs, and by the every ID in the list SQLObject > > >creates object of an appropriate (child) class. Heterogeneous lists? No > > >problem - the result will be a list of children, not parents. > > > > Does this mean that a select that returns a heterogeneous list will > > result in a large number of queries? I.e., are the subclass values > > fetched through individual selects, or some sort of left join? > > Currently, yes. I have a patch that optimize this. I'll commit it > soon. I just commited the patch into the inheritance branch, revision 552. The patch mostly touches dbconnection.Iteration class. In the .next() method it now prefetches children grouping by childName. The patch tested on Postgres and MySQL. Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Ian B. <ia...@co...> - 2005-01-24 17:54:12
|
Oleg Broytmann wrote: > Well, I have seen http://blog.ianbicking.org/rom-vs-orm.html. Is it > the last pronouncement? No, just my current perspective on the problem. >>Does this mean there's a recursive set of queries to retrieve all data? >> I.e., if you have an inheritance like: >> >>Person >>Employee >>Manager >> >>And person.child_name = 'employee', and employee.child_name = 'manager', >>then you'll get a manager? Does that require two extra queries? > > > No. childName always points to the leaf. I.e. person.childName == "Manager" > and employee.childName == "Manager". > The chain of pointers linking from the leaf to its parent to > grandparent and up are constructed at runtime using the chain of Python > classes. So person.child_name = 'manager' and employee.child_name = 'manager'? I.e., you can still do Employee.select(..), and you may get further subclasses, but you won't get Persons who are not Employee or a subclass. That seems reasonable. >>>Our comercial program works with a database based on Health Level Seven >>>Reference Information Model: >>> http://www.hl7.org/library/data-model/RIM/C30204/rim.htm >>>(warning: the page is 1 megagbyte in size + images). > > [skip] > >>>class RoleLink(SQLObject): >>> source = ForeignKey("Role") >>> target = ForeignKey("Role") >>>If I link a Patient and a Hospital >>> link = RoleLink(source=patient, target=hospital) >>>and then dereference the link.source - should I get a Role or a concrete >>>Patient? Now it is a Patient, and that's what I need. >> >>I'd say: >> >>entity = Entity(type='patient') >>patient = Patient(entity=entity, fname='John', lname='Doe') >>patient.entity.addLink('patient', target=hospital.entity) >>hospital.entity.linksOfType('patient') == [patient.entity] > > > I don't have a choice here. I have the HL7 RIM, and I must implement > it, and I must implement it on top of SQL. There have to be a class > RoleLink, and the class must have attributes "source" and "target". Not > only due to the RIM, but because the class carries additional > information, e.g. "this patient was in that hospital from 21 Jan to 12 > Feb due to the following reason..." OK, but SQLObject can't be expected to be a complete map from a database to a specific API. In this case, I think it's reasonable that there be some (perhaps signficant) code to create the interface you need. So "source" and "target" may not be SQLObject Col objects, but may instead be short functions, or if you need this sort of thing a lot they may be descriptors or something higher-level. > An Entity must have an attribute "playedRoles", that lists all > instances of Patients, Organizations and all that. The only choice I have > is how to implement this. Daniel Savard's inheritance patch solves all my > current needs. If I cannot push it into the mainline SQLObject, I will > continue to maintain it separately (in the colorstudy.com repository or > in a repository in our company) until I can find an easier way to select > all played roles (for example) without patching SQLObject and without > creating a hundred of complex accessors (there are really about 30 > MultipleJoins currently in our program that are handled with > inheritance, and the number will be increased as I'll implement more and > more classes from HL7 RIM). If you need ways to generalize accessors, or the hooks to create different kinds of joins, I'm very open to that. That's the kind of extension that can be decoupled from SQLObject much more easily than inheritance. Maybe it would make sense to take a use case -- maybe HL7, since you are familiar with it -- and think about what database schema should look like, and the Python API should look like, and then consider what the best way to map between those two is. -- Ian Bicking / ia...@co... / http://blog.ianbicking.org |
From: Oleg B. <ph...@ph...> - 2005-01-24 18:57:15
|
On Mon, Jan 24, 2005 at 11:53:14AM -0600, Ian Bicking wrote: > OK, but SQLObject can't be expected to be a complete map from a database > to a specific API. Certainly no! I am not arguing to apply the inheritance patch because it solves my needs. I think it is useful in more general situations. And I may be wrong with it. I had a hope there will be more people discussing the issue. But there were only a few negative comments from Ksenia and Barry. The inheritance patch solves two problems, and both solutions - especially when combined - are important, at least for me. Before Daniel published his patch I did things in a different, complex and fragile way. After I have seen and applied the patch I rewrote most of what I have done, and that greatly simplified the implementation. The problem number one is the inheritance of the attributes. If a Person is an Entity, how can I made person.phone to be entity.phone? The question is not as simple - if a Role points to an Entity (with its "player" attribute) without knowing if it is a Person or an Organization, how can it dereference role.player.phone? I don't want to write class Person(Entity): ... because this causes person.phone to be a separate attribute from entity.phone (without the inheritance patch, I mean). Before the patch I did complex and fragile manipulations with attributes in metaclasses. Reading and writing of person.phone were redirected to entity.phone. The problem number two - heterogeneous MultipleJoin: class Entity(SQLObject): playedRoles = MultipleJoin("Role") I don't want person.playedRoles to be a list of Role class instances - I want it to be a list of instances of appropriate descendants of Role. > "source" and "target" may not be SQLObject Col objects, but may instead > be short functions, or if you need this sort of thing a lot they may be > descriptors or something higher-level. Without the inheritance patch I could implement them as properties. And the implementation probably will be similar - upon dereferencing playedRoles getter selects the list of childNames, find classes for these names, and create the appropriate instances. Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Oleg B. <ph...@ph...> - 2005-01-25 05:53:36
|
Hello! Let me look at it from another angle. Cuurently every user of SQLObject gets some kind of inheritance, for free! (-: Like it or not, but this class Entity(SQLObject): pass class Person(Entity): pass is inheritance. According to http://www.objectmatter.com/vbsf/docs/maptool/ormapping.html it is "horizontal mapping". Daniel Savard's patch just adds another kind, vertival mapping. class Entity(SQLObject): _inheritable = True class Person(Entity): pass Now I can see "_inheritable" isn't a good name, as it doesn't turn inheritance on or off - it switches between horizontal and vertical mappings. It is rather "_verticalInheritance". But other than that I do not see any drawback of the patch. Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Ian B. <ia...@co...> - 2005-01-26 17:30:14
|
Oleg Broytmann wrote: > Hello! Let me look at it from another angle. > > Cuurently every user of SQLObject gets some kind of inheritance, for > free! (-: Like it or not, but this > > class Entity(SQLObject): > pass > > class Person(Entity): > pass > > is inheritance. According to > http://www.objectmatter.com/vbsf/docs/maptool/ormapping.html > it is "horizontal mapping". Yes. At one point this kind of inheritance was simply broken, and the resulting classes didn't function; I did what I thought was the most minimal thing possible to get them to work. From that perspective it wasn't a thoughtful way of implementing inheritance, but it was also the closest I could come to dodging the issue. Which is what I'm still trying to do ;) This inheritance does work well for abstract superclasses, and subclasses can share portions of their definition with their superclasses, but don't rely on the superclass in any way at runtime. This kind of inheritance doesn't add any kind of relation. > Daniel Savard's patch just adds another kind, vertival mapping. Yes. But I wasn't that happy with the first one either ;) Is there a way this can be added without changing SQLObject? E.g., as a SQLObject subclass? I assume some new (stable) hooks will be required, so I'm not saying SQLObject can't be changed for this, but I'd rather avoid the actual inheritance implementation being in SQLObject. -- Ian Bicking / ia...@co... / http://blog.ianbicking.org |
From: Oleg B. <ph...@ph...> - 2005-01-26 17:35:20
|
On Wed, Jan 26, 2005 at 11:29:10AM -0600, Ian Bicking wrote: > Is there a way this can be added without changing SQLObject? E.g., as a > SQLObject subclass? That's an idea I need to contemplate. Now thinking... Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Jamie B. <ja...@la...> - 2005-01-27 03:02:07
|
Would AOP-style runtime-binding inheritance be possible or even relevant for SQLObject? On Wednesday 26 January 2005 11:35 am, Oleg Broytmann wrote: > On Wed, Jan 26, 2005 at 11:29:10AM -0600, Ian Bicking wrote: > > Is there a way this can be added without changing SQLObject? E.g., as a > > SQLObject subclass? > > That's an idea I need to contemplate. Now thinking... > > Oleg. |
From: Ian B. <ia...@co...> - 2005-01-27 03:35:14
|
Jamie Becker wrote: > Would AOP-style runtime-binding inheritance be possible or even relevant for > SQLObject? Well, not AOP-style. There's already runtime binding, in the form of addColumn(). Earlier in the thread we were talking about composite secondary keys, and I mentioned extending that to other kinds of objects (besides column descriptions), and that may apply here. I still haven't seen anything in AOP that can't be done more easily in Python using techniques like descriptors, metaclasses, and the mutability of classes. The real difficulty (from my own perspective) is that .get() (and related methods like .select()) may not return an instance of the class you call it on, and that effects a number of places across the library. -- Ian Bicking / ia...@co... / http://blog.ianbicking.org |
From: Oleg B. <ph...@ma...> - 2005-01-27 10:02:53
|
On Wed, Jan 26, 2005 at 08:56:50PM -0600, Jamie Becker wrote: > Would AOP-style runtime-binding inheritance be possible or even relevant for > SQLObject? I do not understand the question, because I do not understand AOP. AOP is a hack applied to Java byte code in order to do... what? To call pre- and post- methods? Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Oleg B. <ph...@ph...> - 2005-02-03 12:20:59
|
On Wed, Jan 26, 2005 at 11:29:10AM -0600, Ian Bicking wrote: > Is there a way this can be added without changing SQLObject? E.g., as a > SQLObject subclass? I've started to find the way. I just committed revision 569 to the inheritance branch. The patch touches main.py and dbconnection.py. I subclassed SQLObject, the new subclass is called InheritableSQLObject, and I subclass a number of supporting classes - SelectResults and Iteration. I haven't subclass MetaSQLObject yet. I'll try to. Also I haven't applied the latest chages in the trunk - the inheritance branch is still based on SQLObject 0.6.1. Is it better? Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |
From: Ian B. <ia...@co...> - 2005-02-03 23:35:28
|
Oleg Broytmann wrote: > On Wed, Jan 26, 2005 at 11:29:10AM -0600, Ian Bicking wrote: > >>Is there a way this can be added without changing SQLObject? E.g., as a >>SQLObject subclass? > > > I've started to find the way. I just committed revision 569 to the > inheritance branch. The patch touches main.py and dbconnection.py. > I subclassed SQLObject, the new subclass is called > InheritableSQLObject, and I subclass a number of supporting classes - > SelectResults and Iteration. It looks like there's a fair amount of redundancy in the subclasses, but maybe once we merge the branches we can refactor that more cleanly. > I haven't subclass MetaSQLObject yet. I'll try to. Also I haven't > applied the latest chages in the trunk - the inheritance branch is still > based on SQLObject 0.6.1. OK; they are all little bugs driven by the test refactoring. I'm planning on changing MetaSQLObject and have the classes in place -- when that's done you probably won't need to subclass it, as it will be fairly general. There will be a __classinit__ method in SQLObject that gets called everytime a class is created, and so your inheritance subclass can override that method instead. -- Ian Bicking / ia...@co... / http://blog.ianbicking.org |
From: Oleg B. <ph...@ph...> - 2005-02-07 10:48:24
|
Hello. Inheritance branch, revision 570. On Thu, Feb 03, 2005 at 05:34:06PM -0600, Ian Bicking wrote: > > I haven't subclass MetaSQLObject yet. I'll try to. Also I haven't > >applied the latest chages in the trunk - the inheritance branch is still > >based on SQLObject 0.6.1. I split sqlobject/main.py and moved inheritance-related classes into sqlobject/inheritance.py. Merged patches from the revisions 559:569 from the trunk; now the branch seems to be syncronized with the trunk. I still haven't subclass MetaSQLObject yet. Also I haven't moved my inheritance tests to the new framework as I don't have SQLite which is required for the new tests framework. I'm working on it. Oleg. -- Oleg Broytmann http://phd.pp.ru/ ph...@ph... Programmers don't die, they just GOSUB without RETURN. |