1

I am currently using Flask, Flask-SQLAlchemy & Flask-Restless to provide REST routes for a small private AngularJS application. Thus far, I have been able to make/use routes effectively that did not include any relationships.

However, I have a few tables that must have relationships with other tables, which is where my issue is. I get the following Flask error when making a POST to the Flask-Restless Route:

  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1836, in __call__
    return self.wsgi_app(environ, start_response)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1820, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1403, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/usr/local/lib/python2.7/dist-packages/flask_restless/views.py", line 145, in decorator
    return func(*args, **kw)
  File "/usr/local/lib/python2.7/dist-packages/mimerender.py", line 227, in wrapper
    result = target(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/flask/views.py", line 84, in view
    return self.dispatch_request(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/flask/views.py", line 149, in dispatch_request
    return meth(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/flask_restless/views.py", line 1263, in post
    self.session.add(instance)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/scoping.py", line 150, in do
    return getattr(self.registry(), name)(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 1492, in add
    self._save_or_update_state(state)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 1510, in _save_or_update_state
    halt_on=self._contains_state):
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/mapper.py", line 2457, in cascade_iterator
    visited_states, halt_on))
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/relationships.py", line 1449, in cascade_iterator
    get_all_pending(state, dict_)
  File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/dynamic.py", line 185, in get_all_pending
    c.all_items
AttributeError: 'NoneType' object has no attribute '_sa_instance_state'

I think this error has something to do with my model. Here are my current model definitions:

surveyOptions = db.Table('survey_options',
    db.Column('survey_id', db.Integer, db.ForeignKey('survey.id')),
    db.Column('option_id', db.Integer, db.ForeignKey('option.id')),
    db.Column('count', db.Integer)                      
    )

class Survey(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    category = db.Column(db.String(50))
    question = db.Column(db.Text)
    startDate = db.Column(db.DateTime)
    endDate = db.Column(db.DateTime)
    options = db.relationship('Option', secondary=surveyOptions,
        backref=db.backref('surveys', lazy='dynamic'), lazy='dynamic')

def __init__(self, category, question, startDate=None, endDate=None, options=None):
    self.category = category
    self.question = question
    if startDate == None:
        self.startDate = datetime.utcnow()
    if endDate == None:
        self.endDate = datetime.utcnow()
    self.options.append(options)

class Option(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    text = db.Column(db.Text)

    def __init__(self, text):
        self.text = text

...

manager = flask.ext.restless.APIManager(app, flask_sqlalchemy_db=db)
manager.create_api(Survey, methods=['GET','POST','PUT','DELETE'], include_columns=['id','category', 'question', 'startDate','endDate', '_options.id','options.text']);

I would really appreciate some help in isolating the source of this error. I am not sure where to go next with troubleshooting this error.

I can provide more code samples upon request for both the front-end and back-end if needed.

DFenstermacher
  • 564
  • 1
  • 9
  • 23
  • normaly a NoneType error comes when you get a `None` instead of an object. But I can't see which object has that attribute in your code. Also in your error log are only libs in the traceback, so it shouldn't be in your model – muthan Nov 02 '14 at 03:47
  • So it could be a problem with what Angular is sending to the server? – DFenstermacher Nov 02 '14 at 06:17
  • May be this is because you have options=None in function definition and then make self.options.append(options)? – Kostya Shkryob Nov 02 '14 at 08:01

1 Answers1

2

It's because you seem to be appending a list with a list in this section:

def __init__(self, category, question, startDate=None, endDate=None, options=None):
    self.category = category
    self.question = question
    if startDate == None:
        self.startDate = datetime.utcnow()
    if endDate == None:
        self.endDate = datetime.utcnow()
    self.options.append(options)  # assuming options is a list, this will fail.

The reason that's a problem is simple. If I have my list of options:

foo = ['one', 'two']

and I want to add a few more options to it:

bar = ['three', 'four']

if I foo.append(bar) I get:

foo = ['one', 'two', ['three', 'four']]

Whereas you're actually looking for foo + bar which would give you:

foo = ['one', 'two', 'three', 'four']

When you fix that problem, you're still left with a second weirder problem-- if you don't specify options when you create your Survey object, then, as it stands, your code will try to self.options.append(None) to your object. You need to add a check for that:

if choices:  # None or [] will evaluate to False
    self.choices += choices

I also notice you do comparisons to None using the == operator, you should really use is to do those comparisons, e.g. if startDate is None:.

Doobeh
  • 9,280
  • 39
  • 32