首页 > 解决方案 > 跨多个文件的烧瓶 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

用户.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

组织.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使用此代码制作了文件

管理.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()

标签: pythonflasksqlalchemyflask-sqlalchemy

解决方案


为了将来参考,我通过在我的__init__.py文件中安排导入语句来解决它,以便在关系中可能引用的任何类都应该在之前导入,所以在我的情况下,我不得不将__init__.py文件更改为以下

from .User import User # User class is imported before it's referenced in the Organization model
from .Attachment import Attachment
from .Invoice import Invoice
from .Organization import Organization
from .Setting import Setting
from .Transaction import Transaction

这不是理想的解决方案,我确信有更好的方法,因此欢迎任何其他/更好的方法供将来参考。


推荐阅读