2

Issue: inheritance class is not checking for strings because it is being passed as None.

The parent class (object.py) of the retrieve method was designed to retrieve objects from the database (e.g. id or user/assessment combo). The child class (convenience.py) is designed to accept the strs of those objects.

object.py

def retrieve_assessment_result(self, *args):
    id, owner, assessment = None, None, None
    if len(args) == 1:
        id, = args # or id = args[0]
    elif len(args) == 2:
        owner, assessment = args
        print 'testa', owner, assessment, id
    else:
        raise ValueError('Value being passed is an object')
    if id is not None:
        print 'testi', id
        return self.session.query(Assessment_Result).\
        filter(Assessment_Result.id == id).one()
    elif owner is not None:
        print 'testo', owner
        return self.session.query(Assessment_Result).\
        filter(Assessment_Result.owner == owner).one()
    elif assessment is not None:
        print 'testa', assessment
        return self.session.query(Assessment_Result).\
        filter(Assessment_Result.assessment == assessment).one()

convenience.py

def retrieve_assessment_result(self, *args):
    id, owner, assessment = None, None, None
    if len(args) == 1:
        id, = args # or id = args[0]
        print " args = ", args
    elif len(args) == 2:
        username, name = args
        print "api args = ", args
        print 'type args', type(args)
        print 'owner: ', owner
        print 'assessment: ', assessment
    if owner is not None:
        owner = self.retrieve_user(username)
    if assessment is not None:
        assessment = self.retrieve_assessment(name)
    return super(ConvenienceAPI, self).retrieve_assessment_result(*args)

test.py

api.retrieve_assessment_result('baseball', 'Becoming a Leader')

Traceback Error:

Traceback (most recent call last):
  File "/usr/local/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "/Users/ack/code/venv/NotssDB/notssdb/test/test.py", line 133, in test1
    api.retrieve_assessment_result('baseball', 'Becoming a Leader')
  File "/Users/ack/code/venv/NotssDB/notssdb/api/convenience.py", line 42, in retrieve_assessment_result
    return super(ConvenienceAPI, self).retrieve_assessment_result(*args)
  File "/Users/ack/code/venv/NotssDB/notssdb/api/object.py", line 327, in retrieve_assessment_result
    filter(Assessment_Result.owner == owner).one()
  File "/usr/local/lib/python2.7/site-packages/sqlalchemy/sql/operators.py", line 301, in __eq__
    return self.operate(eq, other)
  File "/usr/local/lib/python2.7/site-packages/sqlalchemy/orm/attributes.py", line 175, in operate
    return op(self.comparator, *other, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/sqlalchemy/orm/relationships.py", line 1011, in __eq__
    other, adapt_source=self.adapter))
  File "/usr/local/lib/python2.7/site-packages/sqlalchemy/orm/relationships.py", line 1338, in _optimized_compare
    state = attributes.instance_state(state)
AttributeError: 'str' object has no attribute '_sa_instance_state'
------output--------
<Assessment_Result(owner='<User(username ='bambam', firstname ='Sean', lastname ='Cargo', email='car@gmail.com')>', assessment='<Assessment(name='Foo', text='this is foo')>')>
api args =  ('baseball', 'Becoming a Leader')
type args <type 'tuple'>
owner:  None
assessment:  None
testa baseball Becoming a Leader None
testo baseball

-----updates after comment/suggestions:
object.py Updated:

def retrieve_assessment_result(self, id=None, owner=None, assessment=None):
    print 'test_all_objects', owner, assessment, id
    if id is not None:
        print 'test_id_object', id
        return self.session.query(Assessment_Result).\
        filter(Assessment_Result.id == id).one()
    elif owner is not None:
        print 'test_owner_object', owner
        return self.session.query(Assessment_Result).\
        filter(Assessment_Result.owner.has(User.username == username)).one()
    elif assessment is not None:
        print 'test_assessment_object', assessment
        return self.session.query(Assessment_Result).\
        filter(Assessment_Result.assessment.has(Assessment.name == name)).one()

Convenience.py (inherited from object.py) change:

def retrieve_assessment_result(self, id=None, owner=username, assessment=name):
    if owner is not None:
        owner = self.retrieve_user(username)
    if assessment is not None:
        assessment = self.retrieve_assessment(name)
    return super(ConvenienceAPI, self).retrieve_assessment_result(id=None, owner=owner, assessment=assessment)

Traceback:

  File ".../api/convenience.py", line 6, in <module>
    class ConvenienceAPI(BaseAPI):
  File "../api/convenience.py", line 37, in ConvenienceAPI
    def retrieve_assessment_result(self, id=None, owner=username, assessment=name):
NameError: name 'username' is not defined
thesayhey
  • 938
  • 3
  • 17
  • 38
  • Shouldn't that be return super(ConvenienceAPI, self).retrieve_assessment_result(id=None, **owner=owner**, assessment=name)? – pi. Oct 15 '15 at 08:08
  • @pi. still having issues with this. The original design is buggy (as you have suggested). – thesayhey Nov 18 '15 at 16:46

1 Answers1

3

In my experience, the error '???' object has no attribute '_sa_instance_state almost always indicates that you assigned something which is not a SQLAlchemy object (i.e. some basic type, as int, str, etc.) to a relationship or dynamic_loader.

relationship example

>>> result = db.query(Assessment_Result).first()
>>> # owner only accepts Owner objects
>>> result.owner = "Mr. Harvey Example"
Traceback (most recent call last):
...
AttributeError: 'str' object has no attribute '_sa_instance_state'

Furthermore, filtering with other types on a relationship will also lead to this error:

filtering example

>>> Assessment_Result.owner == "baseball"
Traceback (most recent call last):
...
AttributeError: 'str' object has no attribute '_sa_instance_state'

In your case the error is caused by having the string "baseball" as the first entry in the 2-tuple args which you pass on to Object.retrieve_assessment_result, which in turn will be put in the variable owner in the second if block.

>>>         owner, assessment = args

As owner now contains a str this will fail when you filter with it.

I'd like to take this opportunity to point out that this code is not very readable and could be improved by the use of keyword arguments and generally making it more concise.

Community
  • 1
  • 1
pi.
  • 21,112
  • 8
  • 38
  • 59
  • Yes, that is what I indicated above and shown in the printout. Convenience.py is supposed to allow strings but if you look at the trace back printouts, `Owner` is printing `None` ... I think it's from convenience.py ... For some reason The inheritance is not working and causing this issue. – thesayhey Oct 14 '15 at 12:17
  • you are suggesting that I use something like: `retrieve_assessment_result(id=none, owner=none, assessment=none)` ... but wouldn't this cause the same issue? Also, your answer is very eloquently put. – thesayhey Oct 14 '15 at 13:44
  • Only if you pass a string to `owner`, but this error would then be much more obvious as there would be no tuple-unpacking code which obscures the issue. Also, to make it even more obvious, you could add `assert` statements directly at the beginning of the method, checking for the correct types. – pi. Oct 14 '15 at 14:12
  • Gotcha, I can make those changes...the point of convenience.py is to pass strings since the retrieve method takes `int` for `id` and `str` for `owner` or `assessment`. Is the design of convenience.py poor? – thesayhey Oct 14 '15 at 14:15
  • Try to get by with as little code as possible. Less code means less stuff which can go wrong. Combined with `separation of concerns`, this improves code quite a bit. Just try to improve it, then everything will be fine, eventually. :) – pi. Oct 14 '15 at 14:24
  • Made some changes (see above)... I definitely need to keep the `len` check to pass either `id` or `owner/assessment` strings... – thesayhey Oct 14 '15 at 14:44