无法从部分初始化的模块“模型”中导入名称“db”(很可能是由于循环导入)

时间:2021-02-17 23:49:11

标签: python flask flask-sqlalchemy python-unittest

我可以使用以下命令 python -m unittest discover -p testing.py 运行我的烧瓶测试,但是当我尝试运行 python app.py runserver 时,它向我显示以下错误消息:

Traceback (most recent call last):
  File "app.py", line 10, in <module>
    from models import db
  File "/home/paula/projects/envioclicktest/restaurant_flask/models.py", line 1, in <module>
    from app import app as app
  File "/home/paula/projects/envioclicktest/restaurant_flask/app.py", line 10, in <module>
    from models import db
ImportError: cannot import name 'db' from partially initialized module 'models'
(most likely due to a circular import) (/home/paula/projects/envioclicktest/restaurant_flask/models.py)

我的项目结构如下:

-restaurant_flask
    |-app.py
    |-models.py
    |-testing.py

这是我的文件的内容以及我认为在每个文件中造成问题的行

App.py
from flask import Flask
from flask_script import Manager, Shell
from flask_migrate import Migrate, MigrateCommand


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

from models import db
migrate = Migrate(app, db)

manager = Manager(app)
manager.add_command('db', MigrateCommand)


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

models.py
from app import app as app <-------Trouble line Originally "from __main__ import app" but that doesnt work with testing.py
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.sql.functions import func
from sqlalchemy.exc import IntegrityError
import datetime
import pytz
import random
mx = pytz.timezone('America/Mexico_City')

db = SQLAlchemy(app)

class Waiter(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128))
    services = db.relationship("Service", back_populates="waiter")

class Table(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    table_no = db.Column(db.String(128))
    services = db.relationship("Service", back_populates="table")
    
class Service(db.Model):

    def __init__(self, tip):
        time = datetime.datetime.now(mx)
        self.tip = tip
        # check for waiters
        waiters = db.session.query(Waiter, func.count(Service.id).\
            filter(Service.exit>time)).\
                outerjoin(Service).group_by(Waiter).all()
        print(waiters)
        available_waiters= [i for i in waiters if i[1] < 4]
        available_waiters_length = len(available_waiters)
        # Check for tables
        tables = db.session.query(Table, func.count(Table.id).\
            filter(Service.exit>time)).\
                outerjoin(Service).group_by(Table).all()
        print(tables)
        available_tables= [i for i in tables if i[1] < 1]
        available_tables_length = len(available_tables)
        #check if available 
        if available_tables_length == 0 and available_waiters_length == 0:
            raise Exception("not enough waiters or tables")
        if available_waiters_length == 0:
            raise Exception("not enough waiters")
        if available_tables_length == 0:
            raise Exception("not enough tables")
        # add waiter and table
        waiter_obj = random.choice(available_waiters)
        self.waiter_id = waiter_obj[0].id
        table_obj = random.choice(available_tables)
        self.table_id = table_obj[0].id

        
        # check if current time is open
        if time.time() < datetime.time(9,0) or time.time()> datetime.time(23, 30):
            raise Exception("The restaurant is closed")
        # add timedelta to init_time
        if time.time() < datetime.time(17,0):
            self.exit = time + datetime.timedelta(minutes=90)
        if time.time() > datetime.time(17,0):
            self.exit = time + datetime.timedelta(minutes=120)

    id = db.Column(db.Integer, primary_key=True)
    table_id = db.Column(db.Integer, db.ForeignKey('table.id'))
    table = db.relationship("Table", back_populates="services")
    waiter_id = db.Column(db.Integer, db.ForeignKey('waiter.id'))
    waiter = db.relationship("Waiter", back_populates="services")
    arrival = db.Column(db.DateTime, default=datetime.datetime.now(mx))
    exit = db.Column(db.DateTime)
    tip = db.Column(db.Numeric(10,2))

testing.py
from app import app as app <------- This line does not work as "from __main__ import app"
import unittest
from models import db, Waiter, Table, Service
from freezegun import freeze_time
import datetime
import pytz


class UserTest(unittest.TestCase):

    def setUp(self):
        app.config['SQLALCHEMY_DATABASE_URI']  = 'sqlite:///test.db'
        app.config['TESTING'] = True
        app.config['WTF_CSRF_ENABLED'] = False
        self.app = app.test_client()
        db.create_all()

    def tearDown(self):
        db.session.remove()
        db.drop_all()

    dt = datetime.datetime(2018,1,1,11,0,0,tzinfo=pytz.timezone('America/Mexico_City'))
    @freeze_time(dt)
    def test_models(self):
        #Create a Table and a Waiter
        table = Table(table_no="table1")
        db.session.add(table)
        waiter = Waiter(name="Juan")
        db.session.add(waiter)
        db.session.commit()
        service = Service(tip=90.80)
        db.session.add(service)
        db.session.commit()
        
        query =  Service.query.all()
        #Check that all users exist
        assert len(query) == 1
        assert query[0].table_id == 1
        assert query[0].waiter_id == 1
        assert ((query[0].exit-query[0].arrival).seconds/60) >= 90

我无法弄清楚,这让我发疯。

非常感谢任何帮助:)

2 个答案:

答案 0 :(得分:1)

我找到了解决方法。在我的 models.py 中,我替换了这一行:

from app import app as app

为此:

try:
    from app import app as app
except ImportError:
    from __main__ import app

它有效,但它很丑陋。我想一定有更漂亮的方法来做到这一点。

答案 1 :(得分:0)

对于解决方案,简单地做到这一点:

在模型上


db = SQLAlchemy() #Remove the app here 

在应用上


from models import db
db.init_app(app) #Add this line Before migrate line
migrate = Migrate(app, db)