Вот небольшая часть моего кода (я включил не все таблицы, а только те, которые вызывают ошибку).У меня есть отношение «многие ко многим» между таблицами Article и Keyword, материализованными в таблице ArticleKeyword, которая имеет дополнительный атрибут «textrank_score».Я использую association_proxy
с пользовательским создателем в Article и обратными ссылками в ArticleKeyword, как объяснено здесь .На мой взгляд, я добавляю элементы в пример, используя стиль словаря: article.keywords[keyword]=score
, где article
и keyword
- это экземпляры Article
и Keyword
соответственно.Это дает мне ошибку атрибута, потому что sqlalchemy считает score
объектом ORM (та же ошибка, что и здесь ).Однако когда я делаю то же самое в оболочке Python, это удается.
# models.py
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm.collections import attribute_mapped_collection
from .api import db
class Keyword(db.Model):
id = db.Column(db.Integer, primary_key=True)
value = db.Column(db.String(80), unique=True)
def __init__(self, value):
self.value = value
class Article(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200))
url = db.Column(db.String(200))
author = db.Column(db.String(200))
description = db.Column(db.Text())
image_url = db.Column(db.String(200))
publication_date = db.Column(db.DateTime)
source_id = db.Column(db.ForeignKey('source.id'))
category_name = db.Column(db.String(80))
source = db.relationship("Source", backref="article_source")
keywords = association_proxy(
'article_keywords', 'keyword',
creator=lambda k, v: ArticleKeyword(keyword=k, textrank_score=v)
)
__table_args__ = (
db.ForeignKeyConstraint([source_id, category_name], [Category.source_id, Category.name]),
{}
)
def __init__(self, title=None, url=None, author=None,
description=None, image_url=None, publication_date=None,
source=None, category_name=None):
self.title = title
self.url = url
self.author = author
self.description = description
self.image_url = image_url
self.publication_date = publication_date
self.source = source
self.category_name = category_name
class ArticleKeyword(db.Model):
article_id = db.Column('article_id', db.Integer, db.ForeignKey('article.id'), primary_key=True)
keyword_id = db.Column('keyword_id', db.Integer, db.ForeignKey('keyword.id'), primary_key=True)
textrank_score = db.Column('textrank_score', db.Float)
keyword = db.relationship(Keyword)
article = db.relationship(
Article,
backref=db.backref('article_keywords',
cascade='all, delete-orphan',
collection_class=attribute_mapped_collection("keyword"))
)
def __init__(self, article=None, keyword=None, textrank_score=None):
self.article = article
self.keyword = keyword
self.textrank_score = textrank_score
# views.py
class DataFetcher(Resource):
def post(self):
json_data = request.get_json(force=True)
sources = self.sources_schema.load(json_data["sources"])
for keywords, article_dict in fetch_articles(sources):
# keywords = [{"keyword": "Hello", "score": 0.5}, ...]
# article_dict = {"title": "Hello World", ...}
article = Article(**article_dict)
db.session.add(article)
for kw_dict in keywords:
kw_val, score = kw_dict['keyword'], kw_dict['score']
keyword = Keyword(value=kw_val)
db.session.add(keyword)
article.keywords[keyword] = score
# Previous line throws an AttributeError:
# 'float' object has no attribute '_sa_instance_state'
db.session.commit()
return jsonify({"message": "Success"})
Но работает следующее:
$ flask shell
Python 3.6.9 (default, Aug 14 2019, 13:09:32)
[GCC 6.3.0 20170516] on linux
App: api.api [production]
Instance: /instance
>>> from api.models import *
>>> kwd = Keyword("asterix")
>>> article = Article(title="asterix le gaulois")
>>> article.keywords[kwd] = 0.8
>>> db.session.add(article)
>>> db.session.commit()
>>> db.session.query(ArticleKeyword).all()
[<ArticleKeyword 1137, 5344>]
>>> kwda, = db.session.query(ArticleKeyword).all()
>>> kwda.textrank_score
0.8
Есть идеи, что не так?