AttributeError: у объекта 'str' нет атрибута '_sa_instance_state' - PullRequest
2 голосов
/ 27 апреля 2019

Я хочу оценить значения, которые связаны с двумя разными пользователями. Я создал другую таблицу для отслеживания предыдущего опыта пользователя и его заинтересованного опыта и связал их с пользователем. У меня есть подходящая функция, которая пытается найти пользователя с наибольшим сходством, но она выдает ошибку атрибута, указанную в заголовке.

модель:

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'))

маршруты:

@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, используемая в маршруте 'match':

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))

РЕДАКТИРОВАТЬ: ошибка:

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'

РЕДАКТИРОВАТЬ2: __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 Ответ

3 голосов
/ 27 апреля 2019

Вот очень маленький пример, который воспроизводит ошибку:

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

Что приводит к:

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 - это атрибут отношения и хороший ресурс, который поможет вам понятькак они работают здесь , в официальных документах.Ключевым выводом является то, что они имеют дело с другими объектами ORM.Поэтому, когда вы запрашиваете отношение, вам нужно предоставить объект ORM.В приведенном выше примере, если вы замените эту строку: s.query(Child).filter_by(parent='a string').first() на эту строку: s.query(Child).filter_by(parent=parent).first(), ошибка исчезнет, ​​поскольку parent является объектом ORM.

В вашем конкретном случае ошибка возникает из-за этогострока: curr = prevExp.query.filter_by(user=user).first().expDescription, где вы передаете переменную user методу filter_by().user определено в строке выше, user = prevExp.query.filter_by(user=current_user).first().expDescription, где вы запрашиваете объект prevExp, и как только он извлекается с помощью first(), вы затем получаете доступ к его атрибуту expDescription.Определение столбца expDescription: expDescription = db.Column(db.String(400)): строка.Таким образом, вы передаете str для запроса атрибута отношения и создаете точную ситуацию, которую демонстрирует упрощенный пример, приведенный выше.

Причина сообщения об ошибке AttributeError: 'str' object has no attribute '_sa_instance_state' состоит в том, что каждый объект ORM имеет _sa_instance_state атрибут.Поскольку атрибуты отношений предназначены исключительно для представления других объектов ORM, sqlalchemy предполагает, что переданная вами строка является объектом ORM, не потрудившись проверить сначала (с чего бы это ?!).Строка не имеет атрибута _sa_instance_state, и, следовательно, AttributeError.

...