vertical-knowledge / ripozo-sqlalchemy

A python package for integrating sqlalchemy with ripozo
http://ripozo-sqlalchemy.readthedocs.org/
GNU General Public License v2.0
7 stars 6 forks source link

AttributeError: type object '<model>' has no attribute '' #8

Open hroncok opened 8 years ago

hroncok commented 8 years ago

Given the following example (search for HERE IS THE PROBLEM):

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from flask_ripozo import FlaskDispatcher
from ripozo import restmixins, ListRelationship, Relationship, adapters, apimethod
from ripozo_sqlalchemy import AlchemyManager, SessionHandler
from sqlalchemy.engine.url import URL
from sqlalchemy.orm import relationship

app = Flask(__name__)
url = URL('mysql', query={'read_default_file': './mysql.cnf'})
app.config['SQLALCHEMY_DATABASE_URI'] = url
db = SQLAlchemy(app)

session_handler = SessionHandler(db.session)

class Sport(db.Model):
    __tablename__ = 'v_sports'

    id_sport = db.Column(db.Integer, primary_key=True)
    description = db.Column(db.String)

class Course(db.Model):
    __tablename__ = 'v_subjects'

    id_course = db.Column('id_subjects', db.Integer, primary_key=True)
    _sport = db.Column('sport', db.Integer, db.ForeignKey('v_sports.id_sport'))
    sport = relationship(Sport, foreign_keys=(_sport,))     # <--- HERE IS THE PROBLEM

class SportManager(AlchemyManager):
    fields = ('id_sport', 'description',)
    model = Sport

class CourseManager(AlchemyManager):
    fields = ('id_course', 'sport',)
    model = Course

class SportResource(restmixins.RetrieveRetrieveList):
    manager = SportManager(session_handler)
    resource_name = 'sports'
    pks = ('id_sport',)

class CourseResource(restmixins.RetrieveRetrieveList):
    manager = CourseManager(session_handler)
    resource_name = 'courses'
    pks = ('id_course',)

dispatcher = FlaskDispatcher(app, url_prefix='/')
dispatcher.register_resources(SportResource, CourseResource)
dispatcher.register_adapters(adapters.BasicJSONAdapter)

if __name__ == '__main__':
    app.run(debug=True)

If I GET /courses/1/, I get a nasty exception:


type object 'Sport' has no attribute ''
Traceback (most recent call last):
  File "venv/lib64/python3.4/site-packages/ripozo_sqlalchemy/alchemymanager.py", line 102, in _get_field_python_type
    return getattr(model, name).property.columns[0].type.python_type
  File "venv/lib64/python3.4/site-packages/sqlalchemy/util/langhelpers.py", line 840, in __getattr__
    return self._fallback_getattr(key)
  File "venv/lib64/python3.4/site-packages/sqlalchemy/util/langhelpers.py", line 818, in _fallback_getattr
    raise AttributeError(key)
AttributeError: columns

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "venv/lib64/python3.4/site-packages/ripozo_sqlalchemy/alchemymanager.py", line 102, in _get_field_python_type
    return getattr(model, name).property.columns[0].type.python_type
AttributeError: type object 'Sport' has no attribute ''

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "venv/lib64/python3.4/site-packages/flask_ripozo/dispatcher.py", line 213, in flask_dispatch
    adapter = dispatcher.dispatch(f, accepted_mimetypes, ripozo_request)
  File "venv/lib64/python3.4/site-packages/ripozo/dispatch_base.py", line 212, in dispatch
    result = endpoint_func(request, *args, **kwargs)
  File "venv/lib64/python3.4/site-packages/ripozo/decorators.py", line 103, in newfunc
    return self.func(klass, *args)
  File "venv/lib64/python3.4/site-packages/ripozo/decorators.py", line 197, in wrapped
    resource = func(cls, request, *args, **kwargs)
  File "venv/lib64/python3.4/site-packages/ripozo/decorators.py", line 111, in __call__
    return self.__get__(None, klass=cls)(*args, **kwargs)
  File "venv/lib64/python3.4/site-packages/ripozo/decorators.py", line 103, in newfunc
    return self.func(klass, *args)
  File "venv/lib64/python3.4/site-packages/ripozo/decorators.py", line 367, in action
    translate_fields(request, self.fields(cls.manager),
  File "venv/lib64/python3.4/site-packages/ripozo/decorators.py", line 382, in fields
    for field in manager.field_validators:
  File "venv/lib64/python3.4/site-packages/ripozo/decorators.py", line 33, in __get__
    return self.fget.__get__(obj, klass)()
  File "venv/lib64/python3.4/site-packages/ripozo/manager_base.py", line 208, in field_validators
    cls._field_validators[field_name] = cls.get_field_type(field_name)
  File "venv/lib64/python3.4/site-packages/ripozo_sqlalchemy/alchemymanager.py", line 123, in get_field_type
    python_type = cls._get_field_python_type(cls.model, name)
  File "venv/lib64/python3.4/site-packages/ripozo_sqlalchemy/alchemymanager.py", line 106, in _get_field_python_type
    return AlchemyManager._get_field_python_type(model, '.'.join(parts))
  File "venv/lib64/python3.4/site-packages/ripozo_sqlalchemy/alchemymanager.py", line 105, in _get_field_python_type
    model = getattr(model, parts.pop(0)).comparator.mapper.class_
AttributeError: type object 'Sport' has no attribute ''
127.0.0.1 - - [25/Apr/2016 20:24:18] "GET /courses/117/ HTTP/1.1" 500 -
Traceback (most recent call last):
  File "venv/lib/python3.4/site-packages/flask/app.py", line 1836, in __call__
    return self.wsgi_app(environ, start_response)
  File "venv/lib/python3.4/site-packages/flask/app.py", line 1820, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "venv/lib/python3.4/site-packages/flask/app.py", line 1403, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "venv/lib/python3.4/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "venv/lib/python3.4/site-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "venv/lib/python3.4/site-packages/flask/app.py", line 1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "venv/lib/python3.4/site-packages/flask/app.py", line 1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "venv/lib/python3.4/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "venv/lib/python3.4/site-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "venv/lib/python3.4/site-packages/flask/app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "venv/lib/python3.4/site-packages/flask_ripozo/dispatcher.py", line 216, in flask_dispatch
    return dispatcher.error_handler(dispatcher, accepted_mimetypes, e)
  File "venv/lib/python3.4/site-packages/flask_ripozo/dispatcher.py", line 51, in exception_handler
    raise exc
  File "venv/lib/python3.4/site-packages/flask_ripozo/dispatcher.py", line 213, in flask_dispatch
    adapter = dispatcher.dispatch(f, accepted_mimetypes, ripozo_request)
  File "venv/lib/python3.4/site-packages/ripozo/dispatch_base.py", line 212, in dispatch
    result = endpoint_func(request, *args, **kwargs)
  File "venv/lib/python3.4/site-packages/ripozo/decorators.py", line 103, in newfunc
    return self.func(klass, *args)
  File "venv/lib/python3.4/site-packages/ripozo/decorators.py", line 197, in wrapped
    resource = func(cls, request, *args, **kwargs)
  File "venv/lib/python3.4/site-packages/ripozo/decorators.py", line 111, in __call__
    return self.__get__(None, klass=cls)(*args, **kwargs)
  File "venv/lib/python3.4/site-packages/ripozo/decorators.py", line 103, in newfunc
    return self.func(klass, *args)
  File "venv/lib/python3.4/site-packages/ripozo/decorators.py", line 367, in action
    translate_fields(request, self.fields(cls.manager),
  File "venv/lib/python3.4/site-packages/ripozo/decorators.py", line 382, in fields
    for field in manager.field_validators:
  File "venv/lib/python3.4/site-packages/ripozo/decorators.py", line 33, in __get__
    return self.fget.__get__(obj, klass)()
  File "venv/lib/python3.4/site-packages/ripozo/manager_base.py", line 208, in field_validators
    cls._field_validators[field_name] = cls.get_field_type(field_name)
  File "venv/lib/python3.4/site-packages/ripozo_sqlalchemy/alchemymanager.py", line 123, in get_field_type
    python_type = cls._get_field_python_type(cls.model, name)
  File "venv/lib/python3.4/site-packages/ripozo_sqlalchemy/alchemymanager.py", line 106, in _get_field_python_type
    return AlchemyManager._get_field_python_type(model, '.'.join(parts))
  File "venv/lib/python3.4/site-packages/ripozo_sqlalchemy/alchemymanager.py", line 105, in _get_field_python_type
    model = getattr(model, parts.pop(0)).comparator.mapper.class_
AttributeError: type object 'Sport' has no attribute ''

The problem is here: https://github.com/vertical-knowledge/ripozo-sqlalchemy/blob/master/ripozo_sqlalchemy/alchemymanager.py#L106

The joined part is empty string.

But even when I remove that and return just object, as it was previously before 4445a19bb644daa91b1004a8c6555c901e82b301 I still get weird errors.

Traceback (most recent call last):
  File "venv/lib/python3.4/site-packages/flask/app.py", line 1836, in __call__
    return self.wsgi_app(environ, start_response)
  File "venv/lib/python3.4/site-packages/flask/app.py", line 1820, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "venv/lib/python3.4/site-packages/flask/app.py", line 1403, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "venv/lib/python3.4/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "venv/lib/python3.4/site-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "venv/lib/python3.4/site-packages/flask/app.py", line 1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "venv/lib/python3.4/site-packages/flask/app.py", line 1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "venv/lib/python3.4/site-packages/flask/_compat.py", line 33, in reraise
    raise value
  File "venv/lib/python3.4/site-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "venv/lib/python3.4/site-packages/flask/app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "venv/lib/python3.4/site-packages/flask_ripozo/dispatcher.py", line 218, in flask_dispatch
    return Response(response=adapter.formatted_body, headers=adapter.extra_headers,
  File "venv/lib/python3.4/site-packages/ripozo/adapters/basic_json.py", line 59, in formatted_body
    return json.dumps({self.resource.resource_name: response})
  File "/usr/lib64/python3.4/json/__init__.py", line 230, in dumps
    return _default_encoder.encode(obj)
  File "/usr/lib64/python3.4/json/encoder.py", line 192, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib64/python3.4/json/encoder.py", line 250, in iterencode
    return _iterencode(o, 0)
  File "/usr/lib64/python3.4/json/encoder.py", line 173, in default
    raise TypeError(repr(o) + " is not JSON serializable")
TypeError: <__main__.Sport object at 0x7f0079dc62e8> is not JSON serializable

What am I doing wrong? I've spend couple of hours trying and I'm clueless.

Thanks for any help.

hroncok commented 8 years ago

Oh, it seems the problem is here:

class CourseManager(AlchemyManager):
    fields = ('id_course', 'sport',)  # <-- sport should not be here!
    model = Course

My bad :(

Still, perhaps issue a different kind of exception?

timmartin19 commented 8 years ago

You can include 'sport.id' in which case it'll return something like {'sport': {'id': 1}} You're right that it should be a more explicit exception though.