Bài 51: Kỹ thuật ORM trong Python với Cơ sở dữ liệu MySQL Server (p2)

Trong bài 50, chúng ta đã biết cách sử dụng kỹ thuật lập trình ORM để tương tác cơ sở dữ liệu MySQL Server bằng Python. Tuy nhiên chúng ta chưa đề cập tới vấn đề cả bảng dữ liệu có mối quan hệ dạng Master-Details.

Ví dụ như trong Quản lý Sản phẩm ta có Danh mục, ta có sản phẩm. 1 Danh mục có nhiều sản phẩm, và 1 sản phẩm thuộc về 1 danh mục. Vậy với trường hợp có relationship master-details như thế này thì chúng ta lập trình ORM ra sao? và trong thực tế chúng ta luôn luôn gặp các relationship master-details này.

Hay ngay trong quá trình cài đặt MySQL Server, chúng ta cũng được cung cấp một cơ sở dữ liệu mẫu “sakila“, là một cơ sở dữ liệu liên quan tới quản lý cho mượn Film, và cũng có nhiều relationship Master-Details, chúng ta sẽ lập trình ORM trên cơ sở dữ liệu mẫu này luôn (ta làm trên bảng Customer và bảng Rental):

Ta xem Relationship giữa bảng Customer và bảng Rental:

  • Một Customer có nhiều Rental (dựa vào khóa ngoại customer_id trong bảng rental, khi có 1 Customer ta sẽ biết được Customer này có bao nhiêu Rental)
  • Một Rental thuộc về một Customer (từ bảng rental ta có thuộc tính customer_id để suy ngược ra thông tin chi tiết Customer của Rental)

Đây chính là Relationship 1-many.

Ta tạo dự án “sakila_orm” trong Pycharm có cấu trúc như dưới đây:

Trong dự án này Tui chủ ý tách ra làm 3 file mã lệnh Python độc lập để có thể dễ dàng tái sử dụng:

  • File “DatabaseConnection.py“: Là file mã lệnh để kế nối Cơ sở dữ liệu, nó phải được thực hiện trước các câu lệnh khác trong dự án
  • File “ClassMapping.py“: Là file mã lệnh khai báo class mapping và relationship
  • File “TestRelationShipORM.py“: Là file mã lệnh để thử nghiệm kỹ thuật ORM với các bảng có Relationship

Bước 1:

Bây giờ ta xem mã lệnh của DatabaseConnection.py:

from orm import Table

#configuration JSON for connection MySQL
CONFIG={
    'host': 'localhost',
    'port': 3306,
    'user': 'root',
    'password': '@Obama123',
    'database': 'sakila'
}
#connect to database from JSON configuration
Table.connect(config_dict=CONFIG)

Mã lệnh kết nối cơ sở dữ liệu được tách độc lập ra 1 file khác, khi có sự thay đổi về thông tin kết nối thì bạn chỉ cần vào file này chỉnh sửa sẽ không bị rối.

Bước 2:

Tiếp theo là mã lệnh File “ClasssMapping.py“:

Dựa vào cấu trúc bảng Customer và Rental, Ta có 1 Customer có nhiều Rental, nên ở đây trong lớp Customer ta dùng hàm has_many. Còn đối với lớp Rental thì ứng với 1 đối tượng Rental ta biết được Rental này của Customer nào nên ta dùng hàm belongs_to:

from orm import Table, has_many, belongs_to

class Customer(Table):
    table_name = 'customer'

    relations = [
        has_many(name='rentals', _class='Rental', foreign_key='customer_id')
    ]
class Rental(Table):
    table_name = 'rental'

    relations = [
        belongs_to(name='customer', _class='Customer', foreign_key='customer_id',primary_key="customer_id")
    ]

hàm has_many:

Hàm has_many cho biết đối tượng này chứa nhiều đối tượng khác.

+ name=’rentals’: Đây là phương thức rentals() khi gọi phương thức này nó sẽ trả về danh sách đối tượng có kiểu Rental được khai báo trong _class

+_class: Đây là tham số định nghĩa kiểu dữ liệu đối tượng trả về cho hàm “rentals”

+foreign_key=’customer_id’: Cho biết relationship giữa Customer và Rental, khi truy vấn thì dựa vào thuộc tính này chương trình sẽ lọc ra danh sách các Rental theo foreign_key

hàm belongs_to:

hàm belongs_to cho biết đối tượng này thuộc về đối tượng khác

+name=’customer’: Là là phương thức customer() trả về đối tượng Customer của Rental đang xét. Kiểu dữ liệu trả về được khai báo trong _class là ‘Customer’

+_class=’Customer’: Đầy là tham số định nghĩa kiểu dữ liệu đối tượng trả về cho hàm customer()

+foreign_key=’customer_id’: Cho biết relationship giữa Customer và Rental, khi truy vấn thì dựa vào thuộc tính này thì chương trình sẽ cho biết mã tham chiếu tới customer hiện tại của Rental là customer_id

+primary_key=’customer_id’: Khi gọi hàm customer() sẽ truy vấn ngược lại bảng Customer dựa vào primary_key customer_id này.

Bước 3:

Cuối cùng ta viết mã lệnh cho file “TestRelationshipORM.py“:

Đâu tiên chúng ta cần import DatabaseConnection, sau đó tới MappingClass:

import DatabaseConnection
from ClassMapping import Customer, Rental

Lưu ý cần theo đúng trình tự Import ở trên, vì chương trình cần kết nối cơ sở dữ liệu trước sau đó mới tới Mapping các Class cho Table.

Tiếp theo, để truy vấn toàn bộ Customer, ta viết nối tiếp mã lệnh như sau:

#Get all Customer
customers = Customer.all()
align='{0:<6} {1:<10} {2:<10} {3:<10}'
print(align.format('Id', 'First Name','Last Name',"Email"))
for cust in customers:
    print(align.format(cust.customer_id,cust.first_name,cust.last_name,cust.email))

Chạy mã lệnh trên ta có kết quả:

Để truy vấn lấy thông tin chi tiết của Customer khi biết Primary Key:

#Query 1 Customer by primary key customer_id=1:
customer = Customer.find(1)
print(f"Id={customer.customer_id}")
print(f"First Name={customer.first_name}")
print(f"Last Name={customer.last_name}")
print(f"Email={customer.email}")

Chạy mã lệnh trên ta có kết quả:

Để truy vấn lấy danh sách Rental của Customer này ta viết lệnh:

#Get all rentals for this customer:
rentals=customer.rentals()
align='{0:<10} {1:<20} {2:<20} {3:<10} {4:<10}'
print(align.format('Rental Id', 'Rental Date','Return Date',"Staff Id","Customer Id"))
for rental in rentals:
    print(align.format(
        rental.rental_id,
        str(rental.rental_date),
        str(rental.return_date),
        rental.staff_id,
        rental.customer_id))

Thực thi mã lệnh ORM ở trên ta có kết quả danh sách Rentals của Customer như dưới đây:

Để truy vấn lấy thông tin chi tiết của Rental khi biết primary key rental_id ta làm như sau:

#Query Rental detail by primary key rental_id=2
rental=Rental.find(2)
print(align.format('Rental Id', 'Rental Date','Return Date',"Staff Id","Customer Id"))
print(align.format(
        rental.rental_id,
        str(rental.rental_date),
        str(rental.return_date),
        rental.staff_id,
        rental.customer_id))

Mã lệnh ở trên là truy vấn thông tin chi tiết của Rental khi biết rental_id=2

Thực thi lệnh trên ta có kết quả:

Bây giờ, để in thông tin chi tiết Customer của Rental này ta viết mã lệnh:

# Get Customer of this Rental:
customer=rental.customer()
print(f"Id={customer.customer_id}")
print(f"First Name={customer.first_name}")
print(f"Last Name={customer.last_name}")
print(f"Email={customer.email}")

Thực thi lệnh trên ta có kết quả:

Như vậy Tui đã minh họa xong kỹ thuật ORM để xử lý các bảng có Relationship. Đây là bài học rất quan trọng và cần thiết vì nó minh họa trường hợp trong thực tế mà ta thường gặp, hầu hết các bảng dữ liệu đều có các mối quan hệ này.

Dưới đây là mã lệnh đầy đủ của TestRelationshipORM.py:

import DatabaseConnection
from ClassMapping import Customer, Rental

#Get all Customer
customers = Customer.all()
align='{0:<6} {1:<10} {2:<10} {3:<10}'
print(align.format('Id', 'First Name','Last Name',"Email"))
for cust in customers:
    print(align.format(cust.customer_id,cust.first_name,cust.last_name,cust.email))

#Query 1 Customer by primary key customer_id=1:
customer = Customer.find(1)
print(f"Id={customer.customer_id}")
print(f"First Name={customer.first_name}")
print(f"Last Name={customer.last_name}")
print(f"Email={customer.email}")

#Get all rentals for this customer:
rentals=customer.rentals()
align='{0:<10} {1:<20} {2:<20} {3:<10} {4:<10}'
print(align.format('Rental Id', 'Rental Date','Return Date',"Staff Id","Customer Id"))
for rental in rentals:
    print(align.format(
        rental.rental_id,
        str(rental.rental_date),
        str(rental.return_date),
        rental.staff_id,
        rental.customer_id))

#Query Rental detail by primary key rental_id=2
rental=Rental.find(2)
print(align.format('Rental Id', 'Rental Date','Return Date',"Staff Id","Customer Id"))
print(align.format(
        rental.rental_id,
        str(rental.rental_date),
        str(rental.return_date),
        rental.staff_id,
        rental.customer_id))

# Get Customer of this Rental:
customer=rental.customer()
print(f"Id={customer.customer_id}")
print(f"First Name={customer.first_name}")
print(f"Last Name={customer.last_name}")
print(f"Email={customer.email}")

Ngoài ra bạn có thể tải mã nguồn của toàn bộ dự án ở đây:

https://www.mediafire.com/file/v67quhs1j3srmnz/sakila_orm.rar/file

Các chức năng ORM khác như: Thêm, sửa, Xóa thì các bạn tự xử lý theo bài 50 đã được học.

Bài học sau Tui sẽ minh họa ORM trên giao diện tương tác người dùng PyQt6 – Qt Designer.

Chúc các bạn thành công

One thought on “Bài 51: Kỹ thuật ORM trong Python với Cơ sở dữ liệu MySQL Server (p2)”

Leave a Reply