SQLite với Flask Python: Hướng dẫn toàn diện cho người mới bắt đầu

Flask, một framework web siêu nhỏ của Python, kết hợp với SQLite, một hệ quản trị cơ sở dữ liệu (CSDL) nhẹ, là một bộ đôi hoàn hảo để xây dựng các ứng dụng web nhỏ đến trung bình một cách nhanh chóng và hiệu quả. Bài viết này sẽ hướng dẫn bạn từ A đến Z cách sử dụng Sqlite Với Flask Python, từ thiết lập ban đầu đến các thao tác CSDL nâng cao, giúp bạn nắm vững kiến thức và kỹ năng cần thiết để tạo ra các ứng dụng web mạnh mẽ.

Flask và SQLite phối hợp nhịp nhàng như thế nào? Tại sao chúng lại trở thành lựa chọn phổ biến cho các dự án nhỏ và vừa? Hãy cùng khám phá!

Tại sao chọn SQLite với Flask Python?

Có rất nhiều lựa chọn CSDL ngoài kia như MySQL, PostgreSQL, MongoDB,… vậy tại sao chúng ta lại tập trung vào SQLite? Câu trả lời nằm ở sự đơn giản, tiện lợi và hiệu suất của nó, đặc biệt khi kết hợp với sự linh hoạt của Flask:

  • Dễ dàng thiết lập: SQLite không yêu cầu cài đặt server riêng biệt. CSDL được lưu trữ trong một file duy nhất, giúp việc thiết lập và triển khai ứng dụng trở nên cực kỳ đơn giản.
  • Không cần cấu hình: Bạn không cần phải cấu hình phức tạp như các hệ quản trị CSDL khác. Chỉ cần import thư viện sqlite3 trong Python là có thể bắt đầu sử dụng.
  • Nhẹ nhàng: SQLite tiêu thụ ít tài nguyên hệ thống, phù hợp với các ứng dụng có yêu cầu về hiệu năng không quá cao.
  • Tính di động cao: File CSDL SQLite có thể dễ dàng di chuyển giữa các hệ điều hành khác nhau.
  • Miễn phí và mã nguồn mở: Bạn có thể sử dụng SQLite hoàn toàn miễn phí mà không phải lo lắng về chi phí bản quyền.

Chuyên gia phát triển web Nguyễn Văn An chia sẻ: “SQLite là lựa chọn tuyệt vời cho các dự án cá nhân, prototype hoặc các ứng dụng có số lượng người dùng không quá lớn. Sự đơn giản và tiện lợi của nó giúp tôi tập trung vào logic ứng dụng hơn là cấu hình CSDL.”

Thiết lập môi trường phát triển

Trước khi bắt đầu, bạn cần đảm bảo đã cài đặt Python và pip (trình quản lý gói của Python). Sau đó, hãy thực hiện các bước sau:

  1. Tạo một thư mục dự án:

    mkdir flask_sqlite_app
    cd flask_sqlite_app
  2. Tạo môi trường ảo (virtual environment): Môi trường ảo giúp bạn quản lý các gói Python một cách độc lập cho từng dự án.

    python3 -m venv venv
  3. Kích hoạt môi trường ảo:

    • Trên Linux/macOS:

      source venv/bin/activate
    • Trên Windows:

      venvScriptsactivate
  4. Cài đặt Flask:

    pip install flask
  5. Cài đặt thư viện cần thiết cho SQLite (thường đã có sẵn): Hầu hết các bản cài đặt Python đã bao gồm thư viện sqlite3. Nếu chưa có, bạn có thể cài đặt bằng:

    pip install pysqlite3

Kết nối SQLite với Flask

Bây giờ, chúng ta sẽ tạo một ứng dụng Flask đơn giản và kết nối nó với SQLite.

  1. Tạo file app.py: Đây là file chính của ứng dụng Flask.

    from flask import Flask, g
    import sqlite3
    
    app = Flask(__name__)
    app.config['DATABASE'] = 'database.db' # Tên file CSDL
    
    def get_db():
        db = getattr(g, '_database', None)
        if db is None:
            db = g._database = sqlite3.connect(app.config['DATABASE'])
        return db
    
    @app.teardown_appcontext
    def close_connection(exception):
        db = getattr(g, '_database', None)
        if db is not None:
            db.close()
    
    @app.route('/')
    def index():
        return "Xin chào! Flask đã kết nối với SQLite!"
    
    if __name__ == '__main__':
        app.run(debug=True)
  2. Giải thích đoạn code:

    • app.config['DATABASE'] = 'database.db': Thiết lập tên file CSDL là database.db.
    • get_db(): Hàm này trả về kết nối đến CSDL. Nó sử dụng flask.g để lưu trữ kết nối trong một request duy nhất. Nếu kết nối chưa tồn tại, nó sẽ tạo một kết nối mới.
    • close_connection(): Hàm này đóng kết nối đến CSDL sau khi request được xử lý.
    • @app.route('/'): Định nghĩa route cho trang chủ (/).
    • index(): Hàm xử lý request đến trang chủ và trả về một chuỗi.
  3. Chạy ứng dụng:

    python app.py

    Truy cập http://127.0.0.1:5000/ (hoặc địa chỉ hiển thị trên terminal) trong trình duyệt, bạn sẽ thấy dòng chữ “Xin chào! Flask đã kết nối với SQLite!”. Một file database.db sẽ được tạo trong thư mục dự án của bạn.

Tạo bảng và thực hiện các thao tác CRUD (Create, Read, Update, Delete)

Tiếp theo, chúng ta sẽ tạo một bảng trong CSDL và thực hiện các thao tác CRUD cơ bản.

  1. Tạo file schema.sql: File này chứa các câu lệnh SQL để tạo bảng.

    DROP TABLE IF EXISTS users;
    
    CREATE TABLE users (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      username TEXT NOT NULL,
      email TEXT NOT NULL,
      password TEXT NOT NULL
    );
  2. Thêm hàm khởi tạo CSDL vào app.py:

    def init_db():
        with app.app_context():
            db = get_db()
            with app.open_resource('schema.sql', mode='r') as f:
                db.cursor().executescript(f.read())
            db.commit()
    
    # Gọi hàm init_db() để tạo bảng
    with app.app_context():
        init_db()
    • app.open_resource('schema.sql', mode='r'): Mở file schema.sql để đọc.
    • db.cursor().executescript(f.read()): Thực thi các câu lệnh SQL trong file.
    • db.commit(): Lưu các thay đổi vào CSDL.
  3. Thêm các route để thực hiện các thao tác CRUD:

    from flask import Flask, g, request, jsonify
    import sqlite3
    
    app = Flask(__name__)
    app.config['DATABASE'] = 'database.db'
    
    def get_db():
        db = getattr(g, '_database', None)
        if db is None:
            db = g._database = sqlite3.connect(app.config['DATABASE'])
        return db
    
    @app.teardown_appcontext
    def close_connection(exception):
        db = getattr(g, '_database', None)
        if db is not None:
            db.close()
    
    def init_db():
        with app.app_context():
            db = get_db()
            with app.open_resource('schema.sql', mode='r') as f:
                db.cursor().executescript(f.read())
            db.commit()
    
    # with app.app_context():
    #     init_db() # Chỉ chạy một lần khi khởi tạo CSDL
    
    @app.route('/')
    def index():
        return "Xin chào! Flask đã kết nối với SQLite!"
    
    # Create (Tạo mới người dùng)
    @app.route('/users', methods=['POST'])
    def create_user():
        data = request.get_json()
        username = data['username']
        email = data['email']
        password = data['password']
        db = get_db()
        db.execute('INSERT INTO users (username, email, password) VALUES (?, ?, ?)', (username, email, password))
        db.commit()
        return jsonify({'message': 'Người dùng đã được tạo thành công!'}), 201
    
    # Read (Lấy danh sách người dùng)
    @app.route('/users', methods=['GET'])
    def get_users():
        db = get_db()
        cur = db.execute('SELECT id, username, email FROM users')
        users = cur.fetchall()
        user_list = []
        for user in users:
            user_list.append({'id': user[0], 'username': user[1], 'email': user[2]})
        return jsonify({'users': user_list})
    
    # Read (Lấy thông tin một người dùng theo ID)
    @app.route('/users/<int:user_id>', methods=['GET'])
    def get_user(user_id):
        db = get_db()
        cur = db.execute('SELECT id, username, email FROM users WHERE id = ?', (user_id,))
        user = cur.fetchone()
        if user is None:
            return jsonify({'message': 'Không tìm thấy người dùng!'}), 404
        return jsonify({'id': user[0], 'username': user[1], 'email': user[2]})
    
    # Update (Cập nhật thông tin người dùng)
    @app.route('/users/<int:user_id>', methods=['PUT'])
    def update_user(user_id):
        data = request.get_json()
        username = data['username']
        email = data['email']
        password = data['password']
        db = get_db()
        db.execute('UPDATE users SET username = ?, email = ?, password = ? WHERE id = ?', (username, email, password, user_id))
        db.commit()
        return jsonify({'message': 'Thông tin người dùng đã được cập nhật!'})
    
    # Delete (Xóa người dùng)
    @app.route('/users/<int:user_id>', methods=['DELETE'])
    def delete_user(user_id):
        db = get_db()
        db.execute('DELETE FROM users WHERE id = ?', (user_id,))
        db.commit()
        return jsonify({'message': 'Người dùng đã được xóa!'})
    
    if __name__ == '__main__':
        # init_db() # Đảm bảo chạy init_db() một lần duy nhất khi khởi tạo CSDL
        app.run(debug=True)
  4. Giải thích các route CRUD:

    • POST /users: Tạo một người dùng mới. Nhận dữ liệu từ request body (JSON) và lưu vào CSDL.
    • GET /users: Lấy danh sách tất cả người dùng.
    • GET /users/<int:user_id>: Lấy thông tin của một người dùng cụ thể dựa trên ID.
    • PUT /users/<int:user_id>: Cập nhật thông tin của một người dùng cụ thể.
    • DELETE /users/<int:user_id>: Xóa một người dùng cụ thể.
  5. Sử dụng Postman hoặc cURL để kiểm tra các API: Bạn có thể sử dụng các công cụ như Postman hoặc cURL để gửi các request đến các API và kiểm tra xem chúng hoạt động đúng hay không.

Sử dụng thư viện ORM (Object-Relational Mapping)

Mặc dù việc thao tác trực tiếp với SQL không quá phức tạp, nhưng việc sử dụng một thư viện ORM như SQLAlchemy có thể giúp bạn viết code dễ đọc, dễ bảo trì và an toàn hơn. ORM cho phép bạn tương tác với CSDL bằng các đối tượng Python, thay vì phải viết các câu lệnh SQL.

  1. Cài đặt SQLAlchemy:

    pip install flask-sqlalchemy
  2. Cấu hình SQLAlchemy trong app.py:

    from flask import Flask, jsonify
    from flask_sqlalchemy import SQLAlchemy
    
    app = Flask(__name__)
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db' # Đường dẫn đến CSDL
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # Tắt cảnh báo
    db = SQLAlchemy(app)
    
    # Định nghĩa model User
    class User(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        username = db.Column(db.String(80), unique=True, nullable=False)
        email = db.Column(db.String(120), unique=True, nullable=False)
    
        def __repr__(self):
            return '<User %r>' % self.username
    
    # Tạo bảng (chỉ chạy một lần)
    with app.app_context():
        db.create_all()
    
    @app.route('/users', methods=['GET'])
    def get_users():
        users = User.query.all()
        user_list = []
        for user in users:
            user_list.append({'id': user.id, 'username': user.username, 'email': user.email})
        return jsonify({'users': user_list})
    
    if __name__ == '__main__':
        app.run(debug=True)
  3. Giải thích đoạn code:

    • app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db': Thiết lập đường dẫn đến CSDL.
    • app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False: Tắt cảnh báo (không bắt buộc).
    • db = SQLAlchemy(app): Khởi tạo SQLAlchemy.
    • class User(db.Model): Định nghĩa model User, tương ứng với bảng users trong CSDL.
    • db.Column(): Định nghĩa các cột trong bảng.
    • db.create_all(): Tạo tất cả các bảng đã được định nghĩa (chỉ chạy một lần).
    • User.query.all(): Lấy tất cả các người dùng từ CSDL.
  4. Thực hiện các thao tác CRUD với SQLAlchemy: Bạn có thể dễ dàng thực hiện các thao tác CRUD bằng cách sử dụng các phương thức của SQLAlchemy. Ví dụ:

    • Tạo mới người dùng:

      from flask import Flask, request, jsonify
      from flask_sqlalchemy import SQLAlchemy
      
      app = Flask(__name__)
      app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'
      app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
      db = SQLAlchemy(app)
      
      class User(db.Model):
          id = db.Column(db.Integer, primary_key=True)
          username = db.Column(db.String(80), unique=True, nullable=False)
          email = db.Column(db.String(120), unique=True, nullable=False)
      
          def __repr__(self):
              return '<User %r>' % self.username
      
      with app.app_context():
          db.create_all()
      
      @app.route('/users', methods=['POST'])
      def create_user():
          data = request.get_json()
          username = data['username']
          email = data['email']
          new_user = User(username=username, email=email)
          db.session.add(new_user)
          db.session.commit()
          return jsonify({'message': 'Người dùng đã được tạo thành công!'}), 201
      
      if __name__ == '__main__':
          app.run(debug=True)
    • Lấy thông tin người dùng:

      from flask import Flask, request, jsonify
      from flask_sqlalchemy import SQLAlchemy
      
      app = Flask(__name__)
      app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///database.db'
      app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
      db = SQLAlchemy(app)
      
      class User(db.Model):
          id = db.Column(db.Integer, primary_key=True)
          username = db.Column(db.String(80), unique=True, nullable=False)
          email = db.Column(db.String(120), unique=True, nullable=False)
      
          def __repr__(self):
              return '<User %r>' % self.username
      
      with app.app_context():
          db.create_all()
      
      @app.route('/users/<int:user_id>', methods=['GET'])
      def get_user(user_id):
          user = User.query.get(user_id)
          if user is None:
              return jsonify({'message': 'Không tìm thấy người dùng!'}), 404
          return jsonify({'id': user.id, 'username': user.username, 'email': user.email})
      
      if __name__ == '__main__':
          app.run(debug=True)

Bạn có thể tìm hiểu thêm về SQLAlchemy trong tài liệu chính thức: https://www.sqlalchemy.org/

Bảo mật ứng dụng Flask với SQLite

Bảo mật là một yếu tố quan trọng cần được quan tâm khi xây dựng bất kỳ ứng dụng web nào. Dưới đây là một số biện pháp bảo mật quan trọng khi sử dụng Flask với SQLite:

  • Chống tấn công SQL Injection: Tránh sử dụng string formatting trực tiếp để xây dựng các câu lệnh SQL. Thay vào đó, hãy sử dụng parameterized queries (truy vấn tham số hóa) để ngăn chặn tấn công SQL Injection.

    # Không an toàn:
    # username = request.form['username']
    # query = "SELECT * FROM users WHERE username = '" + username + "'"
    
    # An toàn:
    username = request.form['username']
    query = "SELECT * FROM users WHERE username = ?"
    cursor.execute(query, (username,))
  • Bảo vệ mật khẩu: Không bao giờ lưu trữ mật khẩu dưới dạng plain text. Hãy sử dụng các thuật toán băm (hashing) mạnh mẽ như bcrypt hoặc Argon2 để băm mật khẩu trước khi lưu vào CSDL.

    import bcrypt
    
    # Băm mật khẩu
    password = request.form['password']
    hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
    
    # Kiểm tra mật khẩu
    if bcrypt.checkpw(password.encode('utf-8'), hashed_password):
        # Mật khẩu hợp lệ
        pass
    else:
        # Mật khẩu không hợp lệ
        pass
  • Xác thực và phân quyền: Triển khai hệ thống xác thực (authentication) để xác minh danh tính người dùng và hệ thống phân quyền (authorization) để kiểm soát quyền truy cập vào các tài nguyên khác nhau.

  • Kiểm soát dữ liệu đầu vào: Luôn kiểm tra và làm sạch dữ liệu đầu vào từ người dùng để tránh các lỗ hổng bảo mật như Cross-Site Scripting (XSS).

  • Giới hạn quyền truy cập file CSDL: Đảm bảo rằng file CSDL SQLite chỉ có thể được truy cập bởi ứng dụng Flask và không ai khác.

Chuyên gia bảo mật Trần Thị Mai nhận định: “Bảo mật không phải là một tính năng tùy chọn mà là một phần không thể thiếu của bất kỳ ứng dụng web nào. Việc áp dụng các biện pháp bảo mật cơ bản có thể giúp bạn ngăn chặn nhiều cuộc tấn công phổ biến.”

Triển khai ứng dụng Flask với SQLite

Sau khi hoàn thành việc phát triển ứng dụng Flask với SQLite, bạn cần triển khai nó lên một server để người dùng có thể truy cập. Dưới đây là một số lựa chọn triển khai phổ biến:

  • Heroku: Heroku là một nền tảng đám mây (cloud platform) cho phép bạn triển khai ứng dụng web một cách dễ dàng. Bạn có thể sử dụng Heroku CLI để triển khai ứng dụng Flask với SQLite.
  • PythonAnywhere: PythonAnywhere là một nền tảng đám mây khác được thiết kế đặc biệt cho các ứng dụng Python. Nó cung cấp một môi trường phát triển và triển khai dễ sử dụng.
  • AWS (Amazon Web Services): AWS cung cấp nhiều dịch vụ khác nhau để triển khai ứng dụng web, bao gồm EC2 (máy ảo), Elastic Beanstalk (nền tảng PaaS) và Lambda (serverless).
  • Google Cloud Platform (GCP): GCP cũng cung cấp các dịch vụ tương tự như AWS, bao gồm Compute Engine (máy ảo), App Engine (nền tảng PaaS) và Cloud Functions (serverless).
  • DigitalOcean: DigitalOcean là một nhà cung cấp dịch vụ đám mây tập trung vào việc cung cấp các máy ảo (droplets) với giá cả phải chăng.

Khi triển khai ứng dụng, bạn cần lưu ý một số điều sau:

  • Cấu hình môi trường: Đảm bảo rằng môi trường triển khai đã được cấu hình đúng cách, bao gồm việc cài đặt Python, Flask và các thư viện cần thiết.
  • Thiết lập biến môi trường: Sử dụng biến môi trường để lưu trữ các thông tin cấu hình nhạy cảm như khóa API, mật khẩu CSDL,…
  • Sao lưu CSDL: Thường xuyên sao lưu CSDL để đảm bảo an toàn dữ liệu.
  • Giám sát ứng dụng: Theo dõi hiệu suất và nhật ký của ứng dụng để phát hiện và giải quyết các vấn đề kịp thời.

SQLite có phù hợp với mọi dự án Flask?

Mặc dù SQLite là một lựa chọn tuyệt vời cho nhiều dự án Flask, nhưng nó không phải là giải pháp phù hợp cho tất cả mọi trường hợp. Dưới đây là một số hạn chế của SQLite:

  • Khả năng mở rộng: SQLite không được thiết kế để xử lý số lượng lớn người dùng đồng thời hoặc dữ liệu lớn. Nếu ứng dụng của bạn có yêu cầu về khả năng mở rộng cao, bạn nên cân nhắc sử dụng một hệ quản trị CSDL mạnh mẽ hơn như PostgreSQL hoặc MySQL.
  • Đồng thời: SQLite chỉ hỗ trợ một kết nối ghi tại một thời điểm. Điều này có thể gây ra vấn đề về hiệu suất nếu ứng dụng của bạn có nhiều tác vụ ghi đồng thời.
  • Tính năng nâng cao: SQLite thiếu một số tính năng nâng cao có sẵn trong các hệ quản trị CSDL khác, chẳng hạn như stored procedures, triggers và views.
  • Môi trường server: [sqlite có chạy được trên docker không](https://mekong.wiki/co-so-du-lieu/sqlite/sqlite-co-chay-duoc-tren-docker-khong/)? SQLite có thể chạy trên Docker, nhưng cần cẩn thận với việc quản lý file CSDL.

Tổng kết lại, việc lựa chọn hệ quản trị CSDL phù hợp phụ thuộc vào yêu cầu cụ thể của dự án. SQLite là một lựa chọn tốt cho các dự án nhỏ đến trung bình, trong khi các hệ quản trị CSDL khác phù hợp hơn cho các dự án lớn và phức tạp.

Kết luận

Việc sử dụng SQLite với Flask Python là một cách tuyệt vời để xây dựng các ứng dụng web một cách nhanh chóng và dễ dàng. Với sự đơn giản, tiện lợi và hiệu suất của SQLite, kết hợp với sự linh hoạt của Flask, bạn có thể tạo ra các ứng dụng web mạnh mẽ mà không cần phải lo lắng về việc cấu hình CSDL phức tạp. Tuy nhiên, hãy nhớ rằng SQLite có một số hạn chế và có thể không phù hợp với tất cả các dự án. Hãy đánh giá kỹ lưỡng yêu cầu của dự án của bạn trước khi đưa ra quyết định. Hy vọng bài viết này đã cung cấp cho bạn những kiến thức và kỹ năng cần thiết để bắt đầu sử dụng SQLite với Flask Python. Chúc bạn thành công!

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

  1. SQLite có miễn phí không?

    Có, SQLite là hoàn toàn miễn phí và mã nguồn mở. Bạn có thể sử dụng nó cho cả mục đích cá nhân và thương mại mà không phải trả bất kỳ chi phí nào.

  2. Tôi nên sử dụng SQLite hay MySQL/PostgreSQL?

    Sự lựa chọn phụ thuộc vào yêu cầu của dự án của bạn. SQLite phù hợp cho các dự án nhỏ đến trung bình, trong khi MySQL/PostgreSQL phù hợp hơn cho các dự án lớn và phức tạp.

  3. Làm thế nào để bảo mật ứng dụng Flask với SQLite?

    Sử dụng parameterized queries để chống tấn công SQL Injection, băm mật khẩu trước khi lưu vào CSDL, triển khai hệ thống xác thực và phân quyền, kiểm soát dữ liệu đầu vào và giới hạn quyền truy cập file CSDL.

  4. Tôi có thể sử dụng SQLite trong môi trường production không?

    Có, bạn có thể sử dụng SQLite trong môi trường production cho các ứng dụng có số lượng người dùng không quá lớn và yêu cầu về hiệu năng không quá cao. Tuy nhiên, bạn cần lưu ý đến các hạn chế của SQLite về khả năng mở rộng và đồng thời.

  5. Làm thế nào để triển khai ứng dụng Flask với SQLite?

    Bạn có thể sử dụng các nền tảng đám mây như Heroku, PythonAnywhere, AWS, GCP hoặc DigitalOcean để triển khai ứng dụng Flask với SQLite.

  6. ORM là gì và tại sao tôi nên sử dụng nó?

    ORM (Object-Relational Mapping) là một kỹ thuật cho phép bạn tương tác với CSDL bằng các đối tượng Python, thay vì phải viết các câu lệnh SQL. Sử dụng ORM giúp bạn viết code dễ đọc, dễ bảo trì và an toàn hơn.

  7. Tôi có thể sử dụng SQLAlchemy với SQLite không?

    Có, bạn hoàn toàn có thể sử dụng SQLAlchemy, một thư viện ORM phổ biến, với SQLite. SQLAlchemy cung cấp một cách trừu tượng hóa mạnh mẽ để tương tác với CSDL, giúp bạn viết code dễ dàng hơn và tránh được các lỗi phổ biến.