flask-restless icon indicating copy to clipboard operation
flask-restless copied to clipboard

association proxy error: Class object expected, got 'None'.

Open jcrben opened this issue 6 years ago • 0 comments

NOTE: this isn't a priority for me right now, so I may not be able to answer questions or do further debugging.

Sidenote: according to https://github.com/jfinkels/flask-restless/issues/321#issuecomment-198327761 support for association proxies is necessary for many-to-many support, but why? I'm able to get many-to-many related data as long as I pass the proper includes parameter.

I'm pretty new to this stuff so it's possible I'm misconfiguring something.

I have a reproduction at https://gitlab.com/jcrben-repro/flask-restless-assoc-bug - flask-restless is committed in that repo but the version is roughly demonstrated by the commented out requirements.txt line: -e [email protected]:jfinkels/flask-restless.git@8e32477deba1404a11f08bd9311a6122c6b8a31e#egg=Flask_Restless

Stack trace:

--------------------------------------------------------------------------------
ERROR in base [/Users/bencreasy/code/starter-python/flask-restless/flask_restless/views/base.py:664]:
Class object expected, got 'None'.
--------------------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/bencreasy/code/starter-python/flask-restless/flask_restless/views/base.py", line 450, in wrapped
    return func(*args, **kw)
  File "/Users/bencreasy/code/starter-python/flask-restless/flask_restless/views/resources.py", line 403, in get
    return self._get_resource(resource_id)
  File "/Users/bencreasy/code/starter-python/flask-restless/flask_restless/views/resources.py", line 336, in _get_resource
    return self._get_resource_helper(resource)
  File "/Users/bencreasy/code/starter-python/flask-restless/flask_restless/views/base.py", line 1583, in _get_resource_helper
    included = self.get_all_inclusions(resource)
  File "/Users/bencreasy/code/starter-python/flask-restless/flask_restless/views/base.py", line 1425, in get_all_inclusions
    result = simple_serialize_many(to_include, only=only)
  File "/Users/bencreasy/code/starter-python/flask-restless/flask_restless/serialization/serializers.py", line 534, in serialize_many
    serialized = serializer.serialize(instance, only=_only)
  File "/Users/bencreasy/code/starter-python/flask-restless/flask_restless/serialization/serializers.py", line 493, in serialize
    resource = self._dump(instance, only=only)
  File "/Users/bencreasy/code/starter-python/flask-restless/flask_restless/serialization/serializers.py", line 315, in _dump
    assoc_scalars = list(assoc_proxy_scalar_collections(model))
  File "/Users/bencreasy/code/starter-python/flask-restless/flask_restless/helpers.py", line 87, in assoc_proxy_scalar_collections
    and not isinstance(v.remote_attr.property, RelationshipProperty) \
  File "/Users/bencreasy/dotfiles/local/pyenv/versions/3.6.1/envs/starter-python/lib/python3.6/site-packages/sqlalchemy/ext/associationproxy.py", line 175, in remote_attr
    return getattr(self.target_class, self.value_attr)
  File "/Users/bencreasy/dotfiles/local/pyenv/versions/3.6.1/envs/starter-python/lib/python3.6/site-packages/sqlalchemy/util/langhelpers.py", line 764, in __get__
    obj.__dict__[self.__name__] = result = self.fget(obj)
  File "/Users/bencreasy/dotfiles/local/pyenv/versions/3.6.1/envs/starter-python/lib/python3.6/site-packages/sqlalchemy/ext/associationproxy.py", line 225, in target_class
    return self._get_property().mapper.class_
  File "/Users/bencreasy/dotfiles/local/pyenv/versions/3.6.1/envs/starter-python/lib/python3.6/site-packages/sqlalchemy/ext/associationproxy.py", line 214, in _get_property
    return (orm.class_mapper(self.owning_class).
  File "/Users/bencreasy/dotfiles/local/pyenv/versions/3.6.1/envs/starter-python/lib/python3.6/site-packages/sqlalchemy/orm/base.py", line 425, in class_mapper
    "Class object expected, got '%r'." % (class_, ))
sqlalchemy.exc.ArgumentError: Class object expected, got 'None'.
127.0.0.1 - - [19/Nov/2017 07:34:07] "GET /api/users/1 HTTP/1.1" 400 -

Basically self.owning_class ends up as None which seems to be due to shadowing caused by the memoized_property decorator at https://github.com/zzzeek/sqlalchemy/blob/bb21dea84cd6bc70ab12acb61d1f4511a013f90d/lib/sqlalchemy/ext/associationproxy.py#L90

I also mentioned this in the #sqlalchemy IRC channel as it seems possibly a core bug, but held off since I haven't reproduced it without the dependencies.

jcrben avatar Nov 19 '17 21:11 jcrben