Trang chủHướng dẫnHướng dẫn lập trình web với Python Flask
Chuyên gia

Hướng dẫn lập trình web với Python Flask

CyStack blog 32 phút để đọc
CyStack blog11/08/2025
Locker Avatar

Chris Pham

Technical Writer

Locker logo social
Reading Time: 32 minutes

Giới thiệu về Flask

Flask là một khung web (web framework) Python nhỏ gọn và linh hoạt, cung cấp các công cụ và tính năng hữu ích giúp việc tạo các ứng dụng web bằng Python trở nên dễ dàng hơn.

lập trình web với Python Flask

Nó mang lại sự linh hoạt cho các nhà phát triển và là một khung làm việc dễ tiếp cận hơn cho những người mới bắt đầu, bởi vì bạn có thể xây dựng một ứng dụng web nhanh chóng chỉ với một tệp Python duy nhất. Flask cũng có khả năng mở rộng và không ép buộc một cấu trúc thư mục cố định hay yêu cầu mã boilerplate phức tạp trước khi bắt đầu.

Trong bài viết này, chúng ta sẽ sử dụng bộ công cụ Bootstrap để thiết kế giao diện cho ứng dụng của bạn, giúp nó trông đẹp hơn. Bootstrap sẽ hỗ trợ chúng ta kết hợp các trang web responsive (phù hợp với cả màn hình máy tính và thiết bị di động) mà không cần viết quá nhiều mã HTML, CSS và JavaScript của riêng bạn để đạt được các mục tiêu này. Nhờ đó, bạn có thể tập trung vào việc học cách Flask hoạt động.

Flask sử dụng công cụ tạo mẫu Jinja (Jinja template engine) để xây dựng động các trang HTML bằng cách sử dụng các khái niệm Python quen thuộc như biến, vòng lặp, danh sách, v.v. Bạn sẽ sử dụng các mẫu này như một phần của dự án.

Trong hướng dẫn này, chúng ta sẽ xây dựng một ứng dụng blog web nhỏ bằng Flask và SQLite trong Python 3. Người dùng của ứng dụng có thể xem tất cả các bài viết trong cơ sở dữ liệu của bạn, nhấp vào tiêu đề của một bài viết để xem nội dung của nó, đồng thời có khả năng thêm bài viết mới vào cơ sở dữ liệu và chỉnh sửa hoặc xóa một bài viết hiện có.

Chuẩn bị

Trước khi bắt đầu làm theo hướng dẫn này, bạn sẽ cần:

  • Một môi trường lập trình Python 3
  • Hiểu biết về các khái niệm Python 3, chẳng hạn như kiểu dữ liệu, câu lệnh điều kiện, vòng lặp for, hàm và các khái niệm tương tự khác.

Bước 1: Cài đặt Flask

Trong bước này, chúng ta sẽ kích hoạt môi trường Python và cài đặt Flask bằng trình cài đặt gói pip.

Nếu bạn chưa kích hoạt môi trường lập trình của mình, hãy đảm bảo bạn đang ở trong thư mục dự án (flask_blog) và sử dụng lệnh sau để kích hoạt môi trường:

source env/bin/activate

Khi môi trường lập trình của bạn được kích hoạt, dấu nhắc của bạn bây giờ sẽ có tiền tố (env) trông như sau:

(env) sammy@localhost:$

Tiền tố này là dấu hiệu cho thấy môi trường env hiện đang hoạt động, nó có thể có tên khác tùy thuộc vào cách bạn đặt tên khi tạo.

Lưu ý: Nếu bạn đang sử dụng Git, việc bỏ qua thư mục env vừa được tạo trong file .gitignore là một ý tưởng hay để tránh theo dõi các tệp không liên quan đến dự án.

Bây giờ bạn sẽ cài đặt các gói Python và cách ly mã nguồn dự án khỏi bản cài đặt Python chính của hệ thống. Bạn sẽ thực hiện điều này bằng cách sử dụng pippython.

Để cài đặt Flask, hãy chạy lệnh sau:

pip install flask

Sau khi cài đặt hoàn tất, hãy chạy lệnh sau để xác nhận cài đặt:

python -c "import flask; print(flask.__version__)"

Bạn sử dụng giao diện dòng lệnh python với tùy chọn -c để thực thi mã Python. Tiếp theo, bạn nhập gói flask bằng import flask; sau đó in phiên bản Flask, được cung cấp thông qua biến flask.__version__.

Kết quả sẽ là một số phiên bản tương tự như sau:

Output1.1.2

Bạn đã tạo thư mục dự án, một môi trường ảo và cài đặt Flask. Bây giờ bạn đã sẵn sàng chuyển sang thiết lập ứng dụng cơ sở của mình.

Bước 2: Tạo một ứng dụng cơ bản

Khi môi trường lập trình của bạn đã được thiết lập, bạn sẽ bắt đầu sử dụng Flask. Trong bước này, bạn sẽ tạo một ứng dụng web nhỏ trong một tệp Python và chạy nó để khởi động máy chủ, ứng dụng này sẽ hiển thị một số thông tin trên trình duyệt.

Trong thư mục flask_blog , mở một tệp có tên hello.py để chỉnh sửa, sử dụng nano hoặc trình soạn thảo văn bản yêu thích của bạn:

nano hello.py

Tệp hello.py này sẽ đóng vai trò là một ví dụ tối thiểu về cách xử lý các yêu cầu HTTP. Bên trong nó, bạn sẽ nhập đối tượng Flask và tạo một hàm trả về phản hồi HTTP. Viết mã sau vào trong hello.py:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello, World!'

Trong khối mã trên, bạn nhập đối tượng Flask từ gói flask. Sau đó, bạn sử dụng nó để tạo một thể hiện ứng dụng Flask của bạn với tên app. Bạn truyền biến đặc biệt __name__ chứa tên của mô-đun Python hiện tại. Nó được dùng để cho instance biết vị trí của nó bạn cần làm điều này vì Flask thiết lập một số đường dẫn ngầm phía sau.

Khi bạn tạo instance app, bạn sử dụng nó để xử lý các yêu cầu web đến và gửi phản hồi cho người dùng. @app.route là một decorator biến một hàm Python thông thường thành một hàm viewcủa Flask, chuyển đổi giá trị trả về của hàm thành một phản hồi HTTP để được hiển thị bởi một máy khách HTTP, chẳng hạn như trình duyệt web. Bạn truyền giá trị '/' cho @app.route() để biểu thị rằng hàm này sẽ phản hồi các yêu cầu web cho URL /, đây là URL chính.

Hàm xem hello() trả về chuỗi 'Hello, World!' làm phản hồi.

Lưu và đóng tệp.

Để chạy ứng dụng web của bạn, trước tiên bạn sẽ cho Flask biết tệp ứng dụng (tệp hello.py trong trường hợp của bạn) bằng biến môi trường FLASK_APP:

export FLASK_APP=hello

Sau đó chạy nó ở chế độ phát triển với biến môi trường FLASK_ENV:

export FLASK_ENV=development

Cuối cùng, chạy ứng dụng bằng lệnh flask run:

flask run

Khi ứng dụng đang chạy, đầu ra sẽ giống như thế này:

Output * Serving Flask app "hello" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on <http://127.0.0.1:5000/> (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 813-894-335

Đầu ra trên có một số thông tin, chẳng hạn như:

  • Tên của ứng dụng bạn đang chạy.
  • Môi trường mà ứng dụng đang được chạy.
  • Debug mode: on có nghĩa là trình gỡ lỗi của Flask đang chạy. Điều này hữu ích khi phát triển vì nó cung cấp cho chúng ta các thông báo lỗi chi tiết khi có sự cố, giúp việc khắc phục sự cố dễ dàng hơn.
  • Ứng dụng đang chạy cục bộ trên URL http://127.0.0.1:5000/, trong đó 127.0.0.1 là IP đại diện cho localhost của máy bạn và :5000 là số cổng.

Mở trình duyệt và nhập URL http://127.0.0.1:5000/, bạn sẽ nhận được chuỗi Hello, World! làm phản hồi, điều này xác nhận rằng ứng dụng của bạn đang chạy thành công.

Cảnh báo: Flask sử dụng một máy chủ web đơn giản để phục vụ ứng dụng của chúng ta trong môi trường phát triển, điều này cũng có nghĩa là trình gỡ lỗi (debugger) Flask đang chạy để dễ dàng phát hiện lỗi hơn. Máy chủ phát triển này không nên được sử dụng trong môi trường sản xuất (production).

Bây giờ bạn có thể để máy chủ phát triển chạy trong terminal và mở một cửa sổ terminal khác. Di chuyển vào thư mục dự án nơi hello.py đang nằm, kích hoạt môi trường ảo, đặt các biến môi trường FLASK_ENVFLASK_APP, và tiếp tục các bước tiếp theo.

Lưu ý: Khi mở một terminal mới, điều quan trọng là phải nhớ kích hoạt môi trường ảo và đặt các biến môi trường FLASK_ENVFLASK_APP.

Khi máy chủ phát triển của ứng dụng Flask đã chạy, không thể chạy một ứng dụng Flask khác với cùng lệnh flask run. Điều này là do flask run sử dụng số cổng 5000 theo mặc định và một khi nó bị chiếm, nó sẽ không khả dụng để chạy một ứng dụng khác, vì vậy bạn sẽ nhận được lỗi tương tự như sau:

OutputOSError: [Errno 98] Address already in use

Để giải quyết vấn đề này, hãy dừng máy chủ đang chạy bằng CTRL+C, sau đó chạy flask run lại, hoặc nếu bạn muốn chạy cả hai cùng lúc, bạn có thể truyền một số cổng khác cho đối số -p, ví dụ, để chạy một ứng dụng khác trên cổng 5001, sử dụng lệnh sau:

flask run -p 5001

Bây giờ bạn đã có một ứng dụng web Flask nhỏ. Bạn đã chạy ứng dụng của mình và hiển thị thông tin trên trình duyệt web. Tiếp theo, chúng ta sẽ sử dụng các tệp HTML trong ứng dụng của bạn.

Bước 3: Sử dụng các mẫu HTML

Hiện tại, ứng dụng của chúng ta chỉ hiển thị một thông báo đơn giản mà không có bất kỳ HTML nào. Các ứng dụng web chủ yếu sử dụng HTML để hiển thị thông tin cho khách truy cập, vì vậy bây giờ chúng ta sẽ làm việc để kết hợp các tệp HTML vào ứng dụng của mình, các tệp này có thể được hiển thị trên trình duyệt web.

Flask cung cấp hàm trợ giúp render_template() cho phép sử dụng công cụ tạo template Jinja. Điều này sẽ giúp việc quản lý HTML dễ dàng hơn nhiều bằng cách viết mã HTML của bạn trong các tệp .html cũng như sử dụng logic trong mã HTML của bạn. Bạn sẽ sử dụng các tệp HTML này ( gọi là templates) để xây dựng tất cả các trang ứng dụng của mình, chẳng hạn như trang chính nơi bạn sẽ hiển thị các bài đăng blog hiện tại, trang của bài đăng blog, trang nơi người dùng có thể thêm bài đăng mới, v.v.

Trong bước này, chúng ta sẽ tạo ứng dụng Flask chính của mình trong một tệp mới.

Đầu tiên, trong thư mục flask_blog của bạn, sử dụng nano hoặc trình soạn thảo yêu thích của bạn để tạo và chỉnh sửa tệp app.py của bạn. Tệp này sẽ chứa tất cả mã bạn sẽ sử dụng để tạo ứng dụng blog:

nano app.py

Trong tệp mới này, bạn sẽ import đối tượng Flask để tạo một instance ứng dụng Flask giống như bạn đã làm trước đó. Bạn cũng sẽ import hàm hỗ trợ render_template() giúp bạn render (kết xuất) các tệp mẫu HTML nằm trong thư mục templates mà bạn sắp tạo. Tệp này sẽ có một hàm xử lý view duy nhất, chịu trách nhiệm xử lý các yêu cầu đến tuyến chính /. Hãy thêm nội dung sau:

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

Hàm index() trả về kết quả của việc gọi render_template() với index.html làm đối số, điều này cho biết render_template() sẽ tìm kiếm một tệp có tên index.html trong thư mục templates. Cả thư mục và tệp đều chưa tồn tại, bạn sẽ gặp lỗi nếu bạn chạy ứng dụng vào thời điểm này. Bạn sẽ chạy nó dù sao để bạn quen thuộc với ngoại lệ thường gặp này. Sau đó, bạn sẽ khắc phục nó bằng cách tạo thư mục và tệp cần thiết.

Lưu và thoát tệp.

Dừng máy chủ phát triển trong terminal khác đang chạy ứng dụng hello bằng CTRL+C.

Trước khi chạy ứng dụng, hãy đảm bảo bạn chỉ định đúng giá trị cho biến môi trường FLASK_APP, vì bạn không còn sử dụng ứng dụng hello nữa:

export FLASK_APP=app
flask run

Mở URL http://127.0.0.1:5000/ trong trình duyệt của bạn sẽ dẫn đến trang gỡ lỗi thông báo cho bạn rằng mẫu index.html không tìm thấy. Dòng chính trong mã gây ra lỗi này sẽ được đánh dấu. Trong trường hợp này, đó là dòng return render_template('index.html').

Nếu bạn nhấp vào dòng này, trình gỡ lỗi sẽ tiết lộ thêm mã để bạn có thêm ngữ cảnh giúp bạn giải quyết vấn đề.

Để khắc phục lỗi này, hãy tạo một thư mục có tên templates bên trong thư mục flask_blog của bạn. Sau đó bên trong, mở một tệp có tên index.html để chỉnh sửa:

mkdir templates
nano templates/index.html

Tiếp theo, thêm mã HTML sau vào trong index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>FlaskBlog</title>
</head>
<body>
   <h1>Welcome to FlaskBlog</h1>
</body>
</html>

Lưu tệp và sử dụng trình duyệt của bạn để điều hướng đến http://127.0.0.1:5000/ một lần nữa, hoặc làm mới trang. Lần này trình duyệt sẽ hiển thị văn bản “Welcome to FlaskBlog” trong một thẻ <h1>.

Ngoài thư mục templates, các ứng dụng web Flask cũng thường có một thư mục static để lưu trữ các tệp tĩnh, chẳng hạn như tệp CSS, tệp JavaScript và hình ảnh mà ứng dụng sử dụng.

Bạn có thể tạo một tệp stylesheet style.css để thêm CSS vào ứng dụng của mình. Trước tiên, hãy tạo một thư mục có tên là static bên trong thư mục chính flask_blog:

mkdir static

Sau đó tạo một thư mục khác có tên css bên trong thư mục static để lưu trữ các tệp .css. Điều này thường được thực hiện để tổ chức các tệp tĩnh trong các thư mục chuyên dụng, ví dụ, các tệp JavaScript thường nằm trong một thư mục có tên js, hình ảnh được đặt trong một thư mục có tên images (hoặc img), v.v. Lệnh sau sẽ tạo thư mục css bên trong thư mục static:

mkdir static/css

Sau đó mở tệp style.css bên trong thư mục css để chỉnh sửa:

nano static/css/style.css

Thêm quy tắc CSS sau vào tệp style.css của bạn:

h1 {
    border: 2px #eee solid;
    color: brown;
    text-align: center;
    padding: 10px;
}

Mã CSS sẽ thêm một đường viền, thay đổi màu sang nâu, căn giữa văn bản và thêm một ít khoảng đệm cho các thẻ <h1>.

Lưu và đóng tệp.

Tiếp theo, mở tệp mẫu index.html để chỉnh sửa:

nano templates/index.html

Bạn sẽ thêm một liên kết đến tệp style.css bên trong phần <head> của tệp mẫu index.html:

. . .
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="{{ url_for('static', filename= 'css/style.css') }}">
    <title>FlaskBlog</title>
</head>
. . .

Ở đây, bạn sử dụng hàm trợ giúp url_for() để tạo vị trí tệp thích hợp. Đối số đầu tiên chỉ định rằng bạn đang liên kết đến một tệp tĩnh và đối số thứ hai là đường dẫn của tệp bên trong thư mục static.

Lưu và đóng tệp.

Sau khi làm mới trang index của ứng dụng, bạn sẽ nhận thấy văn bản “Welcome to FlaskBlog” giờ đây có màu nâu, căn giữa và nằm trong một khung viền.

Bạn có thể sử dụng ngôn ngữ CSS để tạo kiểu cho ứng dụng và làm cho nó hấp dẫn hơn bằng thiết kế của riêng bạn. Tuy nhiên, nếu bạn không phải là nhà thiết kế web, hoặc nếu bạn chưa quen thuộc với CSS, thì bạn có thể sử dụng bộ công cụ Bootstrap, bộ công cụ này cung cấp các thành phần dễ sử dụng để tạo kiểu cho ứng dụng của bạn. Trong dự án này, chúng ta sẽ sử dụng Bootstrap.

Bạn có thể đã đoán rằng việc tạo một mẫu HTML khác sẽ có nghĩa là lặp lại hầu hết mã HTML bạn đã viết trong mẫu index.html. Bạn có thể tránh việc lặp lại mã không cần thiết với sự trợ giúp của một tệp mẫu cơ sở (base template), mà tất cả các tệp HTML của bạn sẽ kế thừa từ đó. Xem “Kế thừa mẫu trong Jinja” để biết thêm thông tin.

Để tạo một mẫu cơ sở, trước tiên hãy tạo một tệp có tên base.html bên trong thư mục templates của bạn:

nano templates/base.html

Gõ mã sau vào mẫu base.html của bạn:

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="<https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css>" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

    <title>{% block title %} {% endblock %}</title>
  </head>
  <body>
    <nav class="navbar navbar-expand-md navbar-light bg-light">
        <a class="navbar-brand" href="{{ url_for('index')}}">FlaskBlog</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarNav">
            <ul class="navbar-nav">
            <li class="nav-item active">
                <a class="nav-link" href="#">About</a>
            </li>
            </ul>
        </div>
    </nav>
    <div class="container">
        {% block content %} {% endblock %}
    </div>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="<https://code.jquery.com/jquery-3.3.1.slim.min.js>" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="<https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js>" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="<https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js>" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
  </body>
</html>

Lưu và đóng tệp khi bạn đã chỉnh sửa xong.

Hầu hết mã trong khối trên là HTML tiêu chuẩn và mã yêu cầu cho Bootstrap. Các thẻ <meta> cung cấp thông tin cho trình duyệt web, thẻ <link> liên kết các tệp CSS của Bootstrap và các thẻ <script> là các liên kết đến mã JavaScript cho phép một số tính năng Bootstrap bổ sung, hãy xem tài liệu Bootstrap để biết thêm.

Tuy nhiên, các phần được đánh dấu sau đây là cụ thể cho công cụ tạo mẫu Jinja:

  • {% block title %} {% endblock %}: Một khối đóng vai trò là trình giữ chỗ cho tiêu đề, bạn sẽ sử dụng nó sau này trong các mẫu khác để cung cấp tiêu đề tùy chỉnh cho mỗi trang trong ứng dụng của bạn mà không cần viết lại toàn bộ phần <head> mỗi lần.
  • {{ url_for('index')}}: Một lệnh gọi hàm sẽ trả về URL cho hàm xem index(). Điều này khác với lệnh gọi url_for() trước đây bạn đã sử dụng để liên kết một tệp CSS tĩnh, vì nó chỉ nhận một đối số, đó là tên của hàm xem và liên kết đến tuyến đường liên quan đến hàm thay vì một tệp tĩnh.
  • {% block content %} {% endblock %}: Một khối khác sẽ được thay thế bằng nội dung tùy thuộc vào mẫu con (child template) (các mẫu kế thừa từ base.html) sẽ ghi đè nó.

Bây giờ bạn đã có một mẫu cơ sở, bạn có thể tận dụng nó bằng cách sử dụng tính kế thừa. Mở tệp index.html:

nano templates/index.html

Sau đó, thay thế nội dung của nó bằng đoạn mã sau:

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
{% endblock %}

Trong phiên bản mới này của mẫu index.html, bạn sử dụng thẻ {% extends %} để kế thừa từ mẫu base.html. Sau đó, bạn mở rộng nó bằng cách thay thế khối content trong mẫu cơ sở bằng nội dung bên trong khối content trong khối mã trên.

Khối content này chứa một thẻ <h1> với văn bản “Welcome to FlaskBlog” bên trong một khối titlevà khối này sẽ thay thế khối title gốc trong template base.html bằng dòng chữ “Welcome to FlaskBlog”. Cách làm này giúp bạn tránh phải lặp lại cùng một đoạn văn bản hai lần, vì nó vừa đóng vai trò là tiêu đề của trang, vừa là tiêu đề chính hiển thị bên dưới thanh điều hướng được kế thừa từ template gốc.

Kế thừa mẫu cũng cho phép bạn sử dụng lại mã HTML bạn có trong các mẫu khác (base.html trong trường hợp này) mà không phải lặp lại mỗi khi cần.

Lưu và đóng tệp và làm mới trang index trên trình duyệt của bạn. Bạn sẽ thấy trang của mình với thanh điều hướng và tiêu đề đã được tạo kiểu.

Bạn đã sử dụng các mẫu HTML và tệp tĩnh trong Flask. Bạn cũng đã sử dụng Bootstrap để bắt đầu tinh chỉnh giao diện trang của mình và một mẫu cơ sở để tránh lặp lại mã. Trong bước tiếp theo, chúng ta sẽ thiết lập một cơ sở dữ liệu sẽ lưu trữ dữ liệu ứng dụng của bạn.

Bước 4: Thiết lập Cơ sở dữ liệu

Trong bước này, bạn sẽ thiết lập một cơ sở dữ liệu để lưu trữ dữ liệu, cụ thể là các bài đăng blog cho ứng dụng của bạn. Bạn cũng sẽ thêm một vài mục ví dụ vào cơ sở dữ liệu.

Bạn sẽ sử dụng tệp cơ sở dữ liệu SQLite để lưu dữ liệu vì mô-đun sqlite3, mà chúng ta sẽ dùng để tương tác với cơ sở dữ liệu, đã có sẵn trong thư viện chuẩn của Python. Để tìm hiểu thêm về SQLite, bạn có thể tham khảo hướng dẫn này.

Trước tiên, vì dữ liệu trong SQLite được lưu dưới dạng bảng và cột, và dữ liệu chính của bạn là các bài đăng blog, nên bạn cần tạo một bảng có tên là posts với các cột cần thiết. Bạn sẽ tạo một tệp .sql chứa các lệnh SQL để tạo bảng posts với một số cột, sau đó sử dụng tệp này để khởi tạo cơ sở dữ liệu.

Mở một tệp có tên schema.sql bên trong thư mục flask_blog của bạn:

nano schema.sql

Gõ các lệnh SQL sau vào trong tệp này:

DROP TABLE IF EXISTS posts;

CREATE TABLE posts (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    title TEXT NOT NULL,
    content TEXT NOT NULL
);

Lưu và đóng tệp.

Lệnh SQL đầu tiên là DROP TABLE IF EXISTS posts;, lệnh này xóa bất kỳ bảng nào đã tồn tại có tên posts để bạn không gặp phải hành vi gây nhầm lẫn. Lưu ý rằng điều này sẽ xóa tất cả nội dung bạn có trong cơ sở dữ liệu bất cứ khi nào bạn sử dụng các lệnh SQL này, vì vậy hãy đảm bảo bạn không viết bất kỳ nội dung quan trọng nào vào ứng dụng web cho đến khi bạn hoàn thành hướng dẫn này và thử nghiệm với kết quả cuối cùng. Tiếp theo, CREATE TABLE posts được sử dụng để tạo bảng posts với các cột sau:

  • id: Một số nguyên đại diện cho khóa chính (primary key), giá trị này sẽ được cơ sở dữ liệu tự động gán một giá trị duy nhất cho mỗi mục nhập (tức là một bài đăng blog).
  • created: Thời gian bài đăng blog được tạo. NOT NULL có nghĩa là cột này không được để trống và giá trị DEFAULTCURRENT_TIMESTAMP, đó là thời gian bài đăng được thêm vào cơ sở dữ liệu. Giống như id, bạn không cần chỉ định giá trị cho cột này, vì nó sẽ tự động được điền.
  • title: Tiêu đề bài đăng.
  • content: Nội dung bài đăng.

Bây giờ bạn đã có một **cấu trúc cơ sở dữ liệu (**schema SQL) trong tệp schema.sql, bạn sẽ sử dụng nó để tạo cơ sở dữ liệu bằng một tệp Python sẽ tạo một tệp cơ sở dữ liệu SQLite .db. Mở một tệp có tên init_db.py bên trong thư mục flask_blog bằng trình soạn thảo ưu thích của bạn:

nano init_db.py

Và sau đó thêm mã sau.

import sqlite3

connection = sqlite3.connect('database.db')

with open('schema.sql') as f:
    connection.executescript(f.read())

cur = connection.cursor()

cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)",
            ('First Post', 'Content for the first post')
            )

cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)",
            ('Second Post', 'Content for the second post')
            )

connection.commit()
connection.close()

Đầu tiên, bạn nhập mô-đun sqlite3 và sau đó mở một kết nối đến một tệp cơ sở dữ liệu có tên database.db, tệp này sẽ được tạo sau khi bạn chạy tệp Python. Sau đó, bạn sử dụng hàm open() để mở tệp schema.sql. Tiếp theo, bạn thực thi nội dung của nó bằng phương thức executescript() để thực thi nhiều câu lệnh SQL cùng lúc, điều này sẽ tạo bảng posts. Bạn tạo một đối tượng Cursor cho phép bạn sử dụng phương thức execute() của nó để thực thi hai câu lệnh SQL INSERT để thêm hai bài đăng blog vào bảng posts của bạn. Cuối cùng, bạn commit các thay đổi và đóng kết nối.

Lưu và đóng tệp, sau đó chạy nó trong terminal bằng lệnh python:

python init_db.py

Khi tệp kết thúc thực thi, một tệp mới có tên database.db sẽ xuất hiện trong thư mục flask_blog của bạn. Điều này có nghĩa là bạn đã thiết lập cơ sở dữ liệu thành công.

Trong bước tiếp theo, bạn sẽ truy xuất các bài đăng bạn đã chèn vào cơ sở dữ liệu và hiển thị chúng trên trang chủ ứng dụng của bạn.

Bước 5: Hiển thị tất cả bài đăng

Bây giờ bạn đã thiết lập cơ sở dữ liệu của mình, bạn có thể sửa đổi hàm xem index() để hiển thị tất cả các bài đăng bạn có trong cơ sở dữ liệu.

Mở tệp app.py để thực hiện các sửa đổi sau:

nano app.py

Đối với sửa đổi đầu tiên của bạn, bạn sẽ nhập mô-đun sqlite3 ở đầu tệp:

import sqlite3
from flask import Flask, render_template

. . .

Tiếp theo, bạn sẽ tạo một hàm tạo kết nối cơ sở dữ liệu và trả về nó.

. . .
from flask import Flask, render_template

def get_db_connection():
    conn = sqlite3.connect('database.db')
    conn.row_factory = sqlite3.Row
    return conn

. . .

Hàm get_db_connection() này mở một kết nối đến tệp cơ sở dữ liệu database.db, sau đó đặt thuộc tính row_factory thành sqlite3.Row để bạn có thể truy cập các cột dựa trên tên. Điều này có nghĩa là kết nối cơ sở dữ liệu sẽ trả về các hàng hoạt động giống như các từ điển Python thông thường. Cuối cùng, hàm trả về đối tượng kết nối conn mà bạn sẽ sử dụng để truy cập cơ sở dữ liệu.

Sau khi định nghĩa hàm get_db_connection(), sửa đổi hàm index() để trông như sau:

. . .

@app.route('/')
def index():
    conn = get_db_connection()
    posts = conn.execute('SELECT * FROM posts').fetchall()
    conn.close()
    return render_template('index.html', posts=posts)

Trong phiên bản mới này của hàm index(), bạn đầu tiên mở một kết nối cơ sở dữ liệu bằng hàm get_db_connection() bạn đã định nghĩa trước đó. Sau đó, bạn thực thi một truy vấn SQL để chọn tất cả các mục từ bảng posts. Bạn triển khai phương thức fetchall() để lấy tất cả các hàng của kết quả truy vấn, điều này sẽ trả về một danh sách các bài đăng bạn đã chèn vào cơ sở dữ liệu ở bước trước.

Bạn đóng kết nối cơ sở dữ liệu bằng phương thức close() và trả về kết quả hiển thị mẫu index.html. Bạn cũng truyền đối tượng posts làm đối số, đối tượng này chứa các kết quả bạn nhận được từ cơ sở dữ liệu, điều này sẽ cho phép bạn truy cập các bài đăng blog trong mẫu index.html.

Với những sửa đổi này, lưu và đóng tệp app.py.

Bây giờ bạn đã truyền các bài đăng bạn đã lấy từ cơ sở dữ liệu đến mẫu index.html, bạn có thể sử dụng một vòng lặp for để hiển thị mỗi bài đăng trên trang index của mình.

Mở tệp index.html:

nano templates/index.html

Sau đó, sửa đổi nó để trông như sau:

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
    {% for post in posts %}
        <a href="#">
            <h2>{{ post['title'] }}</h2>
        </a>
        <span class="badge badge-primary">{{ post['created'] }}</span>
        <hr>
    {% endfor %}
{% endblock %}

Ở đây, cú pháp {% for post in posts %} là một vòng lặp for của Jinja, tương tự như một vòng lặp for của Python ngoại trừ việc nó phải được đóng lại sau đó bằng cú pháp {% endfor %}. Bạn sử dụng cú pháp này để lặp qua từng mục trong danh sách posts đã được truyền bởi hàm index() trong dòng return render_template('index.html', posts=posts). Bên trong vòng lặp for này, bạn hiển thị tiêu đề bài đăng trong một tiêu đề <h2> bên trong một thẻ <a> (bạn sẽ sử dụng thẻ này sau để liên kết đến từng bài đăng riêng lẻ).

Bạn hiển thị tiêu đề bằng cách sử dụng bộ phân cách biến nguyên văn ({{ ... }}). Hãy nhớ rằng post sẽ là một đối tượng giống từ điển, vì vậy bạn có thể truy cập tiêu đề bài đăng bằng post['title']. Bạn cũng hiển thị ngày tạo bài đăng bằng cùng một phương pháp.

Khi bạn đã chỉnh sửa xong tệp, hãy lưu và đóng nó. Sau đó điều hướng đến trang index trong trình duyệt của bạn. Bạn sẽ thấy hai bài đăng bạn đã thêm vào cơ sở dữ liệu trên trang của mình.

Bây giờ bạn đã sửa đổi hàm xem index() để hiển thị tất cả các bài đăng bạn có trong cơ sở dữ liệu trên trang chủ ứng dụng của bạn, bạn sẽ chuyển sang hiển thị từng bài đăng trong một trang riêng và cho phép người dùng liên kết đến từng bài đăng riêng lẻ.

Bước 6: Hiển thị một bài đăng duy nhất

Trong bước này, chúng ta sẽ tạo một tuyến Flask mới với một hàm xem và một mẫu HTML mới để hiển thị một bài đăng blog riêng lẻ theo ID của nó.

Đến cuối bước này, URL http://127.0.0.1:5000/1 sẽ là một trang hiển thị bài đăng đầu tiên (vì nó có ID 1). URL http://127.0.0.1:5000/ID sẽ hiển thị bài đăng có số ID liên quan nếu nó tồn tại.

Mở app.py để chỉnh sửa:

nano app.py

Vì bạn sẽ cần lấy một bài đăng blog bằng ID của nó từ cơ sở dữ liệu ở nhiều vị trí khác nhau sau này trong dự án này, bạn sẽ tạo một hàm độc lập có tên get_post(). Bạn có thể gọi nó bằng cách truyền cho nó một ID và nhận lại bài đăng blog liên quan đến ID được cung cấp, hoặc để Flask phản hồi bằng thông báo 404 Not Found nếu bài đăng blog không tồn tại.

Để phản hồi bằng trang 404, bạn cần nhập hàm abort() từ thư viện Werkzeug, đã được cài đặt cùng với Flask, ở đầu tệp:

import sqlite3
from flask import Flask, render_template
from werkzeug.exceptions import abort

. . .

Sau đó, thêm hàm get_post() ngay sau hàm get_db_connection() bạn đã tạo ở bước trước:

. . .

def get_db_connection():
    conn = sqlite3.connect('database.db')
    conn.row_factory = sqlite3.Row
    return conn

def get_post(post_id):
    conn = get_db_connection()
    post = conn.execute('SELECT * FROM posts WHERE id = ?',
                        (post_id,)).fetchone()
    conn.close()
    if post is None:
        abort(404)
    return post

. . .

Hàm mới này có đối số post_id để xác định bài đăng blog nào sẽ trả về.

Bên trong hàm, bạn sử dụng hàm get_db_connection() để mở kết nối cơ sở dữ liệu và thực thi một truy vấn SQL để lấy bài đăng blog liên quan đến giá trị post_id đã cho. Bạn thêm phương thức fetchone() để lấy kết quả và lưu trữ nó trong biến post sau đó đóng kết nối. Nếu biến post có giá trị None, nghĩa là không tìm thấy kết quả nào trong cơ sở dữ liệu, bạn sử dụng hàm abort() bạn đã nhập trước đó để phản hồi bằng mã lỗi 404 và hàm sẽ kết thúc thực thi. Tuy nhiên, nếu tìm thấy một bài đăng, bạn trả về giá trị của biến post.

Tiếp theo, thêm hàm xem sau vào cuối tệp app.py:

. . .

@app.route('/<int:post_id>')
def post(post_id):
    post = get_post(post_id)
    return render_template('post.html', post=post)

Trong hàm xem mới này, bạn thêm một quy tắc biến <int:post_id> để chỉ định rằng phần sau dấu gạch chéo (/) là một số nguyên dương (được đánh dấu bằng bộ chuyển đổi int) mà bạn cần truy cập trong hàm xem của mình. Flask nhận ra điều này và truyền giá trị của nó cho đối số từ khóa post_id của hàm xem post() của bạn. Sau đó, bạn sử dụng hàm get_post() để lấy bài đăng blog liên quan đến ID được chỉ định và lưu trữ kết quả trong biến post, biến này bạn truyền đến một mẫu post.html mà bạn sẽ sớm tạo.

Lưu tệp app.py và mở một tệp mẫu post.html mới để chỉnh sửa:

nano templates/post.html

Gõ mã sau vào tệp post.html mới này. Điều này sẽ tương tự như tệp index.html, ngoại trừ việc nó sẽ chỉ hiển thị một bài đăng duy nhất, ngoài việc hiển thị nội dung của bài đăng:

{% extends 'base.html' %}

{% block content %}
    <h2>{% block title %} {{ post['title'] }} {% endblock %}</h2>
    <span class="badge badge-primary">{{ post['created'] }}</span>
    <p>{{ post['content'] }}</p>
{% endblock %}

Bạn thêm khối title mà bạn đã định nghĩa trong mẫu base.html để tiêu đề của trang phản ánh tiêu đề bài đăng được hiển thị trong một tiêu đề <h2> cùng lúc.

Lưu và đóng tệp.

Bây giờ bạn có thể điều hướng đến các URL sau để xem hai bài đăng bạn có trong cơ sở dữ liệu của mình, cùng với một trang thông báo cho người dùng rằng bài đăng blog được yêu cầu không tìm thấy (vì hiện tại không có bài đăng nào có ID số 3):

<http://127.0.0.1:5000/1>
<http://127.0.0.1:5000/2>
<http://127.0.0.1:5000/3>

Quay lại trang index, bạn sẽ làm cho mỗi tiêu đề bài đăng liên kết đến trang tương ứng của nó. Bạn sẽ làm điều này bằng cách sử dụng hàm url_for(). Đầu tiên, mở tệp mẫu index.html để chỉnh sửa:

nano templates/index.html

Sau đó thay đổi giá trị của thuộc tính href từ # thành {{ url_for('post', post_id=post['id']) }} để vòng lặp for sẽ trông chính xác như sau:

{% for post in posts %}
    <a href="{{ url_for('post', post_id=post['id']) }}">
        <h2>{{ post['title'] }}</h2>
    </a>
    <span class="badge badge-primary">{{ post['created'] }}</span>
    <hr>
{% endfor %}

Ở đây, bạn truyền 'post' cho hàm url_for() làm đối số đầu tiên. Đây là tên của hàm xem post() và vì nó chấp nhận đối số post_id, bạn cung cấp cho nó giá trị post['id']. Hàm url_for() sẽ trả về URL phù hợp cho mỗi bài đăng dựa trên ID của nó.

Lưu và đóng tệp.

Các liên kết trên trang index bây giờ sẽ hoạt động như mong đợi. Với điều này, bạn đã hoàn thành việc xây dựng phần ứng dụng chịu trách nhiệm hiển thị các bài đăng blog trong cơ sở dữ liệu của bạn. Tiếp theo, bạn sẽ thêm khả năng tạo, chỉnh sửa và xóa các bài đăng blog vào ứng dụng của bạn.

Bước 7: Sửa đổi bài đăng

Bây giờ bạn đã hoàn thành việc hiển thị các bài đăng blog có trong cơ sở dữ liệu trên ứng dụng web, bạn cần cho phép người dùng ứng dụng của bạn viết các bài đăng blog mới và thêm chúng vào cơ sở dữ liệu, chỉnh sửa các bài đăng hiện có và xóa các bài đăng không cần thiết.

Tạo một bài đăng mới

Cho đến thời điểm này, bạn có một ứng dụng hiển thị các bài đăng trong cơ sở dữ liệu của bạn nhưng không cung cấp cách nào để thêm bài đăng mới trừ khi bạn kết nối trực tiếp với cơ sở dữ liệu SQLite và thêm thủ công. Trong phần này, bạn sẽ tạo một trang nơi bạn có thể tạo một bài đăng bằng cách cung cấp tiêu đề và nội dung của nó.

Mở tệp app.py để chỉnh sửa:

nano app.py

Đầu tiên, bạn sẽ nhập những thứ sau từ khung Flask:

  • Đối tượng request toàn cục để truy cập dữ liệu yêu cầu đến sẽ được gửi qua một biểu mẫu HTML.
  • Hàm url_for() để tạo URL.
  • Hàm flash() để hiển thị thông báo khi một yêu cầu được xử lý.
  • Hàm redirect() để chuyển hướng máy khách đến một vị trí khác.

Thêm các lệnh nhập vào tệp của bạn như sau:

import sqlite3
from flask import Flask, render_template, request, url_for, flash, redirect
from werkzeug.exceptions import abort

. . .

Hàm flash() lưu trữ các thông báo nháy sáng (flashed messages) trong phiên trình duyệt của máy khách, điều này yêu cầu thiết lập một khóa bí mật (secret key). Khóa bí mật này được sử dụng để bảo mật các phiên, cho phép Flask ghi nhớ thông tin từ yêu cầu này sang yêu cầu khác, chẳng hạn như chuyển từ trang bài đăng mới sang trang index. Người dùng có thể truy cập thông tin được lưu trữ trong phiên, nhưng không thể sửa đổi nó trừ khi họ có khóa bí mật, vì vậy bạn không bao giờ được phép ai đó truy cập khóa bí mật của mình.

Để đặt một khóa bí mật, bạn sẽ thêm một cấu hình SECRET_KEY vào ứng dụng của bạn thông qua đối tượng app.config. Thêm nó ngay sau định nghĩa app trước khi định nghĩa hàm xem index():

. . .
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your secret key'

@app.route('/')
def index():
    conn = get_db_connection()
    posts = conn.execute('SELECT * FROM posts').fetchall()
    conn.close()
    return render_template('index.html', posts=posts)

. . .

Hãy nhớ rằng khóa bí mật phải là một chuỗi ngẫu nhiên dài.

Sau khi đặt một khóa bí mật, bạn sẽ tạo một hàm xem sẽ hiển thị một mẫu hiển thị một biểu mẫu bạn có thể điền vào để tạo một bài đăng blog mới. Thêm hàm mới này vào cuối tệp:

. . .

@app.route('/create', methods=('GET', 'POST'))
def create():
    return render_template('create.html')

Điều này tạo một tuyến /create chấp nhận cả yêu cầu GET và POST. Yêu cầu GET được chấp nhận theo mặc định. Để cũng chấp nhận yêu cầu POST, được gửi bởi trình duyệt khi gửi biểu mẫu, bạn sẽ truyền một tuple với các loại yêu cầu được chấp nhận cho đối số methods của decorator @app.route().

Lưu và đóng tệp.

Để tạo mẫu, mở một tệp có tên create.html bên trong thư mục templates của bạn:

nano templates/create.html

Thêm mã sau vào trong tệp mới này:

{% extends 'base.html' %}

{% block content %}
<h1>{% block title %} Create a New Post {% endblock %}</h1>

<form method="post">
    <div class="form-group">
        <label for="title">Title</label>
        <input type="text" name="title"
               placeholder="Post title" class="form-control"
               value="{{ request.form['title'] }}"></input>
    </div>

    <div class="form-group">
        <label for="content">Content</label>
        <textarea name="content" placeholder="Post content"
                  class="form-control">{{ request.form['content'] }}</textarea>
    </div>
    <div class="form-group">
        <button type="submit" class="btn btn-primary">Submit</button>
    </div>
</form>
{% endblock %}

Hầu hết mã này là HTML tiêu chuẩn. Nó sẽ hiển thị một hộp nhập cho tiêu đề bài đăng, một vùng văn bản cho nội dung bài đăng và một nút để gửi biểu mẫu.

Giá trị của đầu vào tiêu đề bài đăng là {{ request.form['title'] }} và vùng văn bản có giá trị {{ request.form['content'] }}, điều này được thực hiện để dữ liệu bạn nhập không bị mất nếu có sự cố. Ví dụ, nếu bạn viết một bài đăng dài và bạn quên đặt tiêu đề, một thông báo sẽ được hiển thị thông báo cho bạn rằng tiêu đề là bắt buộc. Điều này sẽ xảy ra mà không làm mất bài đăng bạn đã viết vì nó sẽ được lưu trữ trong đối tượng toàn cục request mà bạn có thể truy cập trong các mẫu của mình.

Bây giờ, với máy chủ phát triển đang chạy, sử dụng trình duyệt của bạn để điều hướng đến tuyến /create:

<http://127.0.0.1:5000/create>

Bạn sẽ thấy một trang “Create a New Post” với một hộp cho tiêu đề và nội dung.

Biểu mẫu này gửi yêu cầu POST đến hàm xem create() của bạn. Tuy nhiên, chưa có mã nào để xử lý yêu cầu POST trong hàm, vì vậy không có gì xảy ra sau khi điền biểu mẫu và gửi nó.

Bạn sẽ xử lý yêu cầu POST đến khi một biểu mẫu được gửi. Bạn sẽ làm điều này bên trong hàm xem create(). Bạn có thể xử lý riêng yêu cầu POST bằng cách kiểm tra giá trị của request.method. Khi giá trị của nó được đặt thành 'POST', điều đó có nghĩa là yêu cầu là yêu cầu POST, bạn sẽ tiếp tục trích xuất dữ liệu đã gửi, xác thực nó và chèn nó vào cơ sở dữ liệu của bạn.

Mở tệp app.py để chỉnh sửa:

nano app.py

Sửa đổi hàm xem create() để trông chính xác như sau:

. . .

@app.route('/create', methods=('GET', 'POST'))
def create():
    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']

        if not title:
            flash('Title is required!')
        else:
            conn = get_db_connection()
            conn.execute('INSERT INTO posts (title, content) VALUES (?, ?)',
                         (title, content))
            conn.commit()
            conn.close()
            return redirect(url_for('index'))

    return render_template('create.html')

Trong câu lệnh if, bạn đảm bảo rằng mã theo sau nó chỉ được thực thi khi yêu cầu là yêu cầu POST thông qua phép so sánh request.method == 'POST'.

Sau đó, bạn trích xuất tiêu đề và nội dung đã gửi từ đối tượng request.form, đối tượng này cho phép bạn truy cập dữ liệu biểu mẫu trong yêu cầu. Nếu tiêu đề không được cung cấp, điều kiện if not title sẽ được thỏa mãn, hiển thị thông báo cho người dùng thông báo rằng tiêu đề là bắt buộc. Mặt khác, nếu tiêu đề được cung cấp, bạn mở một kết nối với hàm get_db_connection() và chèn tiêu đề và nội dung bạn nhận được vào bảng posts.

Sau đó bạn commit các thay đổi vào cơ sở dữ liệu và đóng kết nối. Sau khi thêm bài đăng blog vào cơ sở dữ liệu, bạn chuyển hướng máy khách đến trang index bằng hàm redirect() truyền cho nó URL được tạo bởi hàm url_for() với giá trị 'index' làm đối số.

Lưu và đóng tệp.

Bây giờ, điều hướng đến tuyến /create bằng trình duyệt web của bạn:

<http://127.0.0.1:5000/create>

Điền vào biểu mẫu với tiêu đề bạn chọn và một số nội dung. Khi bạn gửi biểu mẫu, bạn sẽ thấy bài đăng mới được liệt kê trên trang index.

Cuối cùng, bạn sẽ hiển thị các thông báo nháy sáng và thêm một liên kết vào thanh điều hướng trong mẫu base.html để dễ dàng truy cập trang mới này. Mở tệp mẫu:

nano templates/base.html

Chỉnh sửa tệp bằng cách thêm một thẻ <li> mới theo sau liên kết “About” bên trong thẻ <nav>. Sau đó thêm một vòng lặp for mới ngay phía trên khối content để hiển thị các thông báo nháy sáng bên dưới thanh điều hướng. Các thông báo này có sẵn trong hàm get_flashed_messages() đặc biệt mà Flask cung cấp:

<nav class="navbar navbar-expand-md navbar-light bg-light">
    <a class="navbar-brand" href="{{ url_for('index')}}">FlaskBlog</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarNav">
        <ul class="navbar-nav">
        <li class="nav-item">
            <a class="nav-link" href="#">About</a>
        </li>
        <li class="nav-item">
            <a class="nav-link" href="{{url_for('create')}}">New Post</a>
        </li>
        </ul>
    </div>
</nav>
<div class="container">
    {% for message in get_flashed_messages() %}
        <div class="alert alert-danger">{{ message }}</div>
    {% endfor %}
    {% block content %} {% endblock %}
</div>

Lưu và đóng tệp. Thanh điều hướng bây giờ sẽ có mục “New Post” liên kết đến tuyến /create.

Chỉnh sửa một bài đăng

Để một blog được cập nhật, bạn sẽ cần có khả năng chỉnh sửa các bài đăng hiện có của mình. Phần này sẽ hướng dẫn bạn tạo một trang mới trong ứng dụng của bạn để đơn giản hóa quá trình chỉnh sửa một bài đăng.

Đầu tiên, bạn sẽ thêm một tuyến mới vào tệp app.py. Hàm xem của nó sẽ nhận ID của bài đăng cần được chỉnh sửa, URL sẽ có định dạng /post_id/edit với biến post_id là ID của bài đăng. Mở tệp app.py để chỉnh sửa:

nano app.py

Tiếp theo, thêm hàm xem edit() sau vào cuối tệp. Chỉnh sửa một bài đăng hiện có tương tự như tạo một bài đăng mới, vì vậy hàm xem này sẽ tương tự như hàm xem create():

. . .

@app.route('/<int:id>/edit', methods=('GET', 'POST'))
def edit(id):
    post = get_post(id)

    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']

        if not title:
            flash('Title is required!')
        else:
            conn = get_db_connection()
            conn.execute('UPDATE posts SET title = ?, content = ?'
                         ' WHERE id = ?',
                         (title, content, id))
            conn.commit()
            conn.close()
            return redirect(url_for('index'))

    return render_template('edit.html', post=post)

Bài đăng bạn chỉnh sửa được xác định bởi URL và Flask sẽ truyền số ID cho hàm edit() thông qua đối số id. Bạn thêm giá trị này vào hàm get_post() để lấy bài đăng liên quan đến ID được cung cấp từ cơ sở dữ liệu. Dữ liệu mới sẽ đến trong yêu cầu POST, được xử lý bên trong điều kiện if request.method == 'POST'.

Giống như khi bạn tạo một bài đăng mới, bạn đầu tiên trích xuất dữ liệu từ đối tượng request.form sau đó hiển thị thông báo nếu tiêu đề có giá trị rỗng, nếu không, bạn mở một kết nối cơ sở dữ liệu. Sau đó, bạn cập nhật bảng posts bằng cách đặt tiêu đề mới và nội dung mới nơi ID của bài đăng trong cơ sở dữ liệu bằng với ID có trong URL.

Trong trường hợp yêu cầu GET, bạn hiển thị một mẫu edit.html truyền vào biến post chứa giá trị trả về của hàm get_post(). Bạn sẽ sử dụng điều này để hiển thị tiêu đề và nội dung hiện có trên trang chỉnh sửa.

Lưu và đóng tệp, sau đó tạo một mẫu edit.html mới:

nano templates/edit.html

Viết mã sau vào trong tệp mới này:

{% extends 'base.html' %}

{% block content %}
<h1>{% block title %} Edit "{{ post['title'] }}" {% endblock %}</h1>

<form method="post">
    <div class="form-group">
        <label for="title">Title</label>
        <input type="text" name="title" placeholder="Post title"
               class="form-control"
               value="{{ request.form['title'] or post['title'] }}">
        </input>
    </div>

    <div class="form-group">
        <label for="content">Content</label>
        <textarea name="content" placeholder="Post content"
                  class="form-control">{{ request.form['content'] or post['content'] }}</textarea>
    </div>
    <div class="form-group">
        <button type="submit" class="btn btn-primary">Submit</button>
    </div>
</form>
<hr>
{% endblock %}

Lưu và đóng tệp.

Mã này tuân theo cùng một mẫu ngoại trừ cú pháp {{ request.form['title'] or post['title'] }}{{ request.form['content'] or post['content'] }}. Điều này hiển thị dữ liệu được lưu trữ trong yêu cầu nếu nó tồn tại, nếu không, nó hiển thị dữ liệu từ biến post đã được truyền cho mẫu chứa dữ liệu cơ sở dữ liệu hiện tại.

Bây giờ, điều hướng đến URL sau để chỉnh sửa bài đăng đầu tiên:

<http://127.0.0.1:5000/1/edit>

Bạn sẽ thấy một trang “Edit “First Post””.

Chỉnh sửa bài đăng và gửi biểu mẫu, sau đó đảm bảo bài đăng đã được cập nhật.

Bây giờ, bạn cần thêm một liên kết trỏ đến trang chỉnh sửa cho mỗi bài đăng trên trang index. Mở tệp mẫu index.html:

nano templates/index.html

Chỉnh sửa tệp để trông chính xác như sau:

{% extends 'base.html' %}

{% block content %}
    <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
    {% for post in posts %}
        <a href="{{ url_for('post', post_id=post['id']) }}">
            <h2>{{ post['title'] }}</h2>
        </a>
        <span class="badge badge-primary">{{ post['created'] }}</span>
        <a href="{{ url_for('edit', id=post['id']) }}">
            <span class="badge badge-warning">Edit</span>
        </a>
        <hr>
    {% endfor %}
{% endblock %}

Lưu và đóng tệp.

Ở đây bạn thêm một thẻ <a> để liên kết đến hàm xem edit(), truyền giá trị post['id'] để liên kết đến trang chỉnh sửa của mỗi bài đăng bằng liên kết “Edit”.

Xóa một bài đăng

Đôi khi một bài đăng không còn cần phải được công khai, đó là lý do tại sao chức năng xóa một bài đăng là rất quan trọng. Trong bước này, bạn sẽ thêm chức năng xóa vào ứng dụng của mình.

Đầu tiên, bạn sẽ thêm một tuyến /ID/delete mới chấp nhận yêu cầu POST, tương tự như hàm xem edit(). Hàm xem delete() mới của bạn sẽ nhận ID của bài đăng cần xóa từ URL. Mở tệp app.py:

nano app.py

Thêm hàm xem sau vào cuối tệp:

# ....

@app.route('/<int:id>/delete', methods=('POST',))
def delete(id):
    post = get_post(id)
    conn = get_db_connection()
    conn.execute('DELETE FROM posts WHERE id = ?', (id,))
    conn.commit()
    conn.close()
    flash('"{}" was successfully deleted!'.format(post['title']))
    return redirect(url_for('index'))

Hàm xem này chỉ chấp nhận yêu cầu POST. Điều này có nghĩa là việc điều hướng đến tuyến /ID/delete trên trình duyệt của bạn sẽ trả về lỗi vì các trình duyệt web mặc định là yêu cầu GET.

Tuy nhiên, bạn có thể truy cập tuyến này qua một biểu mẫu gửi yêu cầu POST truyền ID của bài đăng bạn muốn xóa. Hàm sẽ nhận giá trị ID và sử dụng nó để lấy bài đăng từ cơ sở dữ liệu bằng hàm get_post().

Sau đó, bạn mở một kết nối cơ sở dữ liệu và thực thi lệnh SQL DELETE FROM để xóa bài đăng. Bạn commit thay đổi vào cơ sở dữ liệu và đóng kết nối trong khi hiển thị thông báo cho người dùng biết rằng bài đăng đã được xóa thành công và chuyển hướng họ đến trang index.

Lưu ý rằng bạn không hiển thị một tệp mẫu, điều này là do bạn sẽ chỉ thêm một nút “Delete” vào trang chỉnh sửa.

Mở tệp mẫu edit.html:

nano templates/edit.html

Sau đó thêm thẻ <form> sau thẻ <hr> và ngay trước dòng {% endblock %}:

<hr>

<form action="{{ url_for('delete', id=post['id']) }}" method="POST">
    <input type="submit" value="Delete Post"
            class="btn btn-danger btn-sm"
            onclick="return confirm('Are you sure you want to delete this post?')">
</form>

{% endblock %}

Bạn sử dụng phương thức confirm() để hiển thị thông báo xác nhận trước khi gửi yêu cầu.

Bây giờ điều hướng lại đến trang chỉnh sửa của một bài đăng blog và thử xóa nó:

<http://127.0.0.1:5000/1/edit>

Đến cuối bước này, mã nguồn dự án của bạn sẽ trông giống như mã trên trang này.

Với điều này, người dùng ứng dụng của bạn giờ đây có thể viết các bài đăng blog mới và thêm chúng vào cơ sở dữ liệu, chỉnh sửa và xóa các bài đăng hiện có.

Kết luận

Hướng dẫn này đã giới thiệu các khái niệm thiết yếu của khung Python Flask. Bạn đã học cách tạo một ứng dụng web nhỏ, chạy nó trong một máy chủ phát triển và cho phép người dùng cung cấp dữ liệu tùy chỉnh thông qua các tham số URL và biểu mẫu web. Bạn cũng đã sử dụng công cụ tạo mẫu Jinja để tái sử dụng các tệp HTML và sử dụng logic trong chúng. Đến cuối hướng dẫn này, bạn đã có một ứng dụng blog web hoạt động đầy đủ, tương tác với cơ sở dữ liệu SQLite để tạo, hiển thị, chỉnh sửa và xóa các bài đăng blog bằng ngôn ngữ Python và các truy vấn SQL. Nếu bạn muốn tìm hiểu thêm về cách làm việc với Flask và SQLite, hãy xem hướng dẫn này về “Cách sử dụng mối quan hệ cơ sở dữ liệu Một-nhiều với Flask và SQLite”.

Bạn có thể phát triển thêm ứng dụng này bằng cách thêm xác thực người dùng để chỉ những người dùng đã đăng ký mới có thể tạo và sửa đổi các bài đăng blog. Bạn cũng có thể thêm nhận xét và thẻ cho mỗi bài đăng blog, và thêm tính năng tải tệp lên để cung cấp cho người dùng khả năng bao gồm hình ảnh trong bài đăng. Xem tài liệu Flask để biết thêm thông tin.

Flask có nhiều phần mở rộng (extensions) do cộng đồng tạo ra. Sau đây là danh sách các phần mở rộng bạn có thể cân nhắc sử dụng để quá trình phát triển của bạn dễ dàng hơn:

  • Flask-Login: quản lý phiên người dùng và xử lý việc đăng nhập, đăng xuất và ghi nhớ người dùng đã đăng nhập.
  • Flask-SQLAlchemy: đơn giản hóa việc sử dụng Flask với SQLAlchemy, một bộ công cụ SQL Python và Object Relational Mapper (ORM) để tương tác với các cơ sở dữ liệu SQL.
  • Flask-Mail: giúp bạn thực hiện tác vụ gửi tin nhắn email trong ứng dụng Flask của bạn.

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

1. Bạn có thể tạo một trang web bằng Flask không?

Có, Flask là một framework tuyệt vời để xây dựng các trang web và ứng dụng web. Nó đặc biệt phù hợp cho các dự án nhỏ đến vừa, như đã được chứng minh trong hướng dẫn này khi chúng ta xây dựng một blog hoạt động đầy đủ. Flask cung cấp tất cả các công cụ thiết yếu cần thiết để tạo ứng dụng web, từ định tuyến và hiển thị mẫu đến xử lý biểu mẫu và cơ sở dữ liệu.

2. Flask là frontend hay backend?

Flask là framework làm việc backend. Nó xử lý các hoạt động phía máy chủ như xử lý yêu cầu, quản lý cơ sở dữ liệu và phục vụ nội dung. Mặc dù Flask có thể hiển thị các mẫu HTML (như đã trình bày trong hướng dẫn này), nó chủ yếu tập trung vào logic backend. Đối với phát triển frontend, nó thường được kết hợp với HTML, CSS và JavaScript, thường sử dụng các khung làm việc frontend như Bootstrap (như chúng ta đã làm trong hướng dẫn này).

3. Flask có lỗi thời không?

Không, Flask không lỗi thời. Nó vẫn là một trong những framework web Python phổ biến nhất, được duy trì tích cực với các bản cập nhật thường xuyên. Sự đơn giản, linh hoạt và hệ sinh thái mở rộng của các phần mở rộng làm cho nó trở thành một lựa chọn phù hợp cho phát triển web hiện đại. Bản phát hành ổn định mới nhất vẫn tiếp tục nhận được các bản cập nhật và vá lỗi bảo mật.

4. Có trang web lớn nào sử dụng Flask không?

Có, một số công ty đáng chú ý sử dụng Flask:

  • Pinterest sử dụng Flask cho API của họ.
  • Netflix sử dụng Flask cho một số ứng dụng nội bộ của họ.
  • LinkedIn sử dụng Flask cho một số dịch vụ nhất định.
  • Reddit đã sử dụng Flask cho nhiều thành phần khác nhau. Các công ty này chứng minh khả năng của Flask trong việc xử lý các ứng dụng quy mô lớn khi được kiến trúc đúng cách.

5. Flask hay Django tốt hơn?

Không có cái nào tốt hơn tuyệt đối – chúng phục vụ các mục đích khác nhau:

  • Flask lý tưởng cho các dự án nhỏ hơn, microservices và khi bạn muốn kiểm soát nhiều hơn kiến trúc ứng dụng của mình.
  • Django phù hợp hơn cho các dự án lớn hơn cần các tính năng tích hợp sẵn và tuân theo một cấu trúc có quy tắc hơn.

Chọn Flask nếu bạn:

  • Muốn một khung làm việc nhỏ gọn.
  • Cần sự linh hoạt trong các lựa chọn công nghệ của mình.
  • Đang xây dựng một ứng dụng nhỏ đến vừa.
  • Ưu tiên thêm chức năng khi cần thiết.

Chọn Django nếu bạn:

  • Cần một khung làm việc đầy đủ tính năng ngay từ đầu.
  • Đang xây dựng một ứng dụng quy mô lớn.
  • Muốn giao diện quản trị và xác thực tích hợp sẵn.
  • Ưu tiên quy ước hơn cấu hình.

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.

Đăng ký nhận Newsletter

Nhận các nội dung hữu ích mới nhất