跨多个文件的烧瓶SQLAlchemy关系

时间:2020-07-23 09:53:58

标签: python flask sqlalchemy flask-sqlalchemy

我是Flask Sqlalchemy的新手,我想声明多个模型并将它们彼此关联,我按照文档中的示例进行操作,但是我仍然遇到此错误

sqlalchemy.exc.InvalidRequestError: When initializing mapper mapped class Organization->organizations,
expression 'User' failed to locate a name ('User').
If this is a class name,
consider adding this relationship()
to the <class 'models.Organization.Organization'>
class after both dependent classes have been defined

我不想将所有模型存储在一个文件中,因为项目可能会随着时间的流逝而变得越来越大,所以我创建了以下结构:

- models
--- __init__.py
--- User.py
--- Organization.py
--- ...
- manage.py
- app.py

我希望用户属于某个组织,并且该组织有很多用户,我也希望该组织对自己的自反关系具有一个可选字段,这是我尝试过的。

初始化 .py

from .Attachment import Attachment
from .Invoice import Invoice
from .Organization import Organization
from .Setting import Setting
from .Transaction import Transaction
from .User import User

User.py

from app import db, ma
from marshmallow_enum import EnumField
import enum
import bcrypt


class RuleEnum(enum.Enum):
    admin = 'admin',
    collector = 'collector'
    retailer = 'retailer'
    vendor = 'vendor'
    vendor_admin = 'vendor_admin'


class User(db.Model):
    __tablename__ = 'users'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(), nullable=False)
    mobile = db.Column(db.String(), nullable=False)
    username = db.Column(db.String(), unique=True, nullable=False)
    password = db.Column(db.TEXT(), nullable=False)
    is_active = db.Column(db.Boolean(), default=False)
    rule = db.Column(db.Enum(RuleEnum), nullable=False)
    created_on = db.Column(db.DateTime, server_default=db.func.now())
    updated_on = db.Column(db.DateTime, server_default=db.func.now(), server_onupdate=db.func.now())

    # relations

    #related fields
    organization_id = db.Column(db.Integer, db.ForeignKey('organizations.id'), nullable=True)


    def __init__(
            self,
            name,
            username,
            mobile,
            password,
            rule,
            is_active,
            organization_id = None
    ):
        self.name = name
        self.username = username
        self.mobile = mobile
        self.rule = rule
        self.is_active = is_active
        self.organization_id = organization_id
        self.password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode()

    def __repr__(self):
        return "<id %s>" % self.id


class UserSchema(ma.SQLAlchemyAutoSchema):
    rule = EnumField(RuleEnum, by_value=True)
    class Meta:
        exclude = ['password']
        model = User
        load_instance = True

Organization.py

from app import db, ma
import enum
from marshmallow_enum import EnumField


class TypeEnum(enum.Enum):
    vendor = 'vendor'
    retailer = 'retailer'


class Organization(db.Model):
    __tablename__ = 'organizations'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String())
    cr = db.Column(db.String(), unique=True)
    location = db.Column(db.String())
    is_request_approved = db.Column(db.Boolean(), default=False)
    is_active = db.Column(db.Boolean(), default=False)
    type = db.Column(db.Enum(TypeEnum))
    created_on = db.Column(db.DateTime, server_default=db.func.now())
    updated_on = db.Column(db.DateTime, server_default=db.func.now(), server_onupdate=db.func.now())

    # relations

    # virtual columns
    parent = db.relationship('Organization', remote_side=id, backref='sub_organizations')
    users = db.relationship('User', backref='organization')

    # related fields
    parent_id = db.Column(db.Integer, db.ForeignKey('organizations.id'), nullable=True)

    def __init__(
            self,
            name,
            cr,
            location,
            is_request_approved,
            is_active,
            type,
            parent_id = None
    ):
        self.name = name
        self.cr = cr
        self.location = location
        self.is_request_approved = is_request_approved
        self.is_active = is_active
        self.type = type
        self.parent_id = parent_id

    def __repr__(self):
        return "<id %s>" % self.id


class OrganizationSchema(ma.SQLAlchemyAutoSchema):
    type = EnumField(TypeEnum, by_value=True)
    class Meta:
        model = Organization
        load_instance = True

关于迁移,我关注了一个博客文章,并使用此代码制作了manage.py文件

manage.py

import os

from flask_sqlalchemy import SQLAlchemy
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand

from app import app, db

app.config.from_object(os.environ['APP_SETTINGS'])
migrate = Migrate(app, db)
manager = Manager(app)

manager.add_command('db', MigrateCommand)

if __name__ == '__main__':
    manager.run()

1 个答案:

答案 0 :(得分:0)

为了将来参考,我通过在__init__.py文件中安排import语句解决了该问题,因此,在关系中可能引用的任何类都应先导入,因此在我的情况下,我必须更改{{ 1}}文件到以下

__init__.py

这不是理想的解决方案,我敢肯定有更好的方法,因此欢迎其他任何/更好的方法供以后参考。