首页 > 解决方案 > Flask sqlalchemy,在这种情况下如何避免循环依赖?

问题描述

我已经连续两天在这个问题上头疼了,我仍然可以理解/找到一种方法来做到这一点,它看起来超级简单,但我显然忽略了一些东西(我对 DB 来说也是相当新的 :))。

我想要拥有所有者和宠物模型。宠物将“所有者 ID”作为外键,而所有者将“宠物”作为关系,到目前为止一切都很好。但现在我也希望所有者拥有一个写为“最喜欢的宠物”的“宠物 ID”。在两个模型中都有外键(彼此键)开始产生一堆不同的问题(根据我尝试解决它的方式而有所不同,但要么是循环依赖,要么是一些多路径错误)

我还注意到,如果我避免在 Owner 模型中使用“favourite_pet_id”-外键,只保留 favourite_pet-relationship,那么我不会在 DB 中的任何地方写这个(至少不可见),它只作为“relationship”存在?

这样做的正确方法是什么?提前致谢 !

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)


class Owner(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    pets = db.relationship('Pet', foreign_keys='Pet.owner_id')

    favourite_id = db.Column(db.Integer, db.ForeignKey('pet.id'))
    favourite = db.relationship('Pet', uselist=False, foreign_keys='Owner.favourite_id')

class Pet(db.Model):
    id =db.Column(db.Integer, primary_key=True)
    owner_id = db.Column(db.Integer, db.ForeignKey('owner.id'))
    owner = db.relationship('Owner', uselist=False, back_populates='pets', foreign_keys='Pet.owner_id')


o = Owner()  # one owner
p1 = Pet()   # pet 1
p2 = Pet()   # pet 2

p1.owner=o   # setting owner for pet1
p2.owner=o   # setting owner for pet2
o.favourite=p2  # setting pet2 to be favourite

#db.session.add(o)
#db.session.add(p1)
#db.session.add(p2)
#db.session.commit()

print (p1.owner) # owner
print (p2.owner) # owner
print (p1) # pet 1
print (p2) # pet 2
print (o.pets) # owners pets
print (o.favourite) # favourite pet

标签: databasesqliteflaskormsqlalchemy

解决方案


下面是您的代码的工作版本。关键是对关系(例如连接条件)更加明确,因为模型/表之间存在多种关系。

注意:虽然不是您的问题/请求的一部分,但我还重新格式化了一些 PEP8 一致性和可读性。后者是早期采用的好习惯,因为模型文件通常会快速增长,并且可能变得非常难以阅读、消化和调试。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)


class Owner(db.Model):
    id = db.Column(
        db.Integer,
        primary_key=True,
    )
    favourite_pet_id = db.Column(
        db.Integer,
        db.ForeignKey('pet.id'),
        nullable=True,
    )
    favourite_pet = db.relationship(
        'Pet',
        uselist=False,
        foreign_keys=[favourite_pet_id],
        primaryjoin='Pet.owner_id == Owner.favourite_pet_id',
    )


class Pet(db.Model):
    id = db.Column(
        db.Integer,
        primary_key=True,
    )
    owner_id = db.Column(
        db.Integer,
        db.ForeignKey('owner.id'),
    )
    owner = db.relationship(
        'Owner',
        uselist=False,
        foreign_keys=[owner_id],
        primaryjoin='Pet.owner_id == Owner.id',
        backref=db.backref(
            'pets',
            uselist=True,
        ),
    )


db.create_all()

o = Owner()  # one owner
p1 = Pet()  # pet 1
p2 = Pet()  # pet 2

p1.owner = o  # setting owner for pet1
p2.owner = o  # setting owner for pet2
o.favourite_pet = p2  # setting pet2 to be favourite

print(p1.owner)  # owner
print(p2.owner)  # owner
print(p1)  # pet 1
print(p2)  # pet 2
print(o.pets)  # owners pets
print(o.favourite)  # favourite pet

推荐阅读