DJANGO
CÁC CHỦ ĐỀ
BÀI MỚI NHẤT
MỚI CẬP NHẬT

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ệ One-to-One trong Django ORM với Python

Việc quản lý các mối quan hệ giữa các bảng trong cơ sở dữ liệu là rất quan trọng. Django, một trong những framework web phổ biến nhất, cung cấp một cách tiếp cận mạnh mẽ và linh hoạt để xử lý các mối quan hệ này thông qua Django ORM. Sau khi đã làm quen với các khái niệm cơ bản của Django ORM, mình sẽ tiếp tục tìm hiểu mối quan hệ một-một (one-to-one relationship). Hướng dẫn này sẽ giúp bạn hiểu rõ cách thiết lập và quản lý mối quan hệ một-một trong Django, từ đó giúp ứng dụng của bạn trở nên tối ưu và dễ bảo trì hơn.

test php

banquyen png
Bài viết này được đăng tại freetuts.net, không được copy dưới mọi hình thức.

Mối quan hệ One-to-One trong Django là gì?

Mối quan hệ One-to-One trong Django ORM là một loại mối quan hệ giữa hai mô hình (models) trong Django, trong đó một đối tượng của mô hình A chỉ có thể có một đối tượng liên kết trong mô hình B, và ngược lại, một đối tượng của mô hình B cũng chỉ có thể được liên kết với một đối tượng của mô hình A.

Trong mối quan hệ One-to-One, mỗi đối tượng trong một mô hình có một trường liên kết trực tiếp với một đối tượng duy nhất trong mô hình khác.

Ví dụ, mỗi nhân viên có một thông tin liên hệ và mỗi thông tin liên hệ thuộc về một nhân viên. Vì vậy, mối quan hệ giữa nhân viên và thông tin liên hệ là một mối quan hệ một-một.

Bài viết này được đăng tại [free tuts .net]

Để tạo mối quan hệ một-một, bạn sử dụng lớp OneToOneField:

OneToOneField(to, on_delete, parent_link=False, **options)

Trong cú pháp này:

  • Tham số to xác định tên mô hình.
  • on_delete xác định hành động trên liên hệ khi nhân viên bị xóa.

Ví dụ sau sử dụng lớp OneToOneField để định nghĩa mối quan hệ một-một giữa các mô hình Contact và Employee trong file models.py:

from django.db import models

class Contact(models.Model):
    phone = models.CharField(max_length=50, unique=True)
    address = models.CharField(max_length=50)

    def __str__(self):
        return self.phone
#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)

    def __str__(self):
        return f'{self.first_name} {self.last_name}'

Lớp Employee có thuộc tính contact tham chiếu đến một instance của lớp OneToOneField.

Trong OneToOneField, mình xác định mô hình Contact và tùy chọn on_delete để xác định hành vi khi một đối tượng nhân viên bị xóa.

Tùy chọn on_delete=models.CASCADE có nghĩa là nếu một đối tượng Employee bị xóa, đối tượng Contact liên kết với Employee đó cũng sẽ bị xóa tự động.

Lưu ý rằng Django tạo ra một ràng buộc khóa ngoại trong cơ sở dữ liệu mà không có tùy chọn ON DELETE CASCADE. Thay vào đó, Django xử lý việc xóa một cách thủ công trong ứng dụng. Lưu ý rằng việc triển khai nội bộ này có thể thay đổi trong tương lai.

Di chuyển các mô hình vào cơ sở dữ liệu trong Django

Đầu tiên, tạo các di chuyển bằng lệnh makemigrations:

python manage.py makemigrations

Đầu ra:

Migrations for 'hr':
  hr\migrations\0002_contact_employee_contact.py
    - Create model Contact
    - Add field contact to employee

Thứ hai, áp dụng các di chuyển vào cơ sở dữ liệu bằng lệnh migrate:

python manage.py migrate

Đầu ra:

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, hr, sessions
Running migrations:
  Applying hr.0002_contact_employee_contact... OK

Django tạo hai bảng hr_contacthr_employee trong cơ sở dữ liệu:

Django one to one database tables png

Bảng hr_employee có cột contact_id là khóa ngoại liên kết với cột id (khóa chính) của bảng hr_contact.

Liên kết một liên hệ với employee trong Django với Python

Để tương tác với các mô hình Employee và Contact, bạn chạy lệnh shell_plus với tùy chọn --print-sql:

python manage.py shell_plus --print-sql

Tùy chọn --print-sql xuất ra các lệnh SQL mà Django thực thi.

Đầu tiên, tạo một đối tượng Employee mới và lưu vào cơ sở dữ liệu:

>>> e = Employee(first_name='freetuts', last_name='.net')
>>> e.save()

Django thực thi lệnh SQL sau:

INSERT INTO "hr_employee" ("first_name", "last_name", "contact_id")
VALUES ('John', 'Doe', NULL) 
RETURNING "hr_employee"."id"

Thứ hai, tạo và lưu một liên hệ mới vào cơ sở dữ liệu:

>>> c = Contact(phone='1234567788', address='101 ha noi, ha noi1 , ha noi 2')
>>> c.save()

Django cũng thực thi lệnh INSERT sau:

INSERT INTO "hr_contact" ("phone", "address")
VALUES ('1234567788', '101 ha noi, ha noi1 , ha noi 2') 
RETURNING "hr_contact"."id"

Thứ ba, liên kết một liên hệ với một nhân viên:

>>> e.contact = c
>>> e.save()

Django cập nhật giá trị của cột contact_id trong bảng hr_employee thành giá trị của cột id trong bảng hr_contact.

UPDATE "hr_employee"
   SET "first_name" = 'freetuts',
       "last_name" = '.net',
       "contact_id" = 1
 WHERE "hr_employee"."id" = 3

Lấy dữ liệu từ mối quan hệ một-một trong Django

Đầu tiên, lấy nhân viên với tên freetuts.net:

>>> e = Employee.objects.filter(first_name='freetut', last_name='.net').first()
<Employee: freetuts.net>

Django thực thi lệnh SELECT để lấy hàng với first_name là 'John' và last_name là 'Doe'.

Bảng hr_employee có thể có nhiều nhân viên có cùng tên và họ. Do đó, phương thức filter() trả về một QuerySet.

Để lấy hàng đầu tiên trong QuerySet, mình sử dụng phương thức first(). Phương thức first() trả về một instance của lớp Employee.

SELECT "hr_employee"."id",
       "hr_employee"."first_name",
       "hr_employee"."last_name",
       "hr_employee"."contact_id"
  FROM "hr_employee"
#Bài viết này được đăng tại freetuts.net
 WHERE ("hr_employee"."first_name" = 'freetut' AND "hr_employee"."last_name" = '.net')
 ORDER BY "hr_employee"."id" ASC
 LIMIT 1

Lưu ý rằng truy vấn không lấy dữ liệu liên hệ từ bảng hr_contact. Nó chỉ lấy dữ liệu từ bảng hr_employee.

Khi bạn truy cập thuộc tính contact của nhân viên:

>>> e.contact

... Django thực thi lệnh SELECT thứ hai để lấy dữ liệu từ bảng hr_contact:

SELECT "hr_contact"."id",
#Bài viết này được đăng tại freetuts.net
       "hr_contact"."phone",
       "hr_contact"."address"
  FROM "hr_contact"
 WHERE "hr_contact"."id" = 1
 LIMIT 21
<Contact: 40812345678>

Dưới đây là cách lấy liên hệ với id là 1:

>>> c = Contact.objects.get(id=1)

Khi liên kết một liên hệ với một nhân viên, bạn có thể truy cập nhân viên từ đối tượng liên hệ:

>>> c.employee
<Employee: freetuts.net>

Django thực thi lệnh SELECT để lấy dữ liệu từ bảng hr_employee:

SELECT "hr_employee"."id",
       "hr_employee"."first_name",
       "hr_employee"."last_name",
       "hr_employee"."contact_id"
  FROM "hr_employee"
 WHERE "hr_employee"."contact_id" = 1
 LIMIT 21

Lưu ý rằng lớp Contact không có thuộc tính employee. Tuy nhiên, bạn có thể truy cập nhân viên nếu liên hệ đó được liên kết với một nhân viên.

Hãy tạo một liên hệ khác không liên kết với bất kỳ nhân viên nào:

>>> c = Contact(phone='4081111111', address='202 N 1st Street, San Jose, CA')
>>> c.save()

Django sẽ thực thi lệnh INSERT sau:

INSERT INTO "hr_contact" ("phone", "address")
VALUES ('4081111111', '202 N 1st Street, San Jose, CA') 
RETURNING "hr_contact"."id"

Nếu bạn tìm một liên hệ không liên kết với bất kỳ nhân viên nào và truy cập nhân viên, bạn sẽ gặp ngoại lệ RelatedObjectDoesNotExist.

Chọn các đối tượng liên quan trong bảng với Django

Đầu tiên, tạo một nhân viên mới:

>>> e = Employee(first_name='Jane', last_name='Doe')
>>> e.save()
#Bài viết này được đăng tại freetuts.net
INSERT INTO "hr_employee" ("first_name", "last_name", "contact_id")
VALUES ('Jane', 'Doe', NULL) RETURNING "hr_employee"."id"
Execution time: 0.003079s [Database: default]

Thứ hai, lấy tất cả các nhân viên:

>>> Employee.objects.all()

Django trả về hai nhân viên:

<QuerySet [<Employee: freetuts.net>, <Employee: freetuts.net>]>

Nếu bạn cần hiển thị tất cả nhân viên cũng như liên hệ của họ trên cùng một trang, bạn sẽ gặp vấn đề N+1 truy vấn:

  • Đầu tiên, bạn cần một truy vấn để lấy tất cả nhân viên (N nhân viên).
  • Thứ hai, bạn cần N truy vấn để chọn liên hệ liên quan của mỗi nhân viên.

Để tránh điều này, bạn có thể truy vấn tất cả nhân viên và liên hệ bằng một truy vấn duy nhất bằng cách sử dụng phương thức select_related():

>>> Employee.objects.select_related('contact').all()

Trong trường hợp này, Django thực thi lệnh LEFT JOIN sau:

SELECT "hr_employee"."id",
       "hr_employee"."first_name",
       "hr_employee"."last_name",
       "hr_employee"."contact_id",
       "hr_contact"."id",
       "hr_contact"."phone",
#Bài viết này được đăng tại freetuts.net
       "hr_contact"."address"
  FROM "hr_employee"
  LEFT OUTER JOIN "hr_contact"
    ON ("hr_employee"."contact_id" = "hr_contact"."id")
 LIMIT 21

Django sử dụng LEFT JOIN để trả về tất cả nhân viên từ bảng hr_employee và các liên hệ liên kết với nhân viên đã chọn:

<QuerySet [<Employee: freetuts.net>, <Employee: freetuts.net>]>

Kết bài

Qua hướng dẫn này, bạn đã nắm được cách sử dụng lớp OneToOneField để thiết lập và quản lý mối quan hệ một-một trong Django. Việc hiểu rõ và áp dụng mối quan hệ một-một giúp bạn xây dựng cơ sở dữ liệu hiệu quả hơn, đảm bảo dữ liệu liên kết chặt chẽ và dễ dàng truy xuất. Bằng cách nắm vững các khái niệm và kỹ thuật này, bạn có thể tạo ra các ứng dụng Django mạnh mẽ và linh hoạt, đáp ứng tốt hơn các yêu cầu phức tạp của dự án.

Hãy tiếp tục thực hành và tìm hiểu thêm các mối quan hệ khác như một-nhiều (one-to-many) và nhiều-nhiều (many-to-many) để hoàn thiện kỹ năng làm việc với Django ORM của bạn.

Cùng chuyên mục:

Tài liệu tham khảo nhanh về Regex trong Python

Tài liệu tham khảo nhanh về Regex trong Python

Hàm Flags của Regex trong Python

Hàm Flags của Regex trong Python

Hàm split() của Regex trong Python

Hàm split() của Regex trong Python

Hàm finditer() của Regex trong Python

Hàm finditer() của Regex trong Python

Hàm fullmatch() của Regex trong Python

Hàm fullmatch() của Regex trong Python

Hàm match() của Regex trong Python

Hàm match() của Regex trong Python

Hàm sub() của Regex trong Python

Hàm sub() của Regex trong Python

Hàm search() trong Python Regex

Hàm search() trong Python Regex

Hàm findall() của regex trong Python

Hàm findall() của regex trong Python

Lookbehind trong Regex của Python

Lookbehind trong Regex của Python

Lookahead trong Python Regex

Lookahead trong Python Regex

Alternation Regex trong Python

Alternation Regex trong Python

Tìm hiểu Backreferences trong regex của Python

Tìm hiểu Backreferences trong regex của Python

Nhóm Non-capturing trong Regex Python

Nhóm Non-capturing trong Regex Python

Các nhóm Capturing trong regex của Python

Các nhóm Capturing trong regex của Python

Sets và Ranges trong Regex của Python

Sets và Ranges trong Regex của Python

Lượng từ non-greed trong Regex của Python

Lượng từ non-greed trong Regex của Python

Chế độ Greedy trong Regex Python

Chế độ Greedy trong Regex Python

Các lượng từ trong Regex của Python

Các lượng từ trong Regex của Python

Regex Word Boundary trong Python

Regex Word Boundary trong Python

Top