Hardcode URL như “/users/42” khắp template và redirect khiến mỗi lần đổi route là một lần rà soát toàn dự án. Flask đã có sẵn url_for(): bạn gọi tên endpoint thay vì gõ đường dẫn, Flask tự sinh URL đúng — kể cả khi url_prefix của blueprint thay đổi. Hãy dùng url_for ngay từ đầu cho mọi link nội bộ, redirect và file tĩnh, và chỉ hardcode URL của hệ thống ngoài.

Vấn đề

Một dự án Flask nhỏ thường bắt đầu bằng vài đường dẫn gõ tay trong template và trong hàm view. Lúc đầu mọi thứ chạy tốt, nhưng khi bạn thêm tiền tố /admin, đổi /users/<id> thành /members/<id>, hoặc tách route sang blueprint, từng chuỗi URL gõ tay trở thành một điểm vỡ tiềm ẩn. Trình biên dịch không báo lỗi, test không bắt được nếu không click đúng link đó, và người dùng là người đầu tiên phát hiện trang 404.

# Gõ tay: đổi route là vỡ link
return redirect("/users/42")
# Trong template Jinja:
# <a href="/users/{{ user.id }}">Hồ sơ</a>

Giải pháp

url_for() nhận tên endpoint — mặc định là tên hàm view — cùng các giá trị cho biến trong route, rồi trả về URL đã ghép đúng. Khi route đổi, chỉ cần sửa decorator @app.route, mọi nơi gọi url_for tự cập nhật theo.

from flask import Flask, redirect, url_for
app = Flask(__name__)
@app.route("/members/<int:user_id>")
def profile(user_id):
return f"Hồ sơ {user_id}"
@app.route("/go")
def go():
# Sinh "/members/42" dù route đã đổi tên
return redirect(url_for("profile", user_id=42))

Với blueprint, tên endpoint có tiền tố là tên blueprint: url_for("admin.dashboard"). Trong chính blueprint đó, bạn có thể dùng tên tương đối url_for(".dashboard"). Tham số thừa không khớp biến route sẽ tự thành query string, ví dụ url_for("search", q="flask") cho ra /search?q=flask. Cần URL tuyệt đối cho email hay sitemap thì thêm _external=True. File tĩnh cũng đi qua cùng một cơ chế: url_for("static", filename="css/main.css").

# Trong template Jinja — không còn chuỗi URL gõ tay
# <a href="{{ url_for('profile', user_id=user.id) }}">Hồ sơ</a>
# <link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">

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

Tìm trong codebase các chuỗi bắt đầu bằng “/” xuất hiện trong redirect(), href và src, rồi thay dần bằng url_for, bắt đầu từ những link quan trọng nhất như đăng nhập, trang hồ sơ và trang quản trị. Khi đặt tên hàm view, hãy đặt tên rõ nghĩa vì đó chính là tên endpoint mà cả dự án sẽ gọi. Một thói quen nhỏ này giúp bạn đổi cấu trúc URL thoải mái mà không phải rà soát từng template mỗi lần refactor.

Bài viết liên quan

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

url_for dùng ngoài hàm view được không?

Được, nhưng phải có ngữ cảnh: trong request thì dùng trực tiếp, còn trong script hay lệnh CLI thì bọc bằng app.test_request_context(). Nếu đã cấu hình SERVER_NAME, bạn có thể gọi url_for ngay trong application context.

Endpoint của view trong blueprint đặt tên thế nào?

Tên đầy đủ là tên_blueprint.tên_hàm, ví dụ blueprint admin có hàm dashboard thì endpoint là admin.dashboard. Từ trong chính blueprint đó, url_for(".dashboard") cũng trỏ tới cùng một view.

Khi nào cần _external=True?

Khi bạn cần URL tuyệt đối, thường là link trong email xác nhận, link trong sitemap hoặc feed RSS. Với link điều hướng nội bộ trong trình duyệt, URL tương đối mặc định là đủ và gọn hơn.

Tham số thừa khi gọi url_for sẽ đi đâu?

Flask ghép các tham số khớp với biến trong route vào đường dẫn, phần còn lại tự chuyển thành query string. Nhờ vậy bạn không cần tự nối chuỗi ?page=2 bằng tay nữa.

💬 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