Как правильно определить отношения в классе Python SQLAlchemy с несколькими базовыми классами? - PullRequest
0 голосов
/ 13 апреля 2020

Я пытаюсь воссоздать свою базу данных, используя 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'

Сообщение об ошибке ссылается на отношение active_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") 

Channel.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")

1 Ответ

0 голосов
/ 18 апреля 2020

Для тех, кто сталкивается с такими же или похожими проблемами, как описано в моем вопросе:

Мне удалось заставить мой код работать, заменив строковые переменные в атрибуте 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...