New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AssociationProxy needs to act more like a descriptor; "owning_class" is misleading #3423
Comments
Changes by Brendan Abel (@babel):
|
Michael Bayer (@zzzeek) wrote: can you make this a little easier for me and show me a script that raises an exception? thanks. |
Michael Bayer (@zzzeek) wrote: I say this partially because the assocationproxy is a Python descriptor. It should not be called outside of the context of being bound to a class (e.g. from all_orm_descriptors). the all_orm_descriptors use case is strictly so you can get at the .info dictionary. |
Brendan Abel (@babel) wrote: Example script reproducing error. |
Changes by Brendan Abel (@babel):
|
Michael Bayer (@zzzeek) wrote: yeah not sure what can be done here. It's a Python descriptor. It has no idea what the class is until it is called. There is no internal system to support objects that are not mapped attributes such that they get "blessed" as part of a mapping system with a full lifecycle. |
Brendan Abel (@babel) wrote: Just attached a short script that reproduces the error. It's basically the same example used in the Association Proxy docs. My current use case is for a REST framework that automatically unserializes JSON data that corresponds to a table field. It's currently doing this by getting the column/relationship/association_proxy from the mapper and using the data type to decide how to process the JSON data. I'm using the all_orm_descriptors because that was the only place in the mapper (AFAIK) that listed the association proxies. |
Michael Bayer (@zzzeek) wrote: for example, if you initialized it as Python expects, then it's fine:
|
Michael Bayer (@zzzeek) wrote: here's one reason it can't work completely - because mapping has no metaclass requirement, and without that we have no way to know when an attribute is added to a class. So even if we did some kind of "sweep" for attributes that are on the class at the time that we map it, that would fail when the attribute were added after the fact and therefore be inconsistent in its behavior. |
Michael Bayer (@zzzeek) wrote: though that case indicates that all_orm_descriptors is then broken, because it memoizes on first access and wouldn't pick up such attributes anyway... |
Michael Bayer (@zzzeek) wrote: a related issue also came up recently regarding hybrid properties. The object that we get back from all_orm_descriptors is again the descriptor itself; it doesn't represent any SQL expression by itself, only when its invoked. There is a path for that to work also, if it knew its owning class up front. |
Michael Bayer (@zzzeek) wrote: the association proxy mis-implements Python descriptors in any case because it attempts to refer to a single class as state, and this is incorrect. this patch begins to split it out into two objects. the per-owner class needs to be put into a weakref dictionary of some kind so that it can be cached. |
Changes by Michael Bayer (@zzzeek):
|
Michael Bayer (@zzzeek) wrote: There still needs to be something done with all_orm_descriptors such that differentiating between a "descriptor" and the "actual thing the descriptor returns" is more clear, for assocaition proxies, hybrids, and other things; the latter always requires a parent object in order to be correct, and it cannot be assumed to be the same class every time. When you call an AP from an AliasedClass like aliased(User) for example, that is different than calling the original. the AP needs to be refactored to work in this way cleanly, more like a hybrid does. |
Changes by Michael Bayer (@zzzeek):
|
Changes by Michael Bayer (@zzzeek):
|
Changes by Michael Bayer (@zzzeek):
|
Michael Bayer (@zzzeek) wrote: this should be done but for now trying to trim down 1.1 to essentials |
Changes by Michael Bayer (@zzzeek):
|
Changes by Michael Bayer (@zzzeek):
|
Danilo Tuler (@tuler) wrote: Why this was pushed to 1.2 and then to 1.3? I hit a bug that may be related to this, which is a AssociationProxy applied to a AliasedClass generated by with_polymorphic. It raises the exception at https://bitbucket.org/zzzeek/sqlalchemy/src/a1de76c42f6b64808448aed6e821fbb3b988f99b/lib/sqlalchemy/orm/base.py?at=master&fileviewer=file-view-default#base.py-424 |
Michael Bayer (@zzzeek) wrote: @tuler because it is both low priority and very complicated to do correctly. expect it to be pushed out further unless someone wants to work on it. the exception you describe sounds like something entirely different so please open a separate issue with a reproduction case. |
Dmytro Starosud (@dima-starosud) wrote:
|
Michael Bayer (@zzzeek) wrote: with #4116 we've made the concept of "owner" more strictly determined so more folks are hitting this. Here's a test derived from #4202 that shows the issue very simply:
note that the "aptest" example here, as given, can never work; the solution likely needs to remove all concepts like "target_class" completely because these make no sense if you aren't giving it the parent class. In 1.2.3, the aptest example now nicely raises this error:
after this change, it will be called more like this:
|
Changes by Michael Bayer (@zzzeek):
|
Changes by Michael Bayer (@zzzeek):
|
Michael Bayer (@zzzeek) wrote: Ahhhh, scratch that, I got confused. the patch here is about splitting the AP into two objects, that will work. big job. |
Michael Bayer (@zzzeek) wrote: WIP https://gerrit.sqlalchemy.org/#/c/zzzeek/sqlalchemy/+/681 |
Michael Bayer (@zzzeek) wrote: OK so the above gerrit is now working really well. However, this use case still won't work exactly as written:
This is because by accessing the
Or:
The fix includes that there are any number of Folks who want to use this can use the associationproxy.py code up on the gerrit in their applications right now since this is a standalone extension module. |
Michael Bayer (@zzzeek) wrote: Break association proxy into a descriptor + per-class accessor Reworked :class: Change-Id: I634f88aae6306ac5c5237a0e1acbe07d0481d6b6 → 6446e0d |
Changes by Michael Bayer (@zzzeek):
|
Migrated issue, originally created by Brendan Abel (@babel)
This is using sqlalchemy 0.9.8
Assuming a declarative class called "MyClass" with an association proxy field call "my_field"
Without the owning_class set, most of the other properties will raise exceptions when trying to access them. This can be problematic when using the mapper class to procedurally access fields via the mapper.
Attachments: rework_attribute_proxy_owner.patch | aptest.py
The text was updated successfully, but these errors were encountered: