Thông báo: Download 4 khóa học Python từ cơ bản đến nâng cao tại đây.
Mối quan hệ Many-to-Many trong Django với Python
Một trong những mối quan hệ phổ biến mà bạn có thể gặp phải là mối quan hệ nhiều-nhiều (many-to-many). Trong bài viêt này, bạn sẽ học cách sử dụng ManyToManyField
để mô hình hóa mối quan hệ nhiều-nhiều trong Django một cách hiệu quả. Mình sẽ được hướng dẫn từng bước, từ việc định nghĩa mô hình, tạo bảng liên kết trong cơ sở dữ liệu, cho đến cách truy vấn và quản lý dữ liệu liên quan. Bằng cách áp dụng các kỹ thuật này, bạn sẽ có thể xây dựng các ứng dụng Django mạnh mẽ và linh hoạt hơn.
Mối quan hệ Many-to-Many là gì?
Trong một mối quan hệ nhiều-nhiều, nhiều hàng trong một bảng có liên quan đến nhiều hàng trong một bảng khác.
Ví dụ, một nhân viên có thể tham gia nhiều chương trình đãi ngộ và một chương trình đãi ngộ có thể áp dụng cho nhiều nhân viên.
Do đó, nhiều hàng trong bảng nhân viên có liên quan đến nhiều hàng trong bảng chương trình đãi ngộ. Vì vậy, mối quan hệ giữa nhân viên và các chương trình đãi ngộ là một mối quan hệ nhiều-nhiều.
Bài viết này được đăng tại [free tuts .net]
Thông thường, các cơ sở dữ liệu quan hệ không triển khai trực tiếp mối quan hệ nhiều-nhiều giữa hai bảng. Thay vào đó, chúng sử dụng một bảng thứ ba, bảng liên kết, để thiết lập hai mối quan hệ một-nhiều giữa hai bảng và bảng liên kết.
Sơ đồ sau minh họa mối quan hệ nhiều-nhiều trong cơ sở dữ liệu giữa các bảng hr_employee
và hr_compensation
:
Bảng hr_employee_compensations
là một bảng liên kết. Nó có hai khóa ngoại là employee_id
và compensation_id
.
Khóa ngoại employee_id
tham chiếu đến id của bảng hr_employee
và khóa ngoại compensation_id
tham chiếu đến id trong bảng hr_compensation
.
Thông thường, bạn không cần cột id trong bảng hr_employee_compensations
làm khóa chính và sử dụng cả employee_id
và compensation_id
làm khóa chính tổng hợp. Tuy nhiên, Django luôn tạo cột id làm khóa chính cho bảng liên kết.
Ngoài ra, Django tạo một ràng buộc duy nhất bao gồm các cột employee_id
và compensation_id
. Nói cách khác, sẽ không có cặp giá trị employee_id
và compensation_id
trùng lặp trong bảng hr_employee_compensations
.
Để tạo mối quan hệ nhiều-nhiều trong Django, bạn sử dụng ManyToManyField
. Ví dụ sau sử dụng ManyToManyField
để tạo mối quan hệ nhiều-nhiều giữa các mô hình Employee
và Compensation:
class Compensation(models.Model): name = models.CharField(max_length=255) def __str__(self): return self.name #Bài viết này được đăng tại freetuts.net class Employee(models.Model): first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) contact = models.OneToOneField( Contact, on_delete=models.CASCADE, null=True ) department = models.ForeignKey( Department, on_delete=models.CASCADE ) #Bài viết này được đăng tại freetuts.net compensations = models.ManyToManyField(Compensation) def __str__(self): return f'{self.first_name} {self.last_name}'
Cách thức hoạt động
Đầu tiên, định nghĩa một lớp mô hình Compensation mới mở rộng từ lớp models.Model.
Thứ hai, thêm trường compensations
vào lớp Employee. Trường compensations sử dụng ManyToManyField để thiết lập mối quan hệ nhiều-nhiều giữa các lớp Employee và Compensation.
Để lan truyền các thay đổi của các mô hình vào cơ sở dữ liệu, bạn chạy lệnh makemigrations:
python manage.py makemigrations
Nó sẽ hiển thị thông tin như sau:
Migrations for 'hr': hr\migrations\0004_compensation_employee_compensations.py - Create model Compensation - Add field compensations to employee
Và thực hiện lệnh migrate:
python manage.py migrate
Django tạo hai bảng mới là hr_compensation
và một bảng liên kết hr_employee_compensations
như sau:
Tạo dữ liệu trong Django
Đầu tiên, chạy lệnh shell_plus
:
python manage.py shell_plus
Thứ hai, tạo ba chương trình đãi ngộ bao gồm Cổ phiếu, Thưởng và Chia sẻ Lợi nhuận:
>>> c1 = Compensation(name='Stock') >>> c1.save() #Bài viết này được đăng tại freetuts.net >>> c2 = Compensation(name='Bonuses') >>> c2.save() >>> c3 = Compensation(name='Profit Sharing') >>> c3.save() >>> Compensation.objects.all() <QuerySet [<Compensation: Stock>, <Compensation: Bonuses>, <Compensation: Profit Sharing>]>
Lấy nhân viên có tên là freetuts và họ là .net:
>>> e = Employee.objects.filter(first_name='freetuts',last_name='.net').first() >>> e <Employee: freetut.net>
Thêm các chương trình đãi ngộ cho nhân viên
Đầu tiên, thêm .net freetuts vào các chương trình đãi ngộ cổ phiếu (c1) và thưởng (c2) sử dụng phương thức add() của thuộc tính compensations và phương thức save()
của đối tượng Employee:
>>> e.compensations.add(c1) >>> e.compensations.add(c2) >>> e.save()
Thứ hai, truy cập tất cả các chương trình đãi ngộ của John Doe sử dụng phương thức all() của thuộc tính compensations:
>>> e.compensations.all() <QuerySet [<Compensation: Stock>, <Compensation: Bonuses>]>
Rõ ràng trong kết quả, John Doe có hai chương trình đãi ngộ.
Thứ ba, thêm .net freetuts vào ba chương trình đãi ngộ bao gồm cổ phiếu, thưởng và chia sẻ lợi nhuận:
>>> e = Employee.objects.filter(first_name='Jane',last_name='Doe').first() >>> e <Employee: Jane Doe> >>> e.compensations.add(c1) >>> e.compensations.add(c2) #Bài viết này được đăng tại freetuts.net >>> e.compensations.add(c3) >>> e.save() >>> e.compensations.all() <QuerySet [<Compensation: Stock>, <Compensation: Bonuses>, <Compensation: Profit Sharing>]>
Bên trong, Django đã chèn các id của nhân viên và các chương trình đãi ngộ vào bảng liên kết:
id | employee_id | compensation_id ----+-------------+----------------- 1 | 5 | 1 2 | 5 | 2 3 | 6 | 1 4 | 6 | 2 5 | 6 | 3 (5 rows)
ìm tất cả nhân viên đã tham gia chương trình cổ phiếu sử dụng thuộc tính employee_set của đối tượng Compensation:
>>> c1 <Compensation: Stock> #Bài viết này được đăng tại freetuts.net >>> c1.employee_set.all() <QuerySet [<Employee: freetut.net>, <Employee: .net freetuts>]>
Nó trả về hai nhân viên như mong đợi.
Thứ năm, bạn có thể sử dụng thuộc tính employee_set
để tìm tất cả nhân viên có chương trình chia sẻ lợi nhuận:
>>> c3 <Compensation: Profit Sharing> >>> c3.employee_set.all() #Bài viết này được đăng tại freetuts.net <QuerySet [<Employee: .net freetuts>]>
Nó trả về một nhân viên.
Django cho phép bạn truy vấn qua mối quan hệ. Ví dụ, bạn có thể tìm tất cả nhân viên có chương trình đãi ngộ với id là 1:
>>> Employee.objects.filter(compensations__id=1) <QuerySet [<Employee: freetuts .net>, <Employee: .net freetuts>]>
Hoặc với tên là "Profit Sharing":
>>> Employee.objects.filter(compensations__name="Profit Sharing") <QuerySet [<Employee: .net freetuts>]>
Xóa chương trình đãi ngộ khỏi nhân viên
Để xóa một chương trình đãi ngộ khỏi nhân viên, bạn sử dụng phương thức remove() của thuộc tính compensations của đối tượng Employee. Ví dụ:
Đầu tiên, lấy nhân viên có tên là .net freetuts:
>>> e = Employee.objects.filter(first_name='.net',last_name='freetuts').first() >>> e <Employee: .net freetuts>
Thứ hai, xóa chương trình chia sẻ lợi nhuận (c3) khỏi .net freetuts và lưu thay đổi vào cơ sở dữ liệu:
>>> e.compensations.remove(c3) >>> e.save()
Lấy tất cả các chương trình đãi ngộ của .net freetuts:
>>> e.compensations.all() <QuerySet [<Compensation: Stock>, <Compensation: Bonuses>]>
Kết bài
Trong một mối quan hệ nhiều-nhiều, nhiều hàng trong một bảng có liên quan đến nhiều hàng trong một bảng khác. Các cơ sở dữ liệu quan hệ sử dụng bảng liên kết để thiết lập mối quan hệ nhiều-nhiều giữa hai bảng, cho phép quản lý và truy xuất dữ liệu một cách hiệu quả. Việc sử dụng ManyToManyField trong Django giúp bạn mô hình hóa một cách trực quan và dễ dàng các mối quan hệ phức tạp này giữa các mô hình. Bằng cách nắm vững cách thiết lập và quản lý mối quan hệ nhiều-nhiều, bạn có thể xây dựng các ứng dụng Django mạnh mẽ và linh hoạt, đáp ứng tốt các yêu cầu phức tạp của dự án.