python - 如何在具有多个基类的 Python SQLAlchemy 类中正确定义关系?
问题描述
我正在尝试使用 SQLAlchemy 和 Flask 重新创建我的数据库。
我已经创建了所有模型,但现在模型之间的关系存在一些问题。插入或更新对象时 Flask 返回以下错误消息:
File "C:\Users\Lenna\SchoolMi\api-server-v4\venv\lib\site-packages\sqlalchemy\ext\declarative\clsregistry.py", line 326, in __call__
x = eval(self.arg, globals(), self._dict)
File "<string>", line 1, in <module>
# ext/declarative/clsregistry.py
AttributeError: 'Table' object has no attribute 'id'
错误信息引用了profile类中的active_channel关系,表示channel类没有id属性。但是我已经在 ObjectWithDefaultProps 类中定义了这个属性。在 SQL 浏览器中检查 SQL 文件后,id 属性确实存在于 Channel 实体上。
我的第一个假设是外键配置错误,所以我尝试更改外键:
active_channel = db.relationship("Channel", foreign_keys="Channel.id")
代替
active_channel = db.relationship("Channel", foreign_keys="channel.id")
不幸的是,这没有奏效。错误更改为以下内容:
sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on
relationship Profile.active_channel - there are no foreign keys linking these tables. Ensure that referencing
columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression. //
Werkzeug Debugger
我进一步研究了 SQLAlchemy 的多态性方面,并认为它可能与mapperargs 有关,但无法找出实现这一点的正确方法。
在我的代码中,我有以下类:
我的实体派生自多个表示共享属性或关系的对象。
class Profile(db.Model, ObjectWithDefaultProps, ObjectWithAvatar, ObjectWithNotificationProfile):
__tablename__ = "profile"
firebase_uid = db.Column(db.String, unique=True, nullable=False, primary_key=True)
username = db.Column(db.String, unique=True, nullable=False)
firstname = db.Column(db.String, nullable=False)
lastname = db.Column(db.String, nullable=False)
about = db.Column(db.String)
score = db.Column(db.Integer)
email = db.Column(db.String, nullable=False)
active_channel_id = db.Column(db.Integer, db.ForeignKey("channel.id"))
active_channel = db.relationship("Channel", foreign_keys="channel.id")
频道.py
class Channel(db.Model, ObjectBase, ObjectWithAvatar, ObjectWithName, ProfileLinkedObject):
__tablename__ = "channel"
description = db.Column(db.String)
can_add_tags = db.Column(db.Boolean, default=False, nullable=False)
can_public_join = db.Column(db.Boolean, default=False, nullable=False)
from database.provider import db
from datetime import datetime
class ObjectWithDefaultProps:
deleted = db.Column(db.Boolean, nullable=False, default=False)
date_modified = db.Column(db.DateTime, default=datetime.utcnow)
date_added = db.Column(db.DateTime, onupdate=datetime.utcnow)
from database.provider import db
from database.extensions.object_with_color import ObjectWithColor
class ObjectWithAvatar(ObjectWithColor):
image_url = db.Column(db.String)
from database.provider import db
class ObjectWithColor:
color_index = db.Column(db.Integer, default=0)
from database.provider import db
from sqlalchemy.ext.declarative import declared_attr
class ObjectWithNotificationProfile:
auto_follow_questions = db.Column(db.Integer)
auto_follow_answers = db.Column(db.Integer)
auto_follow_comments = db.Column(db.Integer)
auto_follow_questions_on_comment = db.Column(db.Integer)
auto_follow_questions_on_answer = db.Column(db.Integer)
auto_follow_answers_on_comment = db.Column(db.Integer)
send_new_data_notification = db.Column(db.Boolean)
send_new_members_notification = db.Column(db.Boolean)
@declared_attr
def question_event_preferences_id(cls):
return db.Column(db.Integer, db.ForeignKey("custom_event_preferences.id"))
@declared_attr
def question_event_preferences(cls):
return db.relationship("CustomEventPreferences", foreign_keys="custom_event_preferences.id")
@declared_attr
def answer_event_preferences_id(cls):
return db.Column(db.Integer, db.ForeignKey("custom_event_preferences.id"))
@declared_attr
def answer_event_preferences(cls):
return db.relationship("CustomEventPreferences", foreign_keys="custom_event_preferences.id")
@declared_attr
def comment_event_preferences_id(cls):
return db.Column(db.Integer, db.ForeignKey("custom_event_preferences.id"))
@declared_attr
def comment_event_preferences(cls):
return db.relationship("CustomEventPreferences", foreign_keys="custom_event_preferences.id")
@declared_attr
def question_tagging_preferences_id(cls):
return db.Column(db.Integer, db.ForeignKey("custom_tagging_preferences.id"))
@declared_attr
def question_tagging_preferences(cls):
return db.relationship("CustomTaggingPreferences", foreign_keys="custom_tagging_preferences.id")
@declared_attr
def answer_tagging_preferences_id(cls):
return db.Column(db.Integer, db.ForeignKey("custom_tagging_preferences.id"))
@declared_attr
def answer_tagging_preferences(cls):
return db.relationship("CustomTaggingPreferences", foreign_keys="custom_tagging_preferences.id")
@declared_attr
def comment_tagging_preferences_id(cls):
return db.Column(db.Integer, db.ForeignKey("custom_tagging_preferences.id"))
@declared_attr
def comment_tagging_preferences(cls):
return db.relationship("CustomTaggingPreferences", foreign_keys="custom_tagging_preferences.id")
解决方案
对于那些面临与我的问题中描述的相同或相似问题的人:
通过将 foreign_keys 属性中的字符串变量替换为列变量本身,我设法让我的代码正常工作。
经过测试,我发现使用字符串作为 foreign_keys 属性总是会触发我的错误,但是使用 primaryjoin 和 foreign_keys=cls.foreign_key 的替代方法有效。
@declared_attr
def comment_event_preferences(cls):
return db.relationship("CustomEventPreferences", foreign_keys="custom_event_preferences.id")
变成:
@declared_attr
def comment_event_preferences(cls):
return db.relationship("CustomEventPreferences", foreign_keys=cls.comment_event_preferences)
推荐阅读
- nunit - Is there a way to execute multiple category tag via NUnit.ConsoleRunner.3.9.0
- google-sheets - How can I highlight (mark) the active row in google sheets?
- swift - SwiftUI - 如何将 Date() 作为字符串传递给另一个视图?
- php - Laravel / PHP数据过滤多次
- rasa - Deploy rasa application to dokku (docker)
- azure - 使用托管标识 (Asp.net Framework 4.7.2) 保护 Azure 应用服务
- gis - OpenMap - 5m Countour Lines - Paris
- python - 如何从包含在不同文件夹中的docx文件中提取文本
- angular - 什么是存储 API Urls 并在 Angular 中使用的最佳方法
- android - Android File Picker 从已安装的应用程序文件中获取真实的文件路径