Học Django trong 14 ngày - Ngày 2: Mô hình và Cơ sở dữ liệu
By JoeVu, at: 08:58 Ngày 15 tháng 6 năm 2023
Thời gian đọc ước tính: __READING_TIME__ minutes


Models trong Django được sử dụng để định nghĩa cấu trúc và hành vi của dữ liệu được lưu trữ trong cơ sở dữ liệu. Hiểu cách làm việc với models là điều cần thiết để xây dựng các ứng dụng web mạnh mẽ và có khả năng mở rộng. Vì vậy, hãy cùng tìm hiểu về thế giới của Django models!
Mã nguồn mẫu được thêm vào ở đây
1. Giới thiệu về cơ sở dữ liệu và models trong Django
Hỗ trợ cơ sở dữ liệu của Django dựa trên lớp ánh xạ đối tượng-quan hệ (ORM) mạnh mẽ của nó. ORM tách biệt các phức tạp khi tương tác với các hệ quản trị cơ sở dữ liệu (DBMS) khác nhau và cho phép các nhà phát triển làm việc với cơ sở dữ liệu bằng mã Python thay vì viết các truy vấn SQL thô (điều này KHÔNG được khuyến nghị dù bằng cách nào).
Django hỗ trợ nhiều cơ sở dữ liệu quan hệ khác nhau, bao gồm PostgreSQL, MySQL, SQLite và Oracle, cùng nhiều loại khác. Tính linh hoạt này cho phép các nhà phát triển lựa chọn backend cơ sở dữ liệu phù hợp nhất với yêu cầu của dự án. Cơ sở dữ liệu Django được khuyến nghị là PostgreSQL.
Trong đó, một model cơ sở dữ liệu model là một lớp Python đại diện cho một bảng cơ sở dữ liệu. Nó định nghĩa các trường và phương thức để tương tác với cơ sở dữ liệu bên dưới. Django models cung cấp một cách tiếp cận cấp cao và trực quan để làm việc với cơ sở dữ liệu, giúp dễ dàng quản lý và thao tác dữ liệu hơn.
Sau đó, chúng ta sẽ thảo luận về một số chủ đề nâng cao với các gói bổ sung và hữu ích
- Các trường
- https://github.com/respondcreate/django-versatileimagefield: Gói ImageField với nhiều tính năng
- https://github.com/romgar/django-dirtyfields: Theo dõi các thay đổi của phiên bản model và các giá trị cơ sở dữ liệu hiện tại đã được lưu trữ
- https://github.com/stefanfoulis/django-phonenumber-field: Các trường xác thực số điện thoại Python
- Models
- Cấu hình cơ sở dữ liệu
2. Tạo một model Django
Để tạo một model Django, bạn cần định nghĩa một lớp Python kế thừa từ lớp cơ sở django.db.models.Model
. Lớp này đại diện cho một bảng trong cơ sở dữ liệu. Mỗi thuộc tính của lớp đại diện cho một trường trong bảng.
Định nghĩa các trường
Các trường định nghĩa loại dữ liệu có thể được lưu trữ trong cơ sở dữ liệu. Django cung cấp nhiều loại trường khác nhau như CharField
, IntegerField
, DateField
, ForeignKey
và nhiều loại khác nữa. Bạn có thể chọn loại trường phù hợp dựa trên dữ liệu bạn muốn lưu trữ.
Các loại trường
Django cung cấp một loạt các loại trường để xử lý các loại dữ liệu khác nhau. Ví dụ: CharField
được sử dụng để lưu trữ văn bản, IntegerField
để lưu trữ số nguyên, DateField
để lưu trữ ngày tháng, v.v. Việc chọn loại trường chính xác đảm bảo tính toàn vẹn dữ liệu và khả năng lưu trữ hiệu quả.
Các tùy chọn trường
Các trường có thể có thêm tùy chọn để tùy chỉnh hành vi của chúng. Một số tùy chọn phổ biến bao gồm null
để chỉ định xem một trường có thể trống hay không, default
để cung cấp một giá trị mặc định cho trường, unique
để đảm bảo tính duy nhất và nhiều tùy chọn khác nữa. Các tùy chọn này cho phép bạn tinh chỉnh hành vi của models.
Một ví dụ về model được định nghĩa như bên dưới
from django.contrib.postgres.fields import ArrayField
from django.db import models
class Book(models.Model):
author = models.ForeignKey(
"Author",
on_delete=models.CASCADE,
related_name="jobs",
)
tags = ArrayField(
models.CharField(max_length=256, null=True, blank=True),
blank=True,
null=True,
)
description = models.TextField()
title = models.CharField(max_length=256, null=True, blank=True)
category = models.CharField(max_length=256, null=True, blank=True)
price = models.DecimalField(max_digits=None, decimal_places=None)
def is_comic(self):
return "comic" in self.category.lower()
3. Di chuyển cơ sở dữ liệu (Database migrations)
Khi bạn thực hiện các thay đổi đối với models, chẳng hạn như thêm hoặc sửa đổi các trường, Django cung cấp một hệ thống di chuyển để quản lý các thay đổi này trong schema cơ sở dữ liệu.
Tạo các di chuyển
Hệ thống di chuyển của Django cho phép bạn tự động tạo các câu lệnh SQL cần thiết để áp dụng các thay đổi model của bạn. Bạn có thể sử dụng lệnh makemigrations
để tạo các tệp di chuyển dựa trên các thay đổi được phát hiện trong models của bạn.
python manage.py makemigrations app
python manage.py makemigrations
Áp dụng các di chuyển
Sau khi bạn có các tệp di chuyển, bạn có thể áp dụng chúng vào cơ sở dữ liệu bằng lệnh migrate
. Điều này sẽ cập nhật schema cơ sở dữ liệu sao cho khớp với trạng thái hiện tại của models. Django theo dõi các di chuyển đã được áp dụng trong bảng django_migrations
trong cơ sở dữ liệu hiện tại, giúp dễ dàng quản lý các cập nhật schema.
python manage.py migrate <app> <migration_number>
Hoàn tác và khôi phục
Django cũng hỗ trợ hoàn tác và khôi phục các di chuyển. Nếu bạn gặp sự cố sau khi áp dụng một di chuyển, bạn có thể hoàn tác nó để khôi phục trạng thái trước đó. Ngoài ra, bạn có thể khôi phục một tập hợp các di chuyển để quay lại một thời điểm cụ thể.
python manage.py migrate <app> <migration_number>
python manage.py migrate bookstore 0001
python manage.py migrate bookstore zero # Hoàn tác về số 0
Các di chuyển giả lập (Fake migrations)
Đây là một tính năng quan trọng khi có một số xung đột giữa các cập nhật cơ sở dữ liệu thực tế và số di chuyển được lưu trữ trong bảng django_migrations
. Hãy tưởng tượng một trường hợp khi bạn đã thực hiện các thay đổi trong cơ sở dữ liệu do yêu cầu khẩn cấp từ khách hàng, nhưng bạn chưa tạo tập lệnh di chuyển và áp dụng nó vào cơ sở dữ liệu. Đây là lúc --fake migration
được sử dụng.
python manage.py migrate bookstore 0002 --fake # số di chuyển hiện tại là 0001, sau thay đổi này, nó sẽ là 0002 mà không cần thực hiện di chuyển thực tế.
Ưu điểm
-
Sự tiến hóa của Schema cơ sở dữ liệu: Điều này cung cấp sự tiến hóa liền mạch và được kiểm soát của schema cơ sở dữ liệu theo thời gian. Nó đơn giản hóa quá trình thực hiện các thay đổi đối với cấu trúc cơ sở dữ liệu mà không cần các tập lệnh SQL thủ công hoặc tạo lại toàn bộ cơ sở dữ liệu.
-
Kiểm soát phiên bản: Các di chuyển được lưu trữ dưới dạng các tệp có phiên bản trong codebase của dự án, giúp dễ dàng theo dõi và quản lý các thay đổi đối với schema cơ sở dữ liệu cùng với mã nguồn của ứng dụng. Điều này tạo điều kiện thuận lợi cho việc cộng tác, đánh giá code và hoàn tác nếu cần.
-
Di chuyển dữ liệu: Ngoài các thay đổi về cấu trúc, các di chuyển Django hỗ trợ di chuyển dữ liệu. Điều này cho phép bạn viết mã Python để di chuyển và biến đổi dữ liệu khi thực hiện các thay đổi schema, đảm bảo tính toàn vẹn dữ liệu trong quá trình di chuyển.
-
Quản lý phụ thuộc: Các di chuyển hỗ trợ các phụ thuộc giữa các tệp di chuyển khác nhau. Điều này có nghĩa là bạn có thể xác định thứ tự áp dụng các di chuyển, xử lý các trường hợp phức tạp khi một di chuyển phụ thuộc vào việc hoàn thành một di chuyển khác.
-
Hoàn tác và khôi phục: Điều này cho phép bạn thực hiện/hoàn tác các thay đổi đối với schema cơ sở dữ liệu. Điều này có thể hữu ích trong trường hợp một di chuyển gây ra sự cố hoặc khi bạn cần quay lại trạng thái trước đó.
Nhược điểm
-
Các di chuyển phức tạp: Trong một số trường hợp, đặc biệt là khi xử lý các thay đổi cơ sở dữ liệu phức tạp, việc viết và quản lý các di chuyển có thể trở nên khó khăn. Xử lý các trường hợp đặc biệt, chẳng hạn như đổi tên hoặc thay đổi các trường hiện có, có thể yêu cầu can thiệp thủ công hoặc các tập lệnh di chuyển tùy chỉnh. Một số di chuyển tùy chỉnh có thể bị hỏng sau vài năm do nhiều cập nhật ràng buộc cơ sở dữ liệu.
-
Ảnh hưởng đến hiệu suất: Các di chuyển có thể ảnh hưởng đến hiệu suất ứng dụng trong quá trình di chuyển, đặc biệt là khi xử lý các tập dữ liệu lớn. Việc áp dụng các di chuyển phức tạp hoặc di chuyển một lượng lớn dữ liệu có thể yêu cầu xem xét kỹ lưỡng và tối ưu hóa để giảm thiểu thời gian chết và suy giảm hiệu suất. Có một mẹo để tránh điều đó bằng cách di chuyển hiệu suất trong các tác vụ nền (Celery + Redis)
-
Tương tác với dữ liệu hiện có: Di chuyển dữ liệu hiện có có thể khó khăn, đặc biệt là khi xử lý các biến đổi dữ liệu hoặc di chuyển dữ liệu yêu cầu logic phức tạp. Việc đảm bảo tính toàn vẹn và nhất quán dữ liệu trong quá trình di chuyển có thể yêu cầu lập kế hoạch và thử nghiệm cẩn thận.
-
Phụ thuộc bên ngoài: Các di chuyển có thể dựa trên các phụ thuộc hoặc yếu tố bên ngoài bên ngoài hệ thống di chuyển. Các thay đổi đối với các hệ thống bên ngoài, chẳng hạn như máy chủ cơ sở dữ liệu hoặc thư viện, đôi khi có thể gây ra các vấn đề về khả năng tương thích và yêu cầu thêm nỗ lực để giải quyết.
4. Truy vấn cơ sở dữ liệu
Django cung cấp một ORM mạnh mẽ (Ánh xạ đối tượng-quan hệ) cho phép bạn truy vấn cơ sở dữ liệu bằng mã Python. ORM tách biệt engine cơ sở dữ liệu cơ bản, giúp dễ dàng viết các truy vấn cơ sở dữ liệu theo cách không phụ thuộc vào cơ sở dữ liệu.
Các truy vấn cơ bản
Bạn có thể truy xuất các đối tượng từ cơ sở dữ liệu bằng thuộc tính objects
của một lớp model. Ví dụ: MyModel.objects.all()
trả về tất cả các đối tượng của lớp MyModel
. Bạn cũng có thể lọc các đối tượng dựa trên các tiêu chí cụ thể bằng phương thức filter()
.
Book.objects.filter(author_id__in=[1, 2, 3]) # Điều này trả về tất cả các sách có id tác giả là 1,2,3
Book.objects.filter(author__name__contains="Joe", category__icontains="comic") # Điều này trả về tất cả các sách có tên tác giả chứa "Joe" và thể loại chứa "comic" không phân biệt chữ hoa chữ thường.
Lọc và sắp xếp
Django cung cấp nhiều phương thức để lọc và sắp xếp kết quả truy vấn. Bạn có thể sử dụng các phương thức như filter()
, exclude()
và order_by()
để thu hẹp kết quả và chỉ định thứ tự mong muốn.
Book.objects.filter(author__name__contains="Joe", category__icontains="comic").exclude(author__name__contains="Vu").order_by("-id") # Điều này trả về tất cả các sách có tên tác giả chứa "Joe" và thể loại chứa "comic" không phân biệt chữ hoa chữ thường, nhưng loại trừ những cuốn sách có tên tác giả chứa "Vu" phân biệt chữ hoa chữ thường. Danh sách trả về được sắp xếp theo "ID" giảm dần.
Tổng hợp và chú thích
ORM của Django cũng hỗ trợ tổng hợp và chú thích để thực hiện các phép tính và tổng hợp trên kết quả truy vấn. Bạn có thể sử dụng các phương thức như count()
, sum()
, avg()
và annotate()
để truy xuất dữ liệu tổng hợp từ cơ sở dữ liệu.
Book.objects.aggregate(count=Count("id"))
Book.objects.aggregate(total_price=Sum("price"))
5. Mối quan hệ giữa các models
Django cho phép bạn định nghĩa các mối quan hệ giữa các models, cho phép bạn thiết lập các kết nối và liên kết giữa các thực thể khác nhau trong ứng dụng của bạn.
Mối quan hệ một-một
Mối quan hệ một-một là một loại mối quan hệ phổ biến, trong đó mỗi bản ghi trong một model được liên kết với chính xác một bản ghi trong một model khác. Bạn có thể định nghĩa mối quan hệ một-một bằng cách sử dụng kiểu trường OneToOneField
.
Mối quan hệ một-nhiều
Mối quan hệ một-nhiều thể hiện mối quan hệ mà một bản ghi trong một model có thể được liên kết với nhiều bản ghi trong một model khác. Điều này được thực hiện bằng cách sử dụng kiểu trường ForeignKey
.
Mối quan hệ nhiều-nhiều
Mối quan hệ nhiều-nhiều là một mối quan hệ phức tạp hơn, trong đó nhiều bản ghi trong một model có thể được liên kết với nhiều bản ghi trong một model khác. Django cung cấp kiểu trường ManyToManyField
để xử lý loại mối quan hệ này.
6. Kế thừa model
Django hỗ trợ kế thừa model, cho phép bạn tạo các model chuyên biệt hơn dựa trên các model hiện có.
Các lớp cơ sở trừu tượng
Bạn có thể định nghĩa một lớp cơ sở trừu tượng làm lớp cha cho các model khác. Các lớp cơ sở trừu tượng chỉ được sử dụng cho mục đích kế thừa và không được tạo thành các bảng riêng biệt trong cơ sở dữ liệu.
from django.db import models
class BaseModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
@property
def added(self):
return self.created_at
@property
def updated(self):
return self.updated_at
@property
def created_by(self):
if hasattr(self, "added_by"):
return self.added_by
return None
Kế thừa đa bảng
Với kế thừa đa bảng, mỗi model trong phân cấp kế thừa được lưu trữ dưới dạng một bảng riêng biệt trong cơ sở dữ liệu. Django tự động tạo các mối quan hệ giữa các bảng này để duy trì cấu trúc kế thừa.
Các model proxy
Các model proxy là một dạng kế thừa model khác, trong đó bạn có thể tạo một model proxy hoạt động giống như model gốc nhưng với một số sửa đổi. Các model proxy rất hữu ích để thêm các phương thức bổ sung hoặc thay đổi hành vi mặc định của một model.
from django.contrib.postgres.fields import ArrayField
from django.db import models
class Book(models.Model):
author = models.ForeignKey(
"Author",
on_delete=models.CASCADE,
related_name="jobs",
)
tags = ArrayField(
models.CharField(max_length=256, null=True, blank=True),
blank=True,
null=True,
)
description = models.TextField()
title = models.CharField(max_length=256, null=True, blank=True)
category = models.CharField(max_length=256, null=True, blank=True)
price = models.DecimalField(max_digits=None, decimal_places=None)
def is_comic(self):
return "comic" in self.category.lower()
class KidBook(Book):
class Meta:
proxy = True
def for_kid(self):
return True
7. Các thực hành tốt nhất cho Django models
-
Quy ước đặt tên
- Sử dụng danh từ số ít cho tên lớp model và làm cho chúng mô tả và trực quan.
- Sử dụng chữ thường và dấu gạch dưới cho tên trường.
- Cân nhắc sử dụng tên rõ ràng và có ý nghĩa cho các trường để cải thiện khả năng đọc code.
-
Các loại trường và ràng buộc
- Chọn các loại trường phù hợp thể hiện chính xác dữ liệu đang được lưu trữ.
- Thêm các ràng buộc như
null=True
,blank=True
vàunique=True
để đảm bảo tính toàn vẹn dữ liệu. - Sử dụng
ForeignKey
vàManyToManyField
để thiết lập mối quan hệ giữa các models.
-
Chỉ mục
- Xác định các trường thường được sử dụng để lọc hoặc tìm kiếm và cân nhắc thêm chỉ mục cơ sở dữ liệu để cải thiện hiệu suất truy vấn.
- Sử dụng tùy chọn
db_index=True
của Django cho các trường thường được sử dụng trong truy vấn.
-
Phương thức và thuộc tính model
- Định nghĩa các phương thức trên models để đóng gói logic nghiệp vụ hoặc thực hiện các thao tác phức tạp liên quan đến dữ liệu của model.
- Sử dụng các thuộc tính để tính toán các giá trị dẫn xuất hoặc cung cấp quyền truy cập thuận tiện vào dữ liệu liên quan.
-
Tùy chọn Meta
- Sử dụng lớp
Meta
của Django để cung cấp các tùy chọn và siêu dữ liệu bổ sung cho models. - Chỉ định thứ tự của kết quả truy vấn bằng thuộc tính
ordering
. - Định nghĩa các ràng buộc duy nhất bằng
unique_together
để thực thi các kết hợp trường duy nhất.
- Sử dụng lớp
-
Sử dụng Migrations
- Sử dụng hệ thống di chuyển của Django để quản lý các thay đổi đối với schema cơ sở dữ liệu theo thời gian.
- Tạo và áp dụng các di chuyển bất cứ khi nào có thay đổi đối với models hoặc schema cơ sở dữ liệu.
-
Kiểm thử
- Viết các bài kiểm tra đơn vị cho models để đảm bảo hành vi và tương tác của chúng là chính xác.
- Kiểm tra các phương thức, thuộc tính và mối quan hệ của model để xác minh chức năng của chúng.
-
Tổ chức code
- Tổ chức các models thành các mô-đun hoặc tệp riêng biệt dựa trên chức năng hoặc miền liên quan.
- Cân nhắc sử dụng cấu trúc ứng dụng của Django để nhóm các models một cách logic.
-
Tài liệu
- Thêm nhận xét và docstrings vào models, trường và phương thức để cung cấp tính rõ ràng và làm cho code dễ hiểu hơn.
- Tài liệu hóa bất kỳ giả định, ràng buộc hoặc cân nhắc đặc biệt nào liên quan đến thiết kế hoặc việc sử dụng model.
-
Tối ưu hóa hiệu suất
- Sử dụng các phương thức
select_related
vàprefetch_related
của Django để giảm thiểu số lượng truy vấn cơ sở dữ liệu và tối ưu hóa việc truy xuất dữ liệu liên quan. - Lưu ý về các truy vấn cơ sở dữ liệu khi làm việc với các tập dữ liệu lớn và cân nhắc sử dụng phân trang hoặc các chiến lược khác để giới hạn kết quả truy vấn.
- Sử dụng các phương thức
-
Model nên lớn
- Lớp Model nên lớn, phức tạp để các phương thức của nó có thể được gọi ở nhiều nơi khác nhau, cải thiện code sạch và code có thể tái sử dụng
-
Mẹo
- Sử dụng
cached_property
khi bạn muốn tiết kiệm tài nguyên tính toán - Sử dụng Django Signal hoặc ghi đè save nếu có thể để cải thiện các ứng dụng tách rời
- Sử dụng
8. Các chủ đề nâng cao
8.1 Signal
Django Signal cho phép các thành phần khác nhau của một ứng dụng Django gửi và nhận thông báo về các sự kiện cụ thể, cho phép ghép nối lỏng lẻo và thúc đẩy tính mô đun bằng cách tạo điều kiện thuận lợi cho việc giao tiếp giữa các phần ứng dụng mà không có sự phụ thuộc trực tiếp. Cơ chế mạnh mẽ này có thể được sử dụng để mở rộng chức năng, tích hợp với các ứng dụng của bên thứ ba và thực hiện các tác vụ không đồng bộ, làm cho Django Signals trở thành một công cụ có giá trị cho các nhà phát triển.
Tuy nhiên, điều này cũng có nghĩa là làm tăng độ phức tạp của ứng dụng, hãy lưu ý điều đó.
Ưu điểm
- Ghép nối lỏng lẻo: Signal cho phép code mô đun và dễ bảo trì bằng cách tách rời các thành phần ứng dụng.
- Khả năng mở rộng: Các tín hiệu và bộ thu tùy chỉnh tạo điều kiện thuận lợi cho việc thêm chức năng mới một cách dễ dàng.
- Thực thi không đồng bộ: Tính năng này hỗ trợ xử lý nền để cải thiện hiệu suất.
- Tích hợp với các ứng dụng của bên thứ ba: Tính năng này cho phép tích hợp liền mạch với các thành phần bên ngoài.
Nhược điểm
- Độ phức tạp: Signals có thể làm cho codebase khó hiểu hơn và theo dõi các tương tác.
- Thiếu tính rõ ràng: Việc giao tiếp giữa các thành phần không rõ ràng trong code.
- Ảnh hưởng tiềm tàng đến hiệu suất: Xử lý một số lượng lớn tín hiệu và bộ thu có thể gây ra chi phí.