Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't set attribute on result objects in SQLAlchemy flask

I encounter a problem with Flask-SQLAlchemy, I can set the attribute of the objects in place_collections, but when I want to set the attribute for objects in places, an error occurred:

Traceback (most recent call last):
  File "/Users/user/PycharmProjects/website/venv/lib/python3.6/site-packages/flask/app.py", line 2309, in __call__
    return self.wsgi_app(environ, start_response)
  File "/Users/user/PycharmProjects/website/venv/lib/python3.6/site-packages/werkzeug/contrib/fixers.py", line 152, in __call__
    return self.app(environ, start_response)
  File "/Users/user/PycharmProjects/website/venv/lib/python3.6/site-packages/flask/app.py", line 2295, in wsgi_app
    response = self.handle_exception(e)
  File "/Users/user/PycharmProjects/website/venv/lib/python3.6/site-packages/flask/app.py", line 1741, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/user/PycharmProjects/website/venv/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise
    raise value
  File "/Users/user/PycharmProjects/website/venv/lib/python3.6/site-packages/flask/app.py", line 2292, in wsgi_app
    response = self.full_dispatch_request()
  File "/Users/user/PycharmProjects/website/venv/lib/python3.6/site-packages/flask/app.py", line 1815, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Users/user/PycharmProjects/website/venv/lib/python3.6/site-packages/flask/app.py", line 1718, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/user/PycharmProjects/website/venv/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise
    raise value
  File "/Users/user/PycharmProjects/website/venv/lib/python3.6/site-packages/flask/app.py", line 1813, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Users/user/PycharmProjects/website/venv/lib/python3.6/site-packages/flask/app.py", line 1799, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/user/Document/Python/Test/Code/app/main/views.py", line 108, in index
    place.distance = round(distance_computing, 1)
AttributeError: can't set attribute

How can I set the attribute for join search object, could anyone help me?

place_collections = Place.query.filter_by(county='BKK')

places = db.session.query(Place.roll_number, Place.name, Place.website, Place.address, Place.distance)
            .outerjoin(Rank, Rank.place_id == Place.place_id)

Both of them are class 'flask_sqlalchemy.BaseQuery' type.


for place in place_collections:
       distance_computing = Place.distance_calculator(float(place.lat), float(place.lng),
                                                            data['lat'], data['lng'])

       place.distance = round(distance_computing, 1)

for place in places:
       distance_computing = Place.distance_calculator(float(school.lat), float(school.lng),
                                                            data['lat'], data['lng'])

       place.distance = round(distance_computing, 1)

Model

class Rank(db.Model):
    __tablename__ = 'rank'
    place_id = db.Column(db.String(50))
    name = db.Column(db.String(255), nullable=False, primary_key=True)
    rank = db.Column(db.Integer)

class Place(db.Model):
    _tablename_ = 'school'
    place_id = db.Column(db.String(50), primary_key=True)
    roll_number = db.Column(db.String(50))
    name = db.Column(db.String(255), nullable=False)
    address = db.Column(db.String(255))
    distance = db.Column(db.String(50))
    rank = db.relationship('Rank',backref=db.backref('roll_number1'), lazy=True, uselist=False)
like image 472
FlyingBurger Avatar asked Jan 03 '23 03:01

FlyingBurger


1 Answers

place_collections = Place.query.filter_by(county='BKK') will return you a collection of Place objects. This is analogous to session.query(Place).filter_by(county='BKK') in plain vanilla SQLAlchemy.

However, from the SQLAlchemy docs:

The Query also accepts ORM-instrumented descriptors as arguments. Any time multiple class entities or column-based entities are expressed as arguments to the query() function, the return result is expressed as tuples

The key point being that when you specify specific columns to query as you have done here:

places = db.session.query(Place.roll_number, Place.name,
    Place.website, Place.address, Place.distance).\
    outerjoin(Rank, Rank.place_id == Place.place_id)

the result is expressed as a collection of tuples.

My first impression was that can't set attribute was a strange error message to receive from trying to assign an attribute value to a tuple, so I tried it:

>>> t = tuple()
>>> t.attribute = 9
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'attribute'

That's not the error message that you received.

So I performed a query similar to your second one (the model is just something from a project that I had open):

>>> q = session.query(Racecard.id, Racecard.meeting)
>>> a_result = q[0]
>>> type(a_result)
<class 'sqlalchemy.util._collections.result'>

Right, so we aren't getting a tuple, we get a sqlalchemy.util._collections.result object. But..

>>> issubclass(type(a_result), tuple)
True

So, the sqlalchemy.util._collections.result is a type of tuple.

If we try to assign a value to an attribute that is not one of the columns that were queried:

>>> a_result.newattribute = 'something'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'result' object has no attribute 'newattribute'

That error message is very similar to the one that we got before when assigning an attribute to a plain tuple.

So why did you get a different error message? The result object is actually more like a namedtuple:

>>> from collections import namedtuple
>>> Racecard = namedtuple('Racecard', 'id, meeting')
>>> rc = Racecard(1, 'Doomben')
>>> rc.meeting = 'Eagle Farm'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

Which is the same error message that you are getting.

So, the sqlalchemy.util._collections.result object supports attribute access of the column names, but as it is a tuple it is still immutable, so you cannot write to those attributes.

To fix your specific error (unless there was some reason that you were specifically querying only those columns) change your places query to:

places = db.session.query(Place).outerjoin(Rank, Rank.place_id == Place.place_id)
like image 151
SuperShoot Avatar answered Jan 05 '23 15:01

SuperShoot