Hướng dẫn flask mysql - bình mysql

Trong phần này, chúng ta sẽ cùng tìm hiểu về cách sử dụng cơ sở dữ liệu với ứng dụng Flask.

Show

Để giúp cho bạn dễ theo dõi, sau đây là danh sách các bài viết trong loạt bài hướng dẫn này:

  • Phần 1: Hello, World
  • Phần 2: Tìm hiểu về template
  • Phần 3: Tìm hiểu về Web Forms
  • Phần 4: Sử dụng cơ sở dữ liệu (bài viết này)
  • Phần 5: Xử lý đăng nhập
  • Phần 6: Hồ sơ cá nhân và ảnh đại diện
  • Phần 7: Xử lý lỗi
  • Phần 8: Tạo chức năng follower
  • Phần 9: Phân trang
  • Phần 10: Hỗ trợ email
  • Phần 11: Nâng cấp giao diện
  • Phần 12: Xử lý thời gian
  • Phần 13: Hỗ trợ đa ngôn ngữ
  • Phần 14: Sử dụng Ajax
  • Phần 15: Tinh chỉnh cấu trúc ứng dụng
  • Phần 16: Hỗ trợ tìm kiếm
  • Phần 17: Triển khai ứng dụng trên Linux
  • Phần 18: Triển khai ứng dụng với Heroku
  • Phần 19: Triển khai ứng dụng với Docker
  • Phần 20: JavaScript nâng cao
  • Phần 21: Thông báo cho người sử dụng
  • Phần 22: Tìm hiểu về tác vụ nền
  • Phần 23: Xây dựng API

Bạn có thể truy cập mã nguồn cho phần này tại GitHub.

Đây là một phần rất quan trọng vì hầu hết các ứng dụng đều cần đọc và lưu trữ dữ liệu một cách hiệu quả. Vì thế, chúng ta cần phải dùng đến cơ sở dữ liệu cho mục đích này.

Nếu bạn có tìm hiểu qua, bạn sẽ biết rằng Flask không hỗ trợ sẵn cơ sở dữ liệu. Tuy nhiên đây không phải là do sơ sót mà Flask được thiết kế như vậy để cho phép bạn chọn cơ sở dữ liệu nào thích hợp nhất cho ứng dụng của bạn thay vì bắt buộc bạn phải dùng một hệ cơ sở dữ liệu nào đó.

Các thư viện mở rộng trong Flask hỗ trợ cho việc sử dụng nhiều cơ sở dữ liệu khác nhau. Các hệ cơ sở dữ liệu có thể được chia thành hai nhóm chính: nhóm cơ sở dữ liệu theo mô hình quan hệ (relational) và nhóm còn lại không sử dụng mô hình này. Nhóm sau cũng được biết với tên gọi NoSQL bởi vì các hệ cơ sở dữ liệu trong nhóm này không sử dụng ngôn ngữ hỏi đáp SQL nối tiếng. Mặc dù cả hai nhóm đều có các đại diện xuất sắc, các hệ cơ sở dữ liệu trong nhóm đầu thích hợp hơn với các dữ liệu có cấu trúc như là danh sách người sử dụng, các bài viết … trong khi NoSQL thích hợp cho dữ liệu không có cấu trúc chặt chẽ. Ứng dụng của chúng ta có thể sử dụng các hệ cơ sở dữ liệu trong cả hai nhóm, nhưng chúng ta sẽ chọn cơ sở dữ liệu quan hệ theo như lý do đã nói ở trên.

Trong Phần 3, chúng ta đã sử dụng thư viện mở rộng đầu tiên trong Flask (còn nhớ Flask-WTF không?). Trong phần này, chúng ta sẽ sử dụng thêm hai thư viện mở rộng nữa. Thư viện đầu tiên là Flask-SQLAlchemy. Đây là một thư viện cho phép chúng ta sử dụng một gói rất phổ biến là SQLAlchemy. Đây là một phần mềm trong nhóm sản phẩm gọi là Object Relational Mapping hay ORM. Các phần mềm trong nhóm này giúp chúng ta giao tiếp với cơ sở dữ liệu thông qua các thực thể như lớp, đối tượng và phương thức thay vi dùng các bảng và ngôn ngữ SQL (Các đại diện tiêu biểu cho nhóm sản phẩm này gồm có Hibernate cho Java, NHibernate hoặc EntityFramework cho .NET framework, …). Công việc chính của các phần mềm ORM là dịch các tác vụ bậc cao thành các lệnh dùng trong cơ sở dữ liệu.

Một đặc điểm rất hay của SQLAlchemy là nó hỗ trợ cho rất nhiều cơ sở dữ liệu quan hệ khác nhau như MySQL, PostgreSQL và SQLite. Điều này rất hữu ích vì nó có nghĩa là bạn có thể phát triển ứng dụng với một hệ cơ sở dữ liệu đơn giản như SQLite – vốn không đòi hỏi việc cài đặt phức tạp, nhưng khi triển khai ứng dụng,  bạn có thể chuyển sang một hệ cơ sở dữ liệu khác tốt hơn như là MySQL hay PostgreSQL mà không cần phải thay đổi mã trong ứng dụng của bạn.

Để cài đặt Flask-SQLAlchemy trong môi trường ảo của bạn, hãy kích hoạt nó và chạy lệnh sau:

(myenv) $ pip3 install flask-sqlalchemy

Chuyển đổi dữ liệu

Phần lớn các bài hướng dẫn về cơ sở dữ liệu chỉ dạy cách tạo và sử dụng chúng nhưng lại không đề cập đến vấn đề làm sao để cập nhật cấu trúc của cơ sở dữ liệu sẵn có khi ứng dụng thay đổi. Điều này khó thực hiện bởi vì các thiết kế cơ sở dữ liệu quan hệ có liên quan chặt chẽ đến cấu trúc của dữ liệu. Khi cấu trúc của dữ liệu thay đổi, cơ sở dữ liệu cũng phải được chuyển đổi để thích ứng với cấu trúc mới.

Thư viện mở rộng thứ hai mà chúng ta sẽ sử dụng là Flask-Migrate. Thư viện này là một giao diện của Flask với Alembic – một framework chuyên cho viện chuyển đổi dữ liệu với SQLAlchemy. Việc chuyển đổi cơ sở dữ liệu sẽ làm cho chúng ta mất công hơn một chút khi khởi tạo cơ sở dữ liệu, nhưng đáng giá vì nó sẽ giảm nhiều gánh nặng cho chúng ta trong việc thay đổi cơ sở dữ liệu trong tượng lai.

Quá trình cài đặt thư viện Flask-Migrate hoàn toàn tương tự như các thư viện khác:

(myenv) $ pip3 install flask-migrate

Thiết lập cấu hình cho Flask-SQLAlchemy

Chúng ta sẽ sử dụng cơ sở dữ liệu SQLite cho ứng dụng của chúng ta. SQLite là một hệ cơ sở dữ liệu được sử dụng rộng rãi trong quá trình phát triển các ứng dụng nhỏ hoặc thậm chí một vài ứng dụng lớn. Với SQLite, mỗi cơ sở dữ liệu sẽ được lưu trữ trong một file duy nhất và không đòi hỏi phải cài đặt thêm các phần mềm khác trên máy chủ như trong trường hợp của MySQL và PostgreSQL.

Chúng ta sẽ phải thêm vào một số cấu hình mới trong file config.py:

config.py: Cấu hình cho Flask-SQLAlchemy Cấu hình cho Flask-SQLAlchemy

importosos

basedir=os.path.abspath(os.path.dirname(__file__))=os.path.abspath(os.path.dirname(__file__))

classConfig(object):Config(object):

    ......

    SQLALCHEMY_DATABASE_URI=os.environ.get('DATABASE_URL')or\SQLALCHEMY_DATABASE_URI=os.environ.get('DATABASE_URL')or\

        'sqlite:///'+os.path.join(basedir,'app.db')'sqlite:///'+ os.path.join(basedir,'app.db')

    SQLALCHEMY_TRACK_MODIFICATIONS=FalseSQLALCHEMY_TRACK_MODIFICATIONS=False

Thư viện Flask-SQLAlchemy sẽ sử dụng cơ sở dữ liệu tại URL được chỉ định bởi tham số cấu hình SQLALCHEMY_DATABASE_URI. Nếu bạn nhớ lại trong Phần 3, chúng ta có quy ước là thiết lập giá trị cấu hình trong biến môi trường và đồng thời cung cấp một giá trị cố định để làm dự phòng nếu vì lý do nào đó ứng dụng không tìm được biến môi trường. Trong trường hợp này, chúng ta sẽ đặt URL của cơ sở dữ liệu vào biến môi trường DATABASE_URL, và nếu như vì một lý do nào đó biến này không hoạt động, ứng dụng sẽ sử dụng cơ sở dữ liệu app.db trong thư mục chính của nó – cũng là giá trị của biến basedir trong đoạn mã trên.

Tham số SQLALCHEMY_TRACK_MODIFICATIONS sẽ được gán là False bởi vì chúng ta không cần chức năng này của SQLAlchemy – nó sẽ theo dõi và thông báo cho ứng dụng mỗi khi dữ liệu được cập nhật.

Trong ứng dụng của chúng ta, cơ sở dữ liệu sẽ được đại diện bởi một đối tượng gọi là thực thể cơ sở dữ liệu (database instance). Thành phần đảm nhiệm việc chuyển đổi cơ sở dữ liệu cũng sẽ được đại diện bởi một đối tượng khác. Các đối tượng này cần được khởi tạo trong ứng dụng, cụ thể hơn là trong file app/__init__.py:

app/__init__.py:  Khởi tạo các đối tượng Flask-SQLAlchemy và Flask-Migrate  Khởi tạo các đối tượng Flask-SQLAlchemy và Flask-Migrate

fromflask importFlaskflask importFlask

fromconfig importConfigconfig importConfig

fromflask_sqlalchemy importSQLAlchemyflask_sqlalchemy importSQLAlchemy

fromflask_migrate importMigrateflask_migrate importMigrate

app=Flask(__name__) =Flask(__name__)

app.config.from_object(Config).config.from_object(Config)

db=SQLAlchemy(app)=SQLAlchemy(app)

migrate=Migrate(app,db)= Migrate(app,db)

fromapp importroutes,modelsapp importroutes,models

Chúng ta thay đổi ba chỗ trong mã ở trên. Đầu tiên, chúng ta thêm một đối tượng tên là db để đại diện cho cơ sở dữ liệu. Tiếp theo chúng ta thêm một đối tượng nữa gọi là migrate để đại diện cho thành phần chuyển đổi dữ liệu. Đây cũng là cách tổng quát để chúng ta khởi tạo và sử dụng các thư viện mở rộng của Flask. Và cuối cũng, chúng ta sẽ tham chiếu đến một module mới gọi là models ở phía cuối của file. Module này sẽ định nghĩa cấu trúc của cơ sở dữ liệu.

Mô hình dữ liệu

Dữ liệu trong các hệ thống cơ sở dữ liệu sẽ được đại diện bởi một tập hợp các lớp, thường được gọi là mô hình dữ liệu (database model). Lớp ORM bên trong của SQLAlchemy sẽ thực hiện các công việc chuyển đổi cần thiết để ánh xạ các đối tượng từ mô hình dữ liệu vào các hàng và bảng trong cơ sở dữ liệu.

Để bắt đầu, chúng ta hãy thử tạo một mô hình cho người sử dụng theo như sơ đồ sau:

Hướng dẫn flask mysql - bình mysql

Trường (myenv) $ pip3 install flask-migrate0 thường hiện diện trong hầu hết các mô hình dữ liệu và được sử dụng như là khóa chính (primary key). Mỗi người sử dụng sẽ được gán một giá trị Id duy nhất. Trong đa số các trường hợp, các khóa chính thường được gán giá trị tự động bởi cơ sở dữ liệu, vì vậy chúng ta chỉ cần chỉ định trường (myenv) $ pip3 install flask-migrate1 là khóa chính là đủ.

Các trường (myenv) $ pip3 install flask-migrate2, (myenv) $ pip3 install flask-migrate3và (myenv) $ pip3 install flask-migrate4 được định nghĩa là các chuỗi (hay là kiểu (myenv) $ pip3 install flask-migrate5 theo quy ước của cơ sở dữ liệu). Độ dài tối đa của các chuỗi này cũng được xác định để hệ thống cơ sở dữ liệu có thể tối ưu không gian lưu trữ. Ý nghĩa của các trường (myenv) $ pip3 install flask-migrate2 và (myenv) $ pip3 install flask-migrate7 tương đối dễ hiểu, nhưng chúng ta cần giải thích một chút về trường (myenv) $ pip3 install flask-migrate4. Dù ứng dụng của chúng ta không phải là một ứng dụng hoàn chỉnh, chúng ta vẫn muốn tuần theo những quy tắc bảo mật nhất định. Và một trong những quy tắc cơ  bản nhất là không bao giờ lưu trữ mật mã (password) trong cơ sở dữ liệu dưới dạng văn bản (text). Lý do là vì nếu cơ sở dữ liệu bị xâm nhập, hacker sẽ truy nhập được tất cả các mật mã và từ đó có thể lấy tất cả thông tin cá nhân của người sử dụng. Vì vậy, thay vì lưu trữ mật mã dưới dạng văn bản, chúng ta sẽ dùng định dạng băm của mật mã (password hash) – hash là cấu trúc một chiều, chúng ta sẽ không thể chuyển đổi hash thành văn bản gốc – nhờ đó, chúng ta sẽ tránh được tình trạng truy nhập mật khẩu trái phép ngay cả khi cơ sở dữ liệu của chúng ta rơi vào tay của hacker. Chúng ta sẽ thảo luận vấn đề bảo mật kỹ hơn trong các phần sau.

Bây giờ, chúng ta có thể chuyển đổi mô hình dữ liệu người dùng ở trên thành mã chương trình trong file app/models như sau:

app/models.py: Mô hình dữ liệu về người sử dụng Mô hình dữ liệu về người sử dụng

fromapp importdbapp importdb

classUser(db.Model):User(db.Model):

    id=db.Column(db.Integer,primary_key=True)id=db.Column(db.Integer, primary_key=True)

    username=db.Column(db.String(64),index=True,unique=True)username=db.Column(db.String(64),index=True, unique=True)

    email=db.Column(db.String(120),index=True,unique=True)email=db.Column(db.String(120),index=True,unique=True)

    password_hash=db.Column(db.String(128))password_hash=db.Column(db.String(128))

    def__repr__(self):def__repr__(self):

        return''.format(self.username)   return ''.format(self.username)  

Lớp (myenv) $ pip3 install flask-migrate9 được tạo ra ở trên thừa kế từ lớp SQLALCHEMY_DATABASE_URI0, đây là lớp cơ bản cho tất cả các mô hình dữ liệu trong thư viện Flask-SQLAlchemy. Lớp này đặc tả một số trường là các thực thể của lớp SQLALCHEMY_DATABASE_URI1 dưới dạng các biến thuộc lớp. Khi khởi tạo, các trường này, chúng ta cần truyền vào kiểu của dữ liệu trong cơ sở dữ liệu và một số các tham số tùy chọn khác, ví dụ như tham số  để chỉ định rằng các trường này là duy nhất (unique) và có chỉ mục (index), các tham số này sẽ giúp cho cơ sở dữ liệu tối ưu hóa quá trình tìm kiếm dữ liệu.

Phương thức SQLALCHEMY_DATABASE_URI2 báo cho trình biên dịch Python biết cách để in ra các thông tin về đối tượng, và rất hữu dụng cho việc dò tìm lỗi (debugging). Chúng ta có thể thấy cách hoạt động của phương thức SQLALCHEMY_DATABASE_URI2 như trong phần minh họa dưới đây:

>>>fromapp.models importUserfromapp.models importUser

>>>u=User(username='thai',email='')u=User(username='thai',email='')

>>>u u

<Userthai>

Tạo ra kho lưu trữ cho quá trình chuyển đổi dữ liệu (migration repository)

Mô hình dữ liệu mà chúng ta tạo ra ở trên định nghĩa cấu trúc ban đầu của dữ liệu (schema) mà chúng ta sẽ dùng trong ứng dụng. Nhưng khi chúng ta tiếp tục phát triển ứng dụng về sau, sẽ có những lúc chúng ta sẽ cần thay đổi hay cập nhật cấu trúc này. Để giúp cho quá trình này, Alembic (framework cho việc chuyển đổi dữ liệu được sử dụng trong thư viện Flask-Migrate) sẽ đảm nhiệm việc thay đổi cấu trúc dữ liệu trong cơ sở dữ liệu mà không cần phải tạo lại cơ sở dữ liệu từ đầu.

Để thực thi công việc có vẻ khó khăn này, Alembic có một kho lưu trữ cho quá trình chuyển đổi dữ liệu (migration repository). Đây thực ra là một thư mục có chứa các đoạn mã kịch bản (script) cho việc chuyển đổi dữ liệu. Mỗi khi chúng ta cập nhật cấu trúc dữ liệu, một đoạn mã mới có chứa các thay đổi cần thiết sẽ được thêm vào trong kho lưu trữ này. Để thực hiện việc chuyển đổi cho cơ sở dữ liệu, các đoạn mã này sẽ được thực hiện theo đúng thứ tự mà chúng đã được tạo ra.

Thư viện Flask-Migrate có các lệnh riêng được gọi thông qua lệnh SQLALCHEMY_DATABASE_URI4. Cho đến giờ, chúng ta đã biết một lệnh flask là SQLALCHEMY_DATABASE_URI5. Lệnh này được hỗ trợ trực tiếp từ Flask. Hôm nay chúng ta sẽ dùng thêm một lệnh nữa là SQLALCHEMY_DATABASE_URI6, đây là lệnh mở rộng khi chúng ta dùng thư viện Flask-Migrate để thực hiện các thao tác chuyển đổi dữ liệu. Trước hết, hãy tạo một kho lưu trữ chuyển đổi dữ liệu cho ứng dụng của chúng ta bằng lệnh SQLALCHEMY_DATABASE_URI7:

(myenv)$flask db initmyenv)$flask db init

  Creating directory/home/thaipt/Works/Flask/myblog/migrations...doneCreating directory/home/thaipt/Works/Flask/myblog/migrations ...done

  Creating directory/home/thaipt/Works/Flask/myblog/migrations/versions...doneCreating directory/home/thaipt/Works/Flask/myblog/migrations/versions...done

  Generating/home/thaipt/Works/Flask/myblog/migrations/env.py...doneGenerating /home/thaipt/Works/Flask/myblog/migrations/env.py...done

  Generating/home/thaipt/Works/Flask/myblog/migrations/README...doneGenerating /home/thaipt/Works/Flask/myblog/migrations/README...done

  Generating/home/thaipt/Works/Flask/myblog/migrations/script.py.mako...doneGenerating /home/thaipt/Works/Flask/myblog/migrations/script.py.mako...done

  Generating/home/thaipt/Works/Flask/myblog/migrations/alembic.ini...doneGenerating /home/thaipt/Works/Flask/myblog/migrations/alembic.ini...done

  Please edit configuration/connection/logging settings in'/home/thaipt/Works/Flask/myblog/migrations/alembic.ini'before proceeding.Please edit configuration/connection/logging settings in'/home/thaipt/Works/Flask/myblog/migrations/alembic.ini'before proceeding.

Lưu ý rằng lệnh flask cần có biến môi trường FLASK_APP để biết vị trí của ứng dụng Flask. Vì vậy, xin nhắc lại để chạy ứng dụng, chúng ta cần thiết lập biến môi trường SQLALCHEMY_DATABASE_URI8 như đã nói ở Phần 1.

Sau khi chạy lệnh này, bạn sẽ thấy thư mục mới tên là migrations có chứa một số file và một thư mục con của thư mục này tên là versions. Tất cả những file này là một phần trong mã nguồn của bạn và cần được thêm vào hệ thống quản lý mã nguồn của bạn.

Chuyển đổi dữ liệu lần đầu

Khi đã có kho lưu trữ cho chuyển đổi dữ liệu, chúng ta có thể tiến hành thực hiện việc chuyển đổi dữ liệu lần thứ nhất. Quá trình này sẽ tạo ra bảng user trong hệ thống cơ sở dữ liệu tương ứng với mô hình dữ liệu (myenv) $ pip3 install flask-migrate9. Có hai cách để thực hiện việc chuyển đổi dữ liệu: tự động hay thủ công. Với cách chuyển đổi tự động, Alembic sẽ so sánh cấu trúc dữ liệu được định nghĩa bởi  mô hình dữ liệu và cơ sở dữ liệu thật sự và tạo ra mã kịch bản (script) để thay đổi cấu trúc của cơ sở dữ liệu cho phù hợp với mô hình dữ liệu. Trong trường hợp này, bởi vì chúng ta chưa tạo ra cơ sở dữ liệu, quá trình chuyển đổi tự động sẽ đưa toàn bộ mô hình dữ liệu (myenv) $ pip3 install flask-migrate9 vào mã chuyển đổi. Để bắt đầu quá trình chuyển đổi tự động, chúng ta dùng lệnh DATABASE_URL1 như sau:

(myenv)$flask db migrate-m"Tạo bảng users"myenv)$flask db migrate-m"Tạo bảng users"

INFO  [alembic.runtime.migration]Context impl SQLiteImpl.  [alembic.runtime.migration]Context impl SQLiteImpl.

INFO  [alembic.runtime.migration]Will assume non-transactional DDL.  [alembic.runtime.migration] Will assume non-transactional DDL.

INFO  [alembic.autogenerate.compare]Detected added table'user'  [alembic.autogenerate.compare]Detected added table'user'

INFO  [alembic.autogenerate.compare]Detected added index'ix_user_email'on'['email']'  [alembic.autogenerate.compare] Detected added index'ix_user_email'on'['email']'

INFO  [alembic.autogenerate.compare]Detected added index'ix_user_username'on'['username']'  [alembic.autogenerate.compare]Detected added index'ix_user_username'on '['username']'

  Generating/home/thaipt/Works/Flask/myblog/migrations/versions/fcf566b5be7f_tao_bang_users.py...doneGenerating/home/thaipt/Works/Flask/myblog/migrations/versions/fcf566b5be7f_tao_bang_users.py... done

Các dòng trạng thái của lệnh này cho chúng ta biết đại thể Alembic làm việc như thế nào trong quá trình này. Hai dòng đầu tiên chỉ mang tính tham khảo và có thể bỏ qua. Sau đó, Alembic cho biết đã tìm được một bảng user với hai chỉ mục. Tiếp theo, chúng ta cũng thấy được vị trí của mã kịch bản chuyển đổi. Chuỗi fcf566b5be7f là một chuỗi được sinh ra tự động và có giá trị duy nhất (nó sẽ có giá trị khác trên máy của bạn) cho quá trình chuyển đổi. Tham số DATABASE_URL2 và chuỗi “Tạo bảng users” theo sau là tùy chọn (optional), nó chỉ có tác dụng thêm một dòng mô tả vào mã chuyển đổi.

Mã kịch bản cho chuyển đổi dữ liệu sinh ra sau lệnh này sẽ là một phần trong dự án của bạn và cần được thêm vào hệ thống quản lý mã nguồn mà bạn đang sử dụng. Nếu bạn muốn, bạn có thể xem thử các đoạn mã này. Bạn sẽ thấy rằng nó có hai hàm gọi là DATABASE_URL3 và DATABASE_URL4. Hàm DATABASE_URL5) sẽ thực hiện việc chuyển đổi và hàm DATABASE_URL6) sẽ xóa bỏ các thay đổi được tạo ra bởi DATABASE_URL3. Điều này cho phép Alembic chuyển đổi dữ liệu tới bất kỳ điểm nào trong lịch sử chuyển đổi – ngay cả khi cần chuyển về các phiên bản cũ hơn nhờ vào hàm DATABASE_URL4.

Lệnh DATABASE_URL1 sẽ không thực hiện bất kỳ thay đổi nào trong cơ sở dữ liệu, nó chỉ sinh ra các đoạn mã kịch bản (script) cần thiết để chạy quá trình này mà thời. Để thực hiện việc chuyển đổi thực sự, chúng ta phải dùng lệnh basedir0 như sau:

(myenv)$flask db upgrademyenv)$flask db upgrade

INFO  [alembic.runtime.migration]Context impl SQLiteImpl.  [alembic.runtime.migration]Context impl SQLiteImpl.

INFO  [alembic.runtime.migration]Will assume non-transactional DDL.  [alembic.runtime.migration] Will assume non-transactional DDL.

INFO  [alembic.autogenerate.compare]Detected added table'user'  [alembic.runtime.migration]Running upgrade  ->fcf566b5be7f,Tao bang users

INFO  [alembic.autogenerate.compare]Detected added index'ix_user_email'on'['email']'

INFO  [alembic.autogenerate.compare]Detected added index'ix_user_username'on'['username']'

Các dòng trạng thái của lệnh này cho chúng ta biết đại thể Alembic làm việc như thế nào trong quá trình này. Hai dòng đầu tiên chỉ mang tính tham khảo và có thể bỏ qua. Sau đó, Alembic cho biết đã tìm được một bảng user với hai chỉ mục. Tiếp theo, chúng ta cũng thấy được vị trí của mã kịch bản chuyển đổi. Chuỗi fcf566b5be7f là một chuỗi được sinh ra tự động và có giá trị duy nhất (nó sẽ có giá trị khác trên máy của bạn) cho quá trình chuyển đổi. Tham số DATABASE_URL2 và chuỗi “Tạo bảng users” theo sau là tùy chọn (optional), nó chỉ có tác dụng thêm một dòng mô tả vào mã chuyển đổi.

Mã kịch bản cho chuyển đổi dữ liệu sinh ra sau lệnh này sẽ là một phần trong dự án của bạn và cần được thêm vào hệ thống quản lý mã nguồn mà bạn đang sử dụng. Nếu bạn muốn, bạn có thể xem thử các đoạn mã này. Bạn sẽ thấy rằng nó có hai hàm gọi là DATABASE_URL3 và DATABASE_URL4. Hàm DATABASE_URL5) sẽ thực hiện việc chuyển đổi và hàm DATABASE_URL6) sẽ xóa bỏ các thay đổi được tạo ra bởi DATABASE_URL3. Điều này cho phép Alembic chuyển đổi dữ liệu tới bất kỳ điểm nào trong lịch sử chuyển đổi – ngay cả khi cần chuyển về các phiên bản cũ hơn nhờ vào hàm DATABASE_URL4.

Lệnh DATABASE_URL1 sẽ không thực hiện bất kỳ thay đổi nào trong cơ sở dữ liệu, nó chỉ sinh ra các đoạn mã kịch bản (script) cần thiết để chạy quá trình này mà thời. Để thực hiện việc chuyển đổi thực sự, chúng ta phải dùng lệnh basedir0 như sau:

(myenv)$flask db upgrade

INFO  [alembic.runtime.migration]Running upgrade  ->fcf566b5be7f,Tao bang users

Như chúng ta đã thấy, Flask-Migration cũng bao gồm lệnh SQLALCHEMY_TRACK_MODIFICATIONS1 để hủy bỏ các thay đổi trên cơ sở dữ liệu đã được thực hiện trước đây. Dù trong thực tế hiếm khi chúng ta phải sử dụng lệnh này trên máy chủ chính thức, nó sẽ rất hữu ích trong môi trường phát triển của chúng ta. Sẽ có lúc bạn tạo ra một một mã kịch bản chuyển đổi nhưng lại phát hiện nó không chạy như ý. Trong những trường hợp như vậy, bạn có thể dùng lệnh downgrade, xóa bỏ các mã kịch bản này và tạo ra các mã kịch bản đúng.

Sơ lược về quan hệ trong cơ sở dữ liệu

Các hệ thống cơ sở dữ liệu quan hệ đảm nhiệm rất tốt việc lưu trữ và thiết lập các mối quan hệ giữa các bản ghi (record). Ví dụ như trong trường hợp một người sử dụng viết một bài trên blog, người sử dụng sẽ được đại diện bởi một bản ghi trong bản (table) SQLALCHEMY_TRACK_MODIFICATIONS2, và bài viết sẽ được đại diện bởi một bản ghi khác trên bảng SQLALCHEMY_TRACK_MODIFICATIONS3. Cách hiệu quả nhất để biết người nào viết bài nào là thiết lập mối quan hệ giữa hai bản ghi này.

Sau khi mối quan hệ giữa người sử dụng và bài viết được thiết lập, hệ thống cơ sở dữ liệu sẽ có thể tìm ra kết quả cho các truy vấn (query) về mối liên hệ này. Một ví dụ dễ thấy là bạn có một bài viết và muốn tìm ra ai đã viết bài này. Ngược lại, nếu bạn muốn tìm ra các bài viết được thực hiện bởi một tác giả, bạn sẽ phải dùng một truy vấn phức tạp hơn. Flask-SQLAlchemy sẽ hỗ trợ cho chúng ta trong cả hai trường hợp.

Bây giờ hãy mở rộng cơ sở dữ liệu của chúng ta để lưu trữ cả các bài viết để hiểu rõ hơn về các quan hệ. Sau đây là cấu trúc của bảng SQLALCHEMY_TRACK_MODIFICATIONS3:

Hướng dẫn flask mysql - bình mysql

Bảng SQLALCHEMY_TRACK_MODIFICATIONS3 sẽ có trường (myenv) $ pip3 install flask-migrate1 (bắt buộc), SQLALCHEMY_TRACK_MODIFICATIONS7 và SQLALCHEMY_TRACK_MODIFICATIONS8. Tuy vậy, ngoài các trường này, chúng ta sẽ thêm một trường mới là SQLALCHEMY_TRACK_MODIFICATIONS9 để liên kết các bài viết với tác giả. Trước đây, chúng ta đã thảo luận về lý do tại sao chúng ta cần sử dụng trường (myenv) $ pip3 install flask-migrate1 với vai trò khóa chính cho các bản ghi về người sử dụng, và chúng ta cũng đã biết rằng giá trị của các (myenv) $ pip3 install flask-migrate1 phải là duy nhất. Để tạo liên kết giữa các bài viết và tác giả, chúng ta cần thêm tham chiếu đến (myenv) $ pip3 install flask-migrate1 của tác giả (cũng là (myenv) $ pip3 install flask-migrate1 từ các bản ghi trong bản SQLALCHEMY_TRACK_MODIFICATIONS2) từ bản SQLALCHEMY_TRACK_MODIFICATIONS3, và đó là lý do tại sao chúng ta cần có trường SQLALCHEMY_TRACK_MODIFICATIONS9. Trường này được gọi là một khóa ngoại (foreign key). Sơ đồ trên cho chúng ta thấy vai trò của khóa ngoại trong việc thiết lập liên hệ giữa hai bảng SQLALCHEMY_TRACK_MODIFICATIONS3 và SQLALCHEMY_TRACK_MODIFICATIONS2. Trong trường hợp này, liên hệ giữa hai bảng được gọi là một-nhiều (one-to-many) vì một tác giả (basedir3) có thể viết nhiều bài (db0). Nếu bạn vẫn còn cảm thấy không hiểu rõ về quan hệ trong mô hình dữ liệu và cơ sở dữ liệu, bạn có thể tham khảo thêm tại đây.

Chúng ta sẽ cập nhật file app/models.py để tạo mô hình dữ liệu cho các bài viết như sau:

app/models.py: Bảng Posts và liên hệ với bảng User Bảng Posts và liên hệ với bảng User

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

fromdatetimeimportdatetimedatetimeimportdatetime

fromapp importdbapp importdb

classUser(db.Model):User(db.Model):

    id=db.Column(db.Integer,primary_key=True)id= db.Column(db.Integer,primary_key=True)

    username=db.Column(db.String(64),index=True,unique=True)username=db.Column(db.String(64), index=True,unique=True)

    email=db.Column(db.String(120),index=True,unique=True)email=db.Column(db.String(120),index=True, unique=True)

    password_hash=db.Column(db.String(128))password_hash=db.Column(db.String(128))

    posts=db.relationship('Post',backref='author',lazy='dynamic')posts= db.relationship('Post',backref='author',lazy='dynamic')

    def__repr__(self):def__repr__(self):

        return''.format(self.username)return ''.format(self.username)

classPost(db.Model):Post(db.Model):

    id=db.Column(db.Integer,primary_key=True)id= db.Column(db.Integer,primary_key=True)

    body=db.Column(db.String(140))body=db.Column(db.String(140))

    timestamp=db.Column(db.DateTime,index=True,default=datetime.utcnow)timestamp =db.Column(db.DateTime,index=True,default=datetime.utcnow)

    user_id=db.Column(db.Integer,db.ForeignKey('user.id'))user_id= db.Column(db.Integer,db.ForeignKey('user.id'))

    def__repr__(self):def__repr__(self):

        return''.format(self.body)return ''.format(self.body)

Lớp db1 sẽ đại diện cho các bài viết của người sử dụng. Trường SQLALCHEMY_TRACK_MODIFICATIONS8 dùng để lưu lại thời gian bài viết được tạo ra. Trường này được lập chỉ mục để tối ưu quá trình truy vấn các bài viết. Trong mã để khởi tạo trường này, chúng ta cũng thêm tham số db3 và gán giá trị trả về từ hàm db4 cho nó. Điều này đồng nghĩa với việc khi chúng ta tạo một đối tượng db1 mà không chỉ định thời gian nó được tạo ra, khi ứng dụng lưu bản ghi này vào cơ sở dữ liệu, nó sẽ tự động gán thời điểm mà bản ghi được lưu lại vào trường timestamp. Chúng ta cũng sử dụng giờ UTC cho trường này để bảo đảm rằng ứng dụng sẽ sử dụng thời gian chuẩn và không phụ thuộc vào vị trí địa lý của người sử dụng. Giá trị của SQLALCHEMY_TRACK_MODIFICATIONS8 sẽ được chuyển đổi thành thời gian địa phương khi hiển thị cho người sử dụng ở các vị trí khác nhau.

Trường SQLALCHEMY_TRACK_MODIFICATIONS9 được khởi tạo với vai trò là khóa ngoại của db8. Nói cách khác, mỗi giá trị của trường này trong bảng SQLALCHEMY_TRACK_MODIFICATIONS3 là một giá trị từ trường (myenv) $ pip3 install flask-migrate1 của bảng SQLALCHEMY_TRACK_MODIFICATIONS2. Trong giá trị db8, chuỗi basedir3 đại diện cho tên của bảng tương ứng với mô hình dữ liệu. Tuy nhiên, ở đây có một vài điểm không nhất quán mà chúng ta phải tạm thời chấp nhận. Ví dụ như khi gọi hàm migrate4, chúng ta tham chiếu đến mô hình dữ liệu thông qua lớp tương ứng bằng cách dùng tên của nó – bắt đầu bằng chữ hoa, nhưng trong một số trường hợp khác như khi gọi hàm migrate5, chúng ta phải sử dụng tên bảng trong cơ sở dữ liệu để tham chiếu đến mô hình dữ liệu tương ứng với tên gọi bằng chữ thường theo quy ước của SQLAlchemy (hoặc là dùng kiểu snake case nếu tên gọi của mô hình dữ liệu là từ ghép).

Lớp (myenv) $ pip3 install flask-migrate9 cũng được cập nhật để có thêm trường SQLALCHEMY_TRACK_MODIFICATIONS3 và được khởi tạo với hàm migrate4. Đây không phải là một trường thật sự trong cơ sở dữ liệu mà chỉ là một định nghĩa cấp cao để mô tả liên hệ giữa tác giả và bài viết (trường ảo), vì vậy chúng ta không biểu diễn nó trong sơ đồ dữ liệu. Đối với các mối quan hệ một-nhiều, một trường được định nghĩa từ migrate9 ở phía của quan hệ “một” sẽ cho phép truy nhập vào các trường của ở phía “nhiều”. Ví dụ như nếu chúng ta lưu một bản ghi về tác giả trong table models0, biểu thức models1 sẽ thực hiện một truy vấn trong cơ sở dữ liệu để tìm tất cả những bài viết được viết bởi tác giả này. Khi gọi hàm migrate9, tham số đầu tiên là lớp mô hình đại diện cho phía “nhiều” trong mối quan hệ giữa hai mô hình. Tham số này được truyền dưới dạng một chuỗi được gán tên của lớp mô hình tương ứng sẽ được định nghĩa trong phần tiếp theo của module. Tham số thứ hai (models3) định nghĩa tên của trường sẽ được thêm vào các đổi tượng ở phía “nhiều” (trong trường hợp này là các bài viết hay post) và nhờ đó chúng ta truy nhập ngược lại các đối tượng từ phía “một” trong mô hình quan hệ. Cụ thể là nó sẽ cho phép chúng ta dùng biểu thức models4 để tìm ra tác giả của một bài viết nào đó. Tại thời điểm này, chúng ta sẽ tạm thời lướt qua tham số tiếp theo là models5 và sẽ trở lại với tham số này trong các phần tiếp theo. Đừng lo nếu bạn vẫn thấy còn cảm thấy chưa hiểu rõ các chi tiết này, bạn sẽ thấy rõ ràng hơn qua các ví dụ minh họa tiếp theo sau.

Vì chúng ta vừa thay đổi mô hình dữ liệu, chúng ta cần tạo ra mã chuyển đổi mới:

(myenv)$flask db migrate-m"Tạo bảng posts"myenv)$flask db migrate-m"Tạo bảng posts"

INFO  [alembic.runtime.migration]Context impl SQLiteImpl.  [alembic.runtime.migration]Context impl SQLiteImpl.

INFO  [alembic.runtime.migration]Will assume non-transactional DDL.  [alembic.runtime.migration] Will assume non-transactional DDL.

INFO  [alembic.autogenerate.compare]Detected added table'post'  [alembic.autogenerate.compare]Detected added table'post'

INFO  [alembic.autogenerate.compare]Detected added index'ix_post_timestamp'on'['timestamp']'  [alembic.autogenerate.compare] Detected added index'ix_post_timestamp'on'['timestamp']'

  Generating/home/thaipt/Works/Flask/myblog/migrations/versions/f0db34c99253_tao_bang_posts.py...doneGenerating /home/thaipt/Works/Flask/myblog/migrations/versions/f0db34c99253_tao_bang_posts.py...done

Và tiếp theo, chúng ta cần tiến hành quá trình chuyển đổi bằng cách chạy các mã vừa sinh ra ở trên:

(myenv)$flask db upgrademyenv)$flask db upgrade

INFO  [alembic.runtime.migration]Context impl SQLiteImpl.  [alembic.runtime.migration]Context impl SQLiteImpl.

INFO  [alembic.runtime.migration]Will assume non-transactional DDL.  [alembic.runtime.migration] Will assume non-transactional DDL.

INFO  [alembic.autogenerate.compare]Detected added table'post'  [alembic.runtime.migration]Running upgrade fcf566b5be7f->f0db34c99253,Tao bang posts

INFO  [alembic.autogenerate.compare]Detected added index'ix_post_timestamp'on'['timestamp']'

Và tiếp theo, chúng ta cần tiến hành quá trình chuyển đổi bằng cách chạy các mã vừa sinh ra ở trên:

(myenv)$flask db upgrade

INFO  [alembic.runtime.migration]Running upgrade fcf566b5be7f->f0db34c99253,Tao bang posts

Đừng quên thêm các mã chuyển đổi mới này vào hệ thống quản lý mã nguồn của bạn nhé.fromapp importdb

Thực hànhfromapp.models importUser,Post

Suốt từ đầu đến giờ, chúng ta hầu như chỉ nói về việc định nghĩa dữ liệu, thật là chán phải không? Bây giờ chúng ta hãy thực tập một chút để làm quen với cách hoạt động của cơ sở dữ liệu và Python. Bạn hãy gọi trình biên dịch Python bằng lệnh models6 và hãy chắc rằng bạn đã kích hoạt môi trường ảo trước khi chúng ta bắt đầu.

>>>u=User(username='thai',email='')u=User(username='thai',email='')

>>>db.session.add(u)db.session.add(u)

>>>db.session.commit() db.session.commit()

Tại dấu nhắc lệnh của trình biên dịch Python, hãy tham chiếu (import) đến các thư viện và đối tượng trong mô hình dữ liệu của chúng ta:

Tiếp theo, chúng ta hãy thêm một người sử dụng nữa:

>>>u=User(username='nguyen',email='')u=User(username='nguyen',email='')

>>>db.session.add(u)db.session.add(u)

>>>db.session.commit() db.session.commit()

Đến đây, cơ sở dữ liệu của chúng ta có thể trả lời cho truy vấn tìm kiếm tất cả người sử dụng trong hệ thống:

>>>users=User.query.all()users=User.query.all()

>>>usersusers

[,]<Userthai>,<Usernguyen>]

>>>foruinusers:foruinusers:

...     print(u.id,u.username)..     print(u.id,u.username)

.....

1thai thai

2nguyennguyen

Tât cả các đối tượng mô hình dữ liệu đều có thuộc tính (myenv) $ pip3 install flask-migrate03 để thực hiện truy vấn. Truy vấn cơ bản nhất là tìm tất cả những đối tượng thuộc cùng một lớp với hàm (myenv) $ pip3 install flask-migrate04. Lưu ý là trường (myenv) $ pip3 install flask-migrate1 sẽ được tự động gán cá giá trị số nguyên theo thứ tự tăng dần (ở đây là 1 và 2) khi chúng ta thêm các dữ liệu mới vào.

Sau đây chúng ta sẽ thử tìm thông tin về người sử dụng qua Id của họ:

>>>u=User.query.get(1)u=User.query.get(1)

>>>uu

<Userthai>

Vậy nếu chúng ta muốn thêm một bài viết thì sao?

>>>u=User.query.get(1)u=User.query.get(1)

>>>p=Post(body='my first post!',author=u)p=Post(body='my first post!', author=u)

>>>db.session.add(p)db.session.add(p)

>>>db.session.commit()db.session.commit()

Chúng ta không gán giá trị cho trường SQLALCHEMY_TRACK_MODIFICATIONS8 vì giá trị này có được gán giá trị mặc định (bạn còn nhớ hàm (myenv) $ pip3 install flask-migrate07 không?).  Thế còn trường SQLALCHEMY_TRACK_MODIFICATIONS9 thì sao? Bạn có nhớ chúng ta đã dùng hàm migrate9 để thêm biến SQLALCHEMY_TRACK_MODIFICATIONS3 vào lớp (myenv) $ pip3 install flask-migrate9 không? Khi gọi hàm này, chúng ta cũng đồng thời gán trường ảo (myenv) $ pip3 install flask-migrate12 để sử chúng ta có thể sử dụng thay vì dùng Id của người dùng. SQLAlchemy làm rất tốt công việc này khi cho phép chúng ta sử dụng các trừu tượng thay vì sử dụng trực tiếp các khóa ngoại để biểu diễn mối liên hệ giữa các mô hình dữ liệu.

Để kết thúc mục này, chúng ta sẽ xem qua vài ví dụ truy vấn dữ liệu nữa:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

>>># Tìm tất cả bài được viết bởi một user# Tìm tất cả bài được viết bởi một user

>>>u=User.query.get(1)u=User.query.get(1)

>>>uu

<Userthai>

>>>posts=u.posts.all() posts=u.posts.all()

>>>postsposts

Vậy nếu chúng ta muốn thêm một bài viết thì sao?<Post my first post!>]

>>>p=Post(body='my first post!',author=u)# tương tự như vậy, nhưng lần này là một user không có bài viết

>>>u=User.query.get(2)u=User.query.get(2)

>>>uu

<Usernguyen>

>>>u.posts.all() u.posts.all()

Vậy nếu chúng ta muốn thêm một bài viết thì sao?]

>>>p=Post(body='my first post!',author=u)# in ra tên tác giả và toàn bộ các bài viết

>>>posts=Post.query.all()posts=Post.query.all()

>>>forpinposts: forpinposts:

Chúng ta không gán giá trị cho trường SQLALCHEMY_TRACK_MODIFICATIONS8 vì giá trị này có được gán giá trị mặc định (bạn còn nhớ hàm (myenv) $ pip3 install flask-migrate07 không?).  Thế còn trường SQLALCHEMY_TRACK_MODIFICATIONS9 thì sao? Bạn có nhớ chúng ta đã dùng hàm migrate9 để thêm biến SQLALCHEMY_TRACK_MODIFICATIONS3 vào lớp (myenv) $ pip3 install flask-migrate9 không? Khi gọi hàm này, chúng ta cũng đồng thời gán trường ảo (myenv) $ pip3 install flask-migrate12 để sử chúng ta có thể sử dụng thay vì dùng Id của người dùng. SQLAlchemy làm rất tốt công việc này khi cho phép chúng ta sử dụng các trừu tượng thay vì sử dụng trực tiếp các khóa ngoại để biểu diễn mối liên hệ giữa các mô hình dữ liệu...     print(p.id,p.author.username,p.body)

.....

Tât cả các đối tượng mô hình dữ liệu đều có thuộc tính (myenv) $ pip3 install flask-migrate03 để thực hiện truy vấn. Truy vấn cơ bản nhất là tìm tất cả những đối tượng thuộc cùng một lớp với hàm (myenv) $ pip3 install flask-migrate04. Lưu ý là trường (myenv) $ pip3 install flask-migrate1 sẽ được tự động gán cá giá trị số nguyên theo thứ tự tăng dần (ở đây là 1 và 2) khi chúng ta thêm các dữ liệu mới vào.thai my first post!

Sau đây chúng ta sẽ thử tìm thông tin về người sử dụng qua Id của họ:# tìm tất cả các user theo thứ tự abc đảo ngược

>>>User.query.order_by(User.username.desc()).all() User.query.order_by(User.username.desc()).all()

[,]<Userthai>,<Usernguyen>]

Nếu bạn muốn tìm hiểu thêm về các ùy chọn khi truy vấn dữ liệu, bạn có thể tham khảo các tài liệu trực tuyến của Flask-SQLAlchemy.

Vậy nếu chúng ta muốn thêm một bài viết thì sao?

>>>users=User.query.all()users=User.query.all()

>>>foruinusers:foruinusers:

>>>p=Post(body='my first post!',author=u)..     db.session.delete(u)

.....

>>>posts=Post.query.all()posts=Post.query.all()

>>>forpinposts:forp inposts:

Tât cả các đối tượng mô hình dữ liệu đều có thuộc tính (myenv) $ pip3 install flask-migrate03 để thực hiện truy vấn. Truy vấn cơ bản nhất là tìm tất cả những đối tượng thuộc cùng một lớp với hàm (myenv) $ pip3 install flask-migrate04. Lưu ý là trường (myenv) $ pip3 install flask-migrate1 sẽ được tự động gán cá giá trị số nguyên theo thứ tự tăng dần (ở đây là 1 và 2) khi chúng ta thêm các dữ liệu mới vào...     db.session.delete(p)

.....

>>>db.session.commit()db.session.commit()

Tât cả các đối tượng mô hình dữ liệu đều có thuộc tính (myenv) $ pip3 install flask-migrate03 để thực hiện truy vấn. Truy vấn cơ bản nhất là tìm tất cả những đối tượng thuộc cùng một lớp với hàm (myenv) $ pip3 install flask-migrate04. Lưu ý là trường (myenv) $ pip3 install flask-migrate1 sẽ được tự động gán cá giá trị số nguyên theo thứ tự tăng dần (ở đây là 1 và 2) khi chúng ta thêm các dữ liệu mới vào.

Sau đây chúng ta sẽ thử tìm thông tin về người sử dụng qua Id của họ:

>>>fromapp importdb

Vậy nếu chúng ta muốn thêm một bài viết thì sao?fromapp.models importUser,Post

>>>p=Post(body='my first post!',author=u)

(myenv)$pythonmyenv)$python

>>>appapp

Chúng ta không gán giá trị cho trường SQLALCHEMY_TRACK_MODIFICATIONS8 vì giá trị này có được gán giá trị mặc định (bạn còn nhớ hàm (myenv) $ pip3 install flask-migrate07 không?).  Thế còn trường SQLALCHEMY_TRACK_MODIFICATIONS9 thì sao? Bạn có nhớ chúng ta đã dùng hàm migrate9 để thêm biến SQLALCHEMY_TRACK_MODIFICATIONS3 vào lớp (myenv) $ pip3 install flask-migrate9 không? Khi gọi hàm này, chúng ta cũng đồng thời gán trường ảo (myenv) $ pip3 install flask-migrate12 để sử chúng ta có thể sử dụng thay vì dùng Id của người dùng. SQLAlchemy làm rất tốt công việc này khi cho phép chúng ta sử dụng các trừu tượng thay vì sử dụng trực tiếp các khóa ngoại để biểu diễn mối liên hệ giữa các mô hình dữ liệu.(most recent call last):

  File"",line1,inFile"",line 1,in<module>

NameError:name'app'isnotdefined:name'app'isnotdefined

Để kết thúc mục này, chúng ta sẽ xem qua vài ví dụ truy vấn dữ liệu nữa:

>>># Tìm tất cả bài được viết bởi một usermyenv)$flask shell

>>>app app

Python3.7.3(default,Apr  32019,05:39:12)3.7.3(default,Apr  32019,05:39:12)

[]GCC8.3.0]on linux

App:app[production]:app[production]

Instance:/home/thaipt/Works/Flask/myblog/instance:/home/thaipt/Works/Flask/myblog/instance

Để kết thúc mục này, chúng ta sẽ xem qua vài ví dụ truy vấn dữ liệu nữa:

>>># Tìm tất cả bài được viết bởi một user

[]

>>># tương tự như vậy, nhưng lần này là một user không có bài viếtapp importapp,db

>>># in ra tên tác giả và toàn bộ các bài viết app.models importUser,Post

@app.shell_context_processorapp.shell_context_processor

defmake_shell_context(): make_shell_context():

    return{'db':db,'User':User,'Post':Post}return{'db':db,'User':User,'Post':Post}

...     print(p.id,p.author.username,p.body)

Sau khi đã tạo hàm xử lý ngữ cảnh lệnh như trên, bạn có thể làm sử dụng trực tiếp các đối tượng và hàm từ cơ sở dữ liệu mà không cần phải tham chiếu đến chúng:

(myenv)$flask shellmyenv)$flask shell

Python3.7.3(default,Apr  32019,05:39:12)3.7.3(default,Apr  32019,05:39:12)

[GCC8.3.0]on linuxGCC 8.3.0]on linux

App:app[production]:app[production]

Instance:/home/thaipt/Works/Flask/myblog/instance:/home/thaipt/Works/Flask/myblog/instance

>>>db db

<SQLAlchemy engine=sqlite:////home/thaipt/Works/Flask/myblog/app.db>

>>>UserUser

<class 'app.models.User'>

>>>PostPost

<class'app.models.Post'>

>>>

Nếu bạn gặp lỗi (myenv) $ pip3 install flask-migrate23 khi sử dụng các đối tượng db, (myenv) $ pip3 install flask-migrate9 và db1 như trong ví dụ trên, điều đó có nghĩa là hàm (myenv) $ pip3 install flask-migrate27 đã không được đăng ký thành công với Flask. Lý do thường gặp nhất là do bạn quên chỉ định biến môi trường (myenv) $ pip3 install flask-migrate28, hãy xem lại Phần 1 để biết cách khai báo biến này và làm sao để thêm biến này vào file .flaskenv.

Chúng ta sẽ kết thúc phần này ở đây. Hẹn gặp bạn trong phần tiếp theo.