Khi tách ứng dụng Flask thành nhiều file, việc import trực tiếp đối tượng app từ module này sang module khác rất dễ sinh lỗi circular import. Giải pháp là dùng current_app — một proxy trỏ tới ứng dụng đang xử lý request — trong view, blueprint và helper, thay vì giữ tham chiếu tới app cụ thể. Cách này chỉ hoạt động khi có application context, và nó đặc biệt phù hợp với mô hình application factory.

Vấn đề

Giả sử bạn có app.py tạo ứng dụng và helpers.py cần đọc cấu hình. Cách viết trực tiếp trông vô hại:

# helpers.py
from app import app
def get_upload_folder():
return app.config["UPLOAD_FOLDER"]
# app.py
from flask import Flask
from helpers import get_upload_folder
app = Flask(__name__)
app.config["UPLOAD_FOLDER"] = "uploads"

Vấn đề xuất hiện ngay khi app.py import helpers, còn helpers lại import ngược app. Python chưa kịp tạo xong đối tượng app thì đã phải quay lại module đang khởi tạo dở, nên bạn thường gặp lỗi ImportError: cannot import name ‘app’ from partially initialized module. Lỗi này càng dễ gặp khi dự án lớn dần.

Giải pháp

Flask cung cấp current_app — một proxy được giải quyết tại thời điểm chạy, trỏ tới ứng dụng đang gắn với context hiện tại. Bạn không cần biết app được tạo ở đâu, chỉ cần dùng nó khi request hoặc application context đang hoạt động:

# helpers.py
from flask import current_app
def get_upload_folder():
return current_app.config["UPLOAD_FOLDER"]
# routes.py
from flask import Blueprint, current_app
bp = Blueprint("main", __name__)
@bp.route("/upload-folder")
def upload_folder():
current_app.logger.info("Doc thu muc upload")
return {"folder": get_upload_folder()}

Vì helpers.py không còn import app, vòng lặp import biến mất, và cùng một helper dùng được cho nhiều ứng dụng — ví dụ app chính và app test với cấu hình riêng. current_app cũng cho bạn truy cập config, logger và extensions của đúng ứng dụng đang chạy.

Có hai lưu ý quan trọng. Thứ nhất, đừng đọc current_app ở cấp module, vì lúc đó chưa có context nào hoạt động. Hãy đọc nó bên trong hàm. Thứ hai, ngoài request — ví dụ trong thread nền hoặc script riêng — bạn phải tự tạo context:

def send_report(app):
with app.app_context():
folder = get_upload_folder()
current_app.logger.info("Gui bao cao tu %s", folder)

Trong khối with app.app_context(), current_app trỏ tới đúng app bạn truyền vào. Nếu quên bước này, Flask báo lỗi Working outside of application context, và đó là dấu hiệu bạn cần bọc code trong context thay vì quay lại import trực tiếp.

Áp dụng vào dự án của bạn

Hãy rà soát các helper và service, tìm những dòng dạng from app import app rồi thay bằng current_app trong thân hàm. Giữ nguyên application factory trả về app, để blueprint và helper không còn phụ thuộc vào một biến toàn cục cụ thể. Khi viết test, dùng test client hoặc bọc code trong app.app_context(). Sau bước này, việc tách thêm blueprint hay đổi tên module sẽ ít làm vỡ import hơn hẳn.

Bài viết liên quan

Dùng công cụ chèn link của trình chỉnh sửa để gắn link nội bộ tới đúng 3 bài đã xuất bản trên theo tiêu đề (chỉ link tới bài ĐÃ XUẤT BẢN, không link tới bài nháp). Nếu một bài không tìm thấy qua công cụ chèn link thì giữ nguyên text thuần và báo rõ bài nào.

Câu hỏi thường gặp

current_app khác gì với đối tượng app bình thường?

current_app là một proxy: nó chỉ trỏ tới đúng ứng dụng đang hoạt động khi có application context. Nhờ vậy cùng một đoạn code chạy được với nhiều app khác nhau mà không cần import app cụ thể.

Khi nào gặp lỗi “Working outside of application context”?

Khi bạn truy cập current_app ngoài request và ngoài khối app.app_context(), ví dụ trong thread nền, script độc lập hoặc ở cấp module lúc import. Cách sửa là bọc đoạn code đó trong with app.app_context():.

Có nên dùng current_app trong mọi helper không?

Nên, nếu helper cần đọc config, ghi log hoặc truy cập extension của app. Nếu helper chỉ xử lý dữ liệu thuần túy, không cần app, hãy giữ nó độc lập để dễ test và tái sử dụng hơn.

current_app có dùng được với application factory không?

Có, và đó chính là trường hợp nó phát huy tác dụng nhất. Factory có thể tạo nhiều app với cấu hình khác nhau, còn blueprint và helper dùng current_app sẽ luôn thấy đúng app đang chạy.

Bạn đã thử cách này trong dự án của mình chưa? Nếu gặp lỗi hoặc có cách làm gọn hơn, để lại bình luận bên dưới — mình đọc hết và sẽ bổ sung những ý hay vào bài.

Nguồn tham khảo