λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
🐍 Python

[Python/ERROR] Circular import (Runtime과 Import Time의 차이점)

by sgaeng 2023. 4. 24.

μ—λŸ¬λ©”μ‹œμ§€

ImportError: cannot import name 'db' from partially initialized module 'app' (most likely due to a circular import)

ν•΄λ‹Ή μ—λŸ¬λŠ” λͺ¨λ“ˆ 간에 μ„œλ‘œλ₯Ό "μƒν˜Έ μ°Έμ‘°"ν•  λ•Œ λ°œμƒν•œλ‹€.
μƒˆλ‘œμš΄ λͺ¨λ“ˆμ„ import 해쀄 λ•Œ 꼬이면 ν•΄λ‹Ή μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.

 

 


λ‚˜μ˜ 경우 Flask ν”„λ ˆμž„μ›Œν¬λ₯Ό μ΄μš©ν•΄μ„œ μ•„λž˜μ™€ 같은 폴더 ꡬ쑰둜 κ°œλ°œν•˜λ‹€κ°€ μœ„μ˜ μ—λŸ¬λ₯Ό λ§Œλ‚˜κ²Œ λ˜μ—ˆλ‹€. (μ•„λž˜ ν΄λ”κ΅¬μ‘°λŠ” λ‹¨μˆœν™”ν•œ κ²ƒμž„)

app/
β”œβ”€β”€ __init__.py
β”œβ”€β”€ models.py
β”œβ”€β”€ routers.py
β”œβ”€β”€ config.py
└── run.py
app.py

 

1. __init__.py와 models.py와 app.py νŒŒμΌμ„ 각각 μžμ„Ένžˆ μ‚΄νŽ΄λ³΄μž.

# __init__.py

from flask import Flask
from config import Config
from flask_sqlalchemy import SQLAlchemy
from app.models import app

db = SQLAlchemy()

def create_app(config_class=Config):     
    app.config['SQLALCHEMY_DATABASE_URI'] = Config.SQLALCHEMY_DATABASE_URI
    app.secret_key = Config.SECRET_KEY

    db.init_app(app)
    
    from app.views import main
    app.register_blueprint(main.bp)

    return app
# models.py

from flask import Flask
from app import db
from datetime import datetime

app = Flask(__name__)

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

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20), nullable=False)
    email = db.Column(db.String(255), nullable=False)
    password = db.Column(db.String(255), nullable=False)

...

# 파일이 직접 싀행될 λ•Œλ§Œ λ°μ΄ν„°λ² μ΄μŠ€ ν…Œμ΄λΈ”μ„ μƒμ„±ν•˜λ„λ‘ ν•œλ‹€. (μ‹€μ œ applicationμ—μ„œλŠ” 제거)
if __name__ == "__main__":
    with app.app_context():
        db.create_all()
# app.py

from app import create_app
from app import db
from config import Config

app = create_app(config_class=Config)

if __name__ == '__main__':
    app.run(debug=True, port=8000)

 

2. μ‚΄νŽ΄λ³΄λ©΄ models.py와 app폴더 μ•ˆμ˜ __init__.py 파일이 μ„œλ‘œλ₯Ό μ°Έμ‘°ν•˜κ³  μžˆλŠ” 것을 λ³Ό 수 μžˆλ‹€.

__init__.py의 from app.models import app ↔️ models.py의 from app import db

(κΌ­ ν•„μš”ν•œ λΆ€λΆ„λ§Œ 보자면 μ•„λž˜μ™€ κ°™λ‹€)

# __init__.py

from flask import Flask
from config import Config
from flask_sqlalchemy import SQLAlchemy
from app.models import app  # μ—¬κΈ°

db = SQLAlchemy()

def create_app(config_class=Config):     
    app.config['SQLALCHEMY_DATABASE_URI'] = Config.SQLALCHEMY_DATABASE_URI
    app.secret_key = Config.SECRET_KEY

    db.init_app(app)

    return app
# models.py

from flask import Flask
from app import db  # μ—¬κΈ°

app = Flask(__name__)

if __name__ == "__main__":
    with app.app_context():
        db.create_all()

 

3. μ•„λž˜μ™€ 같이 μ°Έμ‘°ν•˜λŠ” μ½”λ“œλ“€μ„ λ°”κΏ”μ£Όμ—ˆλ‹€.

# __init__.py

from flask import Flask
from config import Config
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

def create_app(config_class=Config): 
    app = Flask(__name__)  # μ—¬κΈ°
    
    app.config['SQLALCHEMY_DATABASE_URI'] = Config.SQLALCHEMY_DATABASE_URI
    app.secret_key = Config.SECRET_KEY
    
    db.init_app(app)
    
    from app.views import main
    app.register_blueprint(main.bp)

    return app

- models.py에 있던 app = Flask(__name__) μΈμŠ€ν„΄μŠ€ 생성 ꡬ문을 __init__.py의 create_app() λ©”μ„œλ“œ μ•ˆμ— 지역 μΈμŠ€ν„΄μŠ€λ‘œ 생성

# models.py

from flask import Flask
from app import db
from datetime import datetime

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

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20), nullable=False)
    email = db.Column(db.String(255), nullable=False)
    password = db.Column(db.String(255), nullable=False)
# app.py

from app import create_app
from app import db
from config import Config

app = create_app(config_class=Config)

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
    app.run(debug=True, port=8088)

- models.py에 μžˆλŠ” if문을 app.py 파일의 ifλ¬Έ μ•ˆμ— μΆ”κ°€

 

 

μœ„μ™€ 같이 λ°”κΏ”μ€ŒμœΌλ‘œμ¨ μ—λŸ¬λ₯Ό ν•΄κ²°ν•˜μ˜€λ‹€.

 


μ΄λŸ¬ν•œ μ—λŸ¬κ°€ μ™œ λ°œμƒν–ˆλŠ”μ§€ μ•Œμ•„λ³΄μž

pythonμ—μ„œλŠ” μ½”λ“œμ˜ μ‹€ν–‰ μ‹œμ κ³Ό λͺ¨λ“ˆμ„ μž„ν¬νŠΈν•˜λŠ” μ‹œμ μ΄ λΆ„λ¦¬λ˜μ–΄ 있기 λ•Œλ¬Έμ΄λ‹€.

κ·Έλž˜μ„œ λͺ¨λ“ˆ κ°„μ˜ μˆœν™˜ μ°Έμ‘° λ¬Έμ œκ°€ λ°œμƒν•˜κ²Œ λ˜λŠ” 것이닀.

(python은 μƒν˜Έμ°Έμ‘°λ˜λŠ” λͺ¨λ“ˆ λ‚΄λΆ€μ˜ μ½”λ“œλ₯Ό 순차적으둜 μ‹€ν–‰ν•˜κΈ° λ•Œλ¬Έμ—, ν•˜λ‚˜μ˜ λͺ¨λ“ˆμ΄ μž„ν¬νŠΈν•  λ•Œ ν•΄λ‹Ή λͺ¨λ“ˆ λ‚΄λΆ€μ—μ„œ λ‹€λ₯Έ λͺ¨λ“ˆμ„ μ°Έμ‘°ν•˜κ³ , κ·Έ λ‹€λ₯Έ λͺ¨λ“ˆμ—μ„œ λ‹€μ‹œ 처음 λͺ¨λ“ˆμ„ μ°Έμ‘°ν•˜λŠ” μ‹μœΌλ‘œ μˆœν™˜ μ°Έμ‘° λ°œμƒν•˜κ²Œ 된 것)

 

  • Runtime: λͺ¨λ“ˆμ„ μž„ν¬νŠΈν•œ ν›„ ν•΄λ‹Ή λͺ¨λ“ˆ λ‚΄μ˜ ν΄λž˜μŠ€λ‚˜ ν•¨μˆ˜λ₯Ό μ‹€μ œλ‘œ μ‚¬μš©ν•˜λŠ” μ‹œμ . μ΄λ•Œ ν΄λž˜μŠ€λ‚˜ ν•¨μˆ˜μ˜ 본문이 μ‹€ν–‰λ˜λ©° μ½”λ“œκ°€ μ‹€μ œλ‘œ λ™μž‘ν•œλ‹€.
  • Import Time: Python은 λͺ¨λ“ˆμ„ μž„ν¬νŠΈν•  λ•Œ ν•΄λ‹Ή λͺ¨λ“ˆμ˜ μ½”λ“œλ₯Ό νŒŒμ‹±ν•œλ‹€. μ΄λ•Œ λͺ¨λ“ˆ λ‚΄λΆ€μ˜ 클래슀, ν•¨μˆ˜, λ³€μˆ˜ 등이 μ •μ˜λ˜κ³  μ‹€ν–‰λœλ‹€. 즉 λͺ¨λ“ˆμ΄ μž„ν¬νŠΈλ˜λŠ” μ‹œμ μ—μ„œλŠ” ν΄λž˜μŠ€λ‚˜ ν•¨μˆ˜μ˜ μ‹€μ œ 싀행은 λ°œμƒν•˜μ§€ μ•Šκ³ , λ‹¨μˆœνžˆ μ½”λ“œλ§Œ λΆ„μ„λœ ν›„ μ‹€μ œ 싀행은 미루어진닀.

 

μ΄λŸ¬ν•œ 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•œ λ°©λ²•μœΌλ‘œλŠ” ν”„λ‘œμ νŠΈ ꡬ쑰에 따라 κ·Έλ•Œλ§ˆλ‹€ λ‹¬λΌμ§€κ² μ§€λ§Œ, 

κ²°κ΅­ μž„ν¬νŠΈ μ‹œμ κ³Ό μ‹€ν–‰ μ‹œμ μ„ μ μ ˆν•˜κ²Œ μ‘°μœ¨ν•˜λŠ” κ²ƒμœΌλ‘œ ν•΄κ²°ν•  수 μžˆλ‹€.

 

 

 

 

[참고 링크]

https://blog.mathpresso.com/python-circular-imports-e89c5bf16510

https://mapadubak.tistory.com/82

https://flask.palletsprojects.com/en/2.3.x/api