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ệ ManyToManyField trong Django với Through
Trong Django, mối quan hệ nhiều-nhiều cho phép nhiều hàng trong một bảng liên kết với nhiều hàng trong một bảng khác. Để thiết lập mối quan hệ này, thường sử dụng một bảng liên kết trung gian. Tuy nhiên, có những tình huống yêu cầu bổ sung các trường thông tin vào bảng liên kết này để lưu trữ thêm dữ liệu về mối quan hệ. Trong bài viết này, bạn sẽ học cách sử dụng tham số through trong ManyToManyField
của Django để thêm các trường bổ sung vào mối quan hệ nhiều-nhiều, giúp bạn quản lý dữ liệu một cách chi tiết và hiệu quả hơn.
Giới thiệu về ManyToManyField trong Django với Through
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. Để thiết lập mối quan hệ nhiều-nhiều, cơ sở dữ liệu quan hệ sử dụng một bảng thứ ba gọi là bảng liên kết và tạo ra hai mối quan hệ một-nhiều từ các bảng nguồn.
Thông thường, bảng liên kết chứa các giá trị id của các bảng nguồn để các hàng trong một bảng có thể liên quan đến các hàng trong bảng khác.
Đôi khi, bạn có thể muốn thêm các trường bổ sung vào bảng liên kết. Ví dụ, mỗi nhân viên có thể có nhiều công việc trong suốt sự nghiệp của họ.
Bài viết này được đăng tại [free tuts .net]
Để theo dõi thời gian khi một nhân viên bắt đầu và kết thúc một công việc, bạn có thể thêm các trường begin_date
và end_date
vào bảng liên kết.
Để làm điều đó trong Django, bạn sử dụng ManyToManyField
với tham số through.
Ví dụ, sau đây cho thấy cách liên kết một nhân viên với nhiều công việc thông qua bảng assignments:
from django.db import models class Employee(models.Model): # Các trường của Employee... class Job(models.Model): title = models.CharField(max_length=255) employees = models.ManyToManyField(Employee, through='Assignment') def __str__(self): return self.title #Bài viết này được đăng tại freetuts.net class Assignment(models.Model): employee = models.ForeignKey(Employee, on_delete=models.CASCADE) position = models.ForeignKey(Job, on_delete=models.CASCADE) begin_date = models.DateField() end_date = models.DateField(default=date(9999, 12, 31))
Cách hoạt động:
- Đầu tiên, định nghĩa mô hình Job, thêm thuộc tính employees sử dụng ManyToManyField và truyền Assignment làm tham số through.
- Thứ hai, định nghĩa lớp Assignment có hai khóa ngoại, một liên kết đến mô hình Employee, và một liên kết đến mô hình Job. Cũng thêm các thuộc tính begin_date và end_date vào mô hình Assignment.
- Chạy lệnh
makemigrations
để tạo các migrations mới:
python manage.py makemigrations
Đầu ra sẽ như sau:
Migrations for 'hr': hr\migrations\0005_assignment_job_assignment_job.py - Create model Assignment - Create model Job - Add field job to assignment
Và thực thi lệnh migrate để áp dụng các thay đổi vào cơ sở dữ liệu:
python manage.py migrate
Đầu ra sẽ như sau:
Operations to perform: Apply all migrations: admin, auth, contenttypes, hr, sessions Running migrations: Applying hr.0005_assignment_job_assignment_job... OK
Phía sau, Django sẽ tạo các bảng hr_job
và hr_assignment
trong cơ sở dữ liệu.
Bảng hr_assignment
là bảng liên kết. Ngoài các trường employee_id
và position_id
, nó còn có các trường begin_date
và end_date
.
Tạo dữ liệu mới trong Django
Đầu tiên, chạy lệnh shell_plus
:
python manage.py shell_plus
Thứ hai, tạo ba công việc mới:
>>> j1 = Job(title='Software Engineer I') >>> j1.save() #Bài viết này được đăng tại freetuts.net >>> j2 = Job(title='Software Engineer II') >>> j2.save() >>> j3 = Job(title='Software Engineer III') >>> j3.save() >>> Job.objects.all() <QuerySet [<Job: Software Engineer I>, <Job: Software Engineer II>, <Job: Software Engineer III>]>
Tạo các thực thể cho models trung gian trong Django
Đầu tiên, tìm nhân viên có tên freetuts .net và .net freetuts:
>>> e1 = Employee.objects.filter(first_name='freetuts',last_name='.net').first() >>> e1 <Employee: freetuts .net> #Bài viết này được đăng tại freetuts.net >>> e2 = Employee.objects.filter(first_name='.net', last_name='freetuts').first() >>> e2 <Employee: .net freetuts>
Thứ hai, tạo các thực thể của mô hình trung gian (Assignment):
>>> from datetime import date >>> a1 = Assignment(employee=e1,job=j1, begin_date=date(2019,1,1), end_date=date(2021,12,31)) >>> a1.save() #Bài viết này được đăng tại freetuts.net >>> a2 = Assignment(employee=e1,job=j2, begin_date=date(2022,1,1)) >>> a2.save() >>> a3 = Assignment(employee=e2, job=j1, begin_date=date(2019, 3, 1)) >>> a3.save()
Tìm các nhân viên giữ vị trí Software Engineer I (j1):
>>> j1.employees.all() <QuerySet [<Employee: freetuts .net>, <Employee: .net freetuts>]>
Phía sau, Django thực hiện truy vấn sau:
SELECT "hr_employee"."id", "hr_employee"."first_name", "hr_employee"."last_name", "hr_employee"."contact_id", "hr_employee"."department_id" FROM "hr_employee" #Bài viết này được đăng tại freetuts.net INNER JOIN "hr_assignment" ON ("hr_employee"."id" = "hr_assignment"."employee_id") WHERE "hr_assignment"."job_id" = 1
Tương tự, bạn có thể tìm tất cả nhân viên giữ vị trí Software Engineer II:
>>> j2.employees.all() <QuerySet [<Employee: freetuts .net>]>
Xóa các thực thể của models trung gian trong Django
Đầu tiên, xóa .net freetuts (e2) khỏi công việc Software Engineer II bằng cách sử dụng phương thức remove()
:
>>> j2.employees.remove(e2)
Thứ hai, xóa tất cả nhân viên khỏi công việc Software Engineer I bằng cách sử dụng phương thức clear()
:
>>> j1.employees.clear()
Công việc j1 sẽ không có nhân viên nào nữa:
>>> j1.employees.all() <QuerySet []>
Kết bài
Việc sử dụng tham số through trong ManyToManyField
của Django giúp bạn mở rộng khả năng lưu trữ và quản lý dữ liệu trong mối quan hệ nhiều-nhiều. Bạn có thể dễ dàng thêm các trường bổ sung vào bảng liên kết để lưu trữ thông tin chi tiết hơn về mối quan hệ giữa các bảng. Điều này không chỉ giúp cải thiện cấu trúc dữ liệu mà còn tăng tính linh hoạt và hiệu quả trong việc quản lý và truy vấn dữ liệu. Hy vọng hướng dẫn này đã cung cấp cho bạn những kiến thức cần thiết để áp dụng tham số through trong dự án Django của mình.