问题
I want to evaluate the values that are linked to two different users. I created a different table to keep track of a user's previous experiences and their interested experiences and linked them to the user. I have a matching function that tries to find the user with the most similarities, but it gives the attribute error listed in the title.
model:
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
prev = db.relationship('prevExp', backref='user', lazy='dynamic')
interested = db.relationship('interestedExp', backref='user',
lazy='dynamic')
matches = db.relationship('Match', backref='matchedTo', lazy='dynamic')
class prevExp(db.Model):
id = db.Column(db.Integer, primary_key=True)
arts = db.Column(db.String(1))
consulting = db.Column(db.String(1))
dataScience = db.Column(db.String(1))
education = db.Column(db.String(1))
engineering = db.Column(db.String(1))
finance = db.Column(db.String(1))
government = db.Column(db.String(1))
law = db.Column(db.String(1))
management = db.Column(db.String(1))
marketing = db.Column(db.String(1))
medical = db.Column(db.String(1))
technology = db.Column(db.String(1))
expDescription = db.Column(db.String(400))
userID = db.Column(db.Integer, db.ForeignKey('user.id'))
class interestedExp(db.Model):
id = db.Column(db.Integer, primary_key=True)
arts = db.Column(db.String(1))
consulting = db.Column(db.String(1))
dataScience = db.Column(db.String(1))
education = db.Column(db.String(1))
engineering = db.Column(db.String(1))
finance = db.Column(db.String(1))
government = db.Column(db.String(1))
law = db.Column(db.String(1))
management = db.Column(db.String(1))
marketing = db.Column(db.String(1))
medical = db.Column(db.String(1))
technology = db.Column(db.String(1))
userID = db.Column(db.Integer, db.ForeignKey('user.id'))
class Match(db.Model):
id = db.Column(db.Integer, primary_key=True)
person = db.Column(db.String(120))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
userID = db.Column(db.Integer, db.ForeignKey('user.id'))
routes:
@app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
user = User(firstName=form.firstName.data, lastName=form.lastName.data,
username=form.username.data, email=form.email.data)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
flash('Congratulations, you are now a registered user!')
return redirect(url_for('login'))
return render_template('register.html', form=form)
@app.route('/profile/edit/', methods=['GET', 'POST'])
@login_required
def editProfile():
form = ProfileForm(obj=current_user)
if form.validate_on_submit():
previous = prevExp(user=current_user)
previous.arts = form.prevArts.data
previous.consulting = form.prevConsulting.data
previous.dataScience = form.prevDataScience.data
previous.education = form.prevEducation.data
previous.engineering = form.prevEngineering.data
previous.finance = form.prevFinance.data
previous.government = form.prevGovernment.data
previous.law = form.prevLaw.data
previous.management = form.prevManagement.data
previous.management = form.prevMarketing.data
previous.medical = form.prevMedical.data
previous.technology = form.prevTechnology.data
previous.expDescription = form.expDescription.data
interested = interestedExp(user=current_user)
interested.arts = form.arts.data
interested.consulting = form.consulting.data
interested.dataScience = form.dataScience.data
interested.education = form.education.data
interested.engineering = form.engineering.data
interested.finance = form.finance.data
interested.government = form.government.data
interested.law = form.law.data
interested.management = form.management.data
interested.marketing = form.marketing.data
interested.medical = form.medical.data
interested.technology = form.technology.data
db.session.commit()
flash('Your information has been saved!')
return redirect(url_for('profile', username=current_user.username))
return render_template('editProfile.html', form=form)
@app.route('/matches')
@login_required
def matches():
match = bestMatch()
if match == None:
message = "Sorry, there are no matches at this time. \
Please check back later!"
else:
message = "We've found a match for you! Check out "
return render_template('matches.html', message=message,
match=User.query.filter_by(username=match).first())
bestMatch function used in 'match' route:
def bestMatch():
curr = []
curr.append(current_user)
users = set(User.query.all()) - set(curr)
existingMatches = set(Match.query.filter_by(matchedTo=current_user))
users -= existingMatches
matchScore = dict()
for user in users:
username, score = countSimilarities(user)
matchScore[username] = score
scores = matchScore.values()
bestScore = max(scores, default=0) if max(scores, default=0) > 0 else -1
return matchScore.get(bestScore, None)
def countSimilarities(user):
score = 0
if user.major == current_user.major:
score += 1
score += countCollege(user.college, current_user.college)
score += countHobbies(user.hobbies, current_user.hobbies)
score += countPrevToInterested(user)
score += countExpDescription(user)
return user.username, score
def countExpDescription(user):
user = prevExp.query.filter_by(user=current_user).first().expDescription
curr = prevExp.query.filter_by(user=user).first().expDescription
user = set(user.split(' '))
curr = set(curr.split(' '))
return len(user.intersection(curr))
EDIT: the error:
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/flask/app.py", line 2309, in __call__
return self.wsgi_app(environ, start_response)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/flask/app.py", line 2295, in wsgi_app
response = self.handle_exception(e)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/flask/app.py", line 1741, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise
raise value
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/flask/app.py", line 2292, in wsgi_app
response = self.full_dispatch_request()
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/flask/app.py", line 1815, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/flask/app.py", line 1718, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise
raise value
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/flask/app.py", line 1813, in full_dispatch_request
rv = self.dispatch_request()
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/flask/app.py", line 1799, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/flask_login/utils.py", line 261, in decorated_view
return func(*args, **kwargs)
File "/Users/gracekim/Desktop/projects/coffeechat/app/routes.py", line 96, in matches
match = bestMatch()
File "/Users/gracekim/Desktop/projects/coffeechat/app/matching.py", line 13, in bestMatch
username, score = countSimilarities(user)
File "/Users/gracekim/Desktop/projects/coffeechat/app/matching.py", line 26, in countSimilarities
score += countExpDescription(user)
File "/Users/gracekim/Desktop/projects/coffeechat/app/matching.py", line 52, in countExpDescription
curr = prevExp.query.filter_by(user=user).first().expDescription
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 1792, in filter_by
for key, value in kwargs.items()
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 1792, in <listcomp>
for key, value in kwargs.items()
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/sql/operators.py", line 365, in __eq__
return self.operate(eq, other)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/orm/attributes.py", line 211, in operate
return op(self.comparator, *other, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/orm/relationships.py", line 1093, in __eq__
other, adapt_source=self.adapter
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/sqlalchemy/orm/relationships.py", line 1466, in _optimized_compare
state = attributes.instance_state(state)
AttributeError: 'str' object has no attribute '_sa_instance_state'
EDIT2: __init__.py
from flask import Flask
from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
login = LoginManager(app)
login.login_view = 'login'
from app import routes, models
回答1:
Here's a really small example that reproduces the error:
class Parent(Base):
id = sa.Column(sa.Integer, primary_key=True)
child = relationship('Child', backref='parent')
class Child(Base):
id = sa.Column(sa.Integer, primary_key=True)
parent_id = sa.Column(sa.Integer, sa.ForeignKey('parent.id'))
if __name__ == '__main__':
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
parent = Parent()
child = Child(parent=parent)
s = Session()
s.add(parent)
s.commit()
s.query(Child).filter_by(parent='a string').first() # this line causes error
Which raises:
Traceback (most recent call last):
File "55876558.py", line 27, in <module>
s.query(Child).filter_by(parent='a string').first()
File "C:\Users\peter_000\.virtualenvs\test-_0Fb_hDQ\lib\site-packages\sqlalchemy\orm\query.py", line 1689, in filter_by
for key, value in kwargs.items()]
File "C:\Users\peter_000\.virtualenvs\test-_0Fb_hDQ\lib\site-packages\sqlalchemy\orm\query.py", line 1689, in <listcomp>
for key, value in kwargs.items()]
File "C:\Users\peter_000\.virtualenvs\test-_0Fb_hDQ\lib\site-packages\sqlalchemy\sql\operators.py", line 344, in __eq__
return self.operate(eq, other)
File "C:\Users\peter_000\.virtualenvs\test-_0Fb_hDQ\lib\site-packages\sqlalchemy\orm\attributes.py", line 180, in operate
return op(self.comparator, *other, **kwargs)
File "C:\Users\peter_000\.virtualenvs\test-_0Fb_hDQ\lib\site-packages\sqlalchemy\orm\relationships.py", line 1039, in __eq__
other, adapt_source=self.adapter))
File "C:\Users\peter_000\.virtualenvs\test-_0Fb_hDQ\lib\site-packages\sqlalchemy\orm\relationships.py", line 1372, in _optimized_compare
state = attributes.instance_state(state)
AttributeError: 'str' object has no attribute '_sa_instance_state'
Child.parent
is a relationship attribute, and a good resource to help you understand how they work is here, in the official docs. The key takeaway is that they deal with other ORM objects. So, when you query on a relationship, you need to provide an ORM object. In the above example if you replace this line: s.query(Child).filter_by(parent='a string').first()
with this line: s.query(Child).filter_by(parent=parent).first()
, the error goes away because parent
is an ORM object.
In your specific case, the error comes from this line: curr = prevExp.query.filter_by(user=user).first().expDescription
, where you pass a variable user
to the filter_by()
method. user
is defined on the line above that, user = prevExp.query.filter_by(user=current_user).first().expDescription
where you query a prevExp
object and once it's retrieved using first()
, you then access it's expDescription
attribute. The column definition of expDescription
is expDescription = db.Column(db.String(400))
: a string. So you are passing a str
to query the relationship attribute and creating the exact situation that the simplified example above demonstrates.
The reason for the error message AttributeError: 'str' object has no attribute '_sa_instance_state'
, is that every ORM object has an _sa_instance_state
attribute. As relationship attributes are purely for representing other ORM objects, sqlalchemy assumes that the string you have passed in is an ORM object without bothering to check first (why would it?!). A string doesn't have a _sa_instance_state
attribute, and hence the AttributeError
.
来源:https://stackoverflow.com/questions/55876558/attributeerror-str-object-has-no-attribute-sa-instance-state