[sqlalchemy] Re: Pre-commit validation spanning multiple tables/ORM classes
-Original Message- From: sqlalchemy@googlegroups.com [mailto:sqlalch...@googlegroups.com] On Behalf Of Randall Nortman Sent: 22 September 2009 16:31 To: sqlalchemy Subject: [sqlalchemy] Pre-commit validation spanning multiple tables/ORM classes [SNIP] So if I do this with a SessionExtension.before_commit(), I would have to iterate through the new, dirty, and deleted instances lists, inspect the type of each instance, and do whatever is required. I am not sure, though, how to handle the case of a change in membership in the parent/child relationship -- the child instance that is present in the dirty list will have only the new parent on it -- how do I find out what the old parent was, so I can validate it? If a flush has already occurred, the old value is already lost in the context of the current transaction, and I think that if I open a new transaction inside a before_commit() validator I'm just asking for trouble. Do I need to instrument the Child class with a descriptor that tracks changes to the parent and remembers the old parent? Or can I set the cascade option in such a way that the old parent will end up in the dirty list, even though there are no changes to its underlying table, and in fact it may never have been explicitly loaded into the session? (I must admit to be somewhat unsure of what the different cascade options do -- but they don't seem to be useful for tracking something like this.) I can't answer most of your question, but as far as finding out what the old parent was, could you use the get_history function? http://www.sqlalchemy.org/docs/05/reference/orm/mapping.html#sqlalchemy .orm.attributes.get_history Hope that helps, Simon --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups sqlalchemy group. To post to this group, send email to sqlalchemy@googlegroups.com To unsubscribe from this group, send email to sqlalchemy+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en -~--~~~~--~~--~--~---
[sqlalchemy] Re: Pre-commit validation spanning multiple tables/ORM classes
My issue with SQLA validators is that they don't allow inconsistent state even on fields of a single object, which makes multi-field validation impossible. Eg. imagine you have fields tax_id and country_code on a customer object. For country code 'us', tax_id should be 9 digits long; for country code 'ca', tax_id should be 15 digits long . If you create appropriate validators on both fields, you will never be able to change either the tax_id or country_code of any customer object. The solution would be to allow inconsistent objects but do validation just before commit. I've been planning on implementing such validation for a project I'm working on, so any suggestions are welcome. On Sep 22, 5:30 pm, Randall Nortman wondercl...@gmail.com wrote: In my application, I have a set of tables that model parts of what are conceptually composite objects, and I need to validate that the state of the objects is coherent before committing them to the database. In the course of building up the network of objects, the state may be temporarily inconsistent (because it will, in general, be impossible to maintain consistency at every step of the process), but I want to make sure it is consistent by the time it hits the database. I think the answer is to make a SessionExtension and use before_commit(), but I still have some questions about exactly how that works. To make the question concrete, I have a one-to-many relationship, and the relevant state is the state of the parent+children composite. Any change to a child's attributes needs to trigger re-validation of the parent, and obviously any change in membership in the collection of children needs to trigger revalidation. In particular, if a child moves from one parent to another, then *both* parents must be re- validated before the transaction is committed. All this validation needs to occur even though the parent table is not modified in any of those cases. And I think I will likely want this to work also in a many-to-many relationship, where any change to the association table should trigger validation of all related (or newly unrelated) objects. Furthermore, I want to work with these objects as individual Parent and Child objects, not a single ParentWithChildren object. Or at a minimum, I want to be able to pass around and modify Child objects on their own; if I get the Children every time I ask for the Parent, that's fine. The @validates decorator is largely useless for this purpose, as it validates a particular attribute of a particular class, and it gets called at the wrong time, and in the case of collections, only gets called on append events, not remove events (afaict). So if I do this with a SessionExtension.before_commit(), I would have to iterate through the new, dirty, and deleted instances lists, inspect the type of each instance, and do whatever is required. I am not sure, though, how to handle the case of a change in membership in the parent/child relationship -- the child instance that is present in the dirty list will have only the new parent on it -- how do I find out what the old parent was, so I can validate it? If a flush has already occurred, the old value is already lost in the context of the current transaction, and I think that if I open a new transaction inside a before_commit() validator I'm just asking for trouble. Do I need to instrument the Child class with a descriptor that tracks changes to the parent and remembers the old parent? Or can I set the cascade option in such a way that the old parent will end up in the dirty list, even though there are no changes to its underlying table, and in fact it may never have been explicitly loaded into the session? (I must admit to be somewhat unsure of what the different cascade options do -- but they don't seem to be useful for tracking something like this.) And lastly, what do I do inside before_commit() if I want to prevent the commit from proceeding? Do I just raise an exception? Any particular type of exception, or is it my choice? Sorry for the long question, and thanks for any assistance, Randall --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups sqlalchemy group. To post to this group, send email to sqlalchemy@googlegroups.com To unsubscribe from this group, send email to sqlalchemy+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en -~--~~~~--~~--~--~---
[sqlalchemy] Re: Pre-commit validation spanning multiple tables/ORM classes
bojanb wrote: My issue with SQLA validators is that they don't allow inconsistent state even on fields of a single object, which makes multi-field validation impossible. meaning, you set A.a and you can't depend on A.b being correct yet ? Well sure. How would you have it done ? Something has to trigger the validate event at some point. So if you need to wait for all of A.a, A.b, A.c, etc. to be setup first, then sure you'd throw your validation into before_flush() or mapper extension before_insert()/before_update() - or just tailor your classes' interface as needed, such as A.set_values(a, b, c). Personally I opt for the latter since its simple and produces an immediate validation effect rather than waiting for a flush. --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups sqlalchemy group. To post to this group, send email to sqlalchemy@googlegroups.com To unsubscribe from this group, send email to sqlalchemy+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en -~--~~~~--~~--~--~---
[sqlalchemy] Re: Pre-commit validation spanning multiple tables/ORM classes
meaning, you set A.a and you can't depend on A.b being correct yet ? Well sure. How would you have it done ? Something has to trigger the validate event at some point. So if you need to wait for all of A.a, A.b, A.c, etc. to be setup first, then sure you'd throw your validation into before_flush() or mapper extension before_insert()/before_update() - or just tailor your classes' interface as needed, such as A.set_values(a, b, c). Personally I opt for the latter since its simple and produces an immediate validation effect rather than waiting for a flush. What I meant to say is that validators don't fit well with something that I'm attempting to do. I'll probably implement a little validation framework that will do validations just before a flush. I'll be sure to post it here if its interesting enough! --~--~-~--~~~---~--~~ You received this message because you are subscribed to the Google Groups sqlalchemy group. To post to this group, send email to sqlalchemy@googlegroups.com To unsubscribe from this group, send email to sqlalchemy+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en -~--~~~~--~~--~--~---