Trong phần này chúng ta tiếp tục tìm hiểu về các tính chất của model trong Django.
Metadata
Các lớp trong Django có thể chứa các metadata (siêu dữ liệu), đây là các thông tin lưu trữ về các thiết lập của model, các thông tin này không được dùng để tạo bảng. Ví dụ:
from django.db import models class Ox(models.Model): horn_length = models.IntegerField() class Meta: ordering = ["horn_length"] db_table = "oxen"
Các siêu dữ liệu sẽ được lưu trong một lớp nội có tên là Meta
. Lớp này lưu trữ các thông tin cấu hình model, chẳng hạn như ordering
tức là sắp xếp dữ liệu được trả về mặc định theo cột nào, db_table
là tên bảng được tạo trong CSDL… bạn có thể xem danh sách tất cả các thuộc tính metadata ở đây.
Thừa kế model
Model trong Django cũng có thể thừa kế lẫn nhau. Chúng ta có thể viết các lớp model cơ sở (hay model cha) rồi sau đó viết các model khác thừa kế từ lớp model cơ sở này. Các lớp model cơ sở phải được thừa kế từ lớp django.db.models.Model
, rồi từ đó các lớp con sẽ kế thừa từ lớp cha này. Model cha có thể có bảng riêng hoặc không có.
Có 3 cách để một model được thừa kế trong Django:
- Thừa kế từ lớp trừu tượng.
- Thừa kế đa bảng.
- Dùng lớp Proxy.
Thừa kế từ lớp trừu tượng
Chúng ta sử dụng lớp trừu tượng khi muốn model cha chỉ có nhiệm vụ là lưu trữ các thông tin dùng chung trong các model con chứ không phải nhằm mục đích lưu các thông tin thật trong CSDL. Để một model là “trừu tượng” thì chúng ta gán thuộc tính abstract = True
của lớp nội Meta
.
Ví dụ:
from django.db import models class CommonInfo(models.Model): name = models.CharField(max_length=100) age = models.PositiveIntegerField() class Meta: abstract = True class Student(CommonInfo): home_group = models.CharField(max_length=5)
Model Student
thừa kế từ model trừu tượng là CommonInfo
, Student
sẽ bao gồm 3 trường là name
, age
và home_group
.
Khi chúng ta dùng lệnh manage.py migrate
, chỉ có model Student
mới được tạo bảng cùng với 3 trường của nó.
Khi một model con thừa kế model cha mà không khai báo lớp nội Meta
thì sẽ tự động thừa kế lớp nội đó từ model cha. Nhưng nếu muốn các model vẫn có thể có lớp nội riêng hoặc kế thừa và thêm các thuộc tính mới từ model cha.
Ví dụ:
from django.db import models class CommonInfo(models.Model): # ... class Meta: abstract = True ordering = ['name'] class Student(CommonInfo): # ... class Meta(CommonInfo.Meta): db_table = 'student_info'
Ở đây lớp con sẽ kế thừa cả lớp nội Meta
từ lớp cha. Nhưng có gì đó kì kì, nếu như vậy thì lớp con cũng sẽ trở thành lớp trừu tượng luôn sao vì nó sẽ kế thừa cả thuộc tính abstract
? Không! Trước khi kế thừa từ lớp nội Meta
thì Django sẽ tự động thiết lập biến abstract
thành False
ở lớp cha rồi sau đó sẽ cho kế thừa đến các lớp con, sau khi kế thừa xong thì Django mới thiết lập thuộc tính abstract
lại như cũ ở lớp cha. Tuy nhiên nếu muốn bạn vẫn có thể thiết lập abstract = True
ở các lớp con để tiếp tục kế thừa sâu hơn nữa.
Thừa kế đa bảng
Thừa kế đa bảng chỉ khác kiểu thừa kế lớp trừu tượng ở chỗ là lớp cha ở đây là lớp “thật” chứ không phải lớp trừu tượng. Tức là các model cha bây giờ sẽ có cả các bảng riêng của chúng trong CSDL. Ví dụ:
from django.db import models class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) class Restaurant(Place): serves_hot_dogs = models.BooleanField(default=False) serves_pizza = models.BooleanField(default=False)
Chúng ta có 2 model là Place
và Restaurant
kế thừa từ Place
. Khác với kiểu kế thừa từ lớp trừu tượng, ở lớp trừu tượng thì các trường của lớp cha sẽ nằm cùng bảng với các lớp con vì lớp cha không được tạo bảng riêng, với thừa kế đa bảng thì các lớp con cũng sẽ kế thừa các trường của lớp cha nhưng các trường của lớp cha sẽ nằm trong bảng riêng của lớp cha.
>>> Place.objects.filter(name="Bob's Cafe") >>> Restaurant.objects.filter(name="Bob's Cafe")
Chúng ta vẫn có thể truy xuất các thông tin từ cả 2 lớp con và lớp cha như thường.
Lớp Proxy – lớp ủy quyền
Đôi khi bạn không muốn các lớp con có thêm các thuộc tính mới mà chỉ muốn có các phương thức mới, với 2 kiểu kế thừa ở trên bạn vẫn có thể làm được điều này nhưng Django sẽ tạo ra các bảng CSDL mới mặc dù không có trường nào cả, làm như thế sẽ lãng phí bộ nhớ và cũng khá kì cục.
Do đó Django cho phép chúng ta tạo ra các lớp Proxy (lớp ủy quyền), các lớp này kế thừa từ lớp cha nhưng không được tạo bảng mới trong CSDL, những lớp con này sẽ chỉ dùng để lưu các phương thức mới chứ không lưu các thuộc tính mới. Bạn có thể thực hiện thêm, sửa, xóa dữ liệu được kế thừa từ lớp cha, những dữ liệu này vẫn lưu ở bảng cha.
Ví dụ:
from django.db import models class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) class MyPerson(Person): class Meta: proxy = True def do_something(self): # ... pass
Chúng ta tạo lớp Person
và lớp proxy MyPerson
kế thừa từ lớp Person
. Chúng ta tạo một lớp Proxy bằng cách khai báo lớp nội Meta
và đưa vào thuộc tính proxy = True
trong lớp Meta
này.
>>> p = Person.objects.create(first_name="foobar") >>> MyPerson.objects.get(first_name="foobar") <MyPerson: foobar>
Bất cứ thao tác cập nhật dữ liệu nào từ lớp MyPerson cũng sẽ là thao tác trực tiếp với dữ liệu từ lớp Person.
class OrderedPerson(Person): class Meta: ordering = ["last_name"] proxy = True
Bạn vẫn có thể định nghĩa lại các thông tin khác trong lớp Meta
như sử dụng thuộc tính ordering
chẳng hạn… vì bản thân các thuộc tính trong lớp Meta cũng không được dùng để tạo bảng mà nó chỉ là các thông tin cấu hình model thôi.
Đa thừa kế
Vì Django được phát triển từ Python nên các model trong Django có thể thừa kế từ nhiều model cha. Lớp Meta
của model con chỉ có thể thừa kế từ lớp Meta của lớp cha đầu tiên.
Thường thì chúng ta cũng ít khi thừa kế từ nhiều bảng vì càng thừa kế nhiều thì sẽ càng làm cho ứng dụng trở nên phức tạp, khó quản lý.
Ngoài ra khi sử dụng đa thừa kế mà các model cha có cùng chung tên thuộc tính khóa chính thì Django sẽ báo lỗi. Do đó khi sử dụng đa thừa kế, chúng ta nên khai báo các trường làm khóa chính một cách rõ ràng. Ví dụ:
class Article(models.Model): article_id = models.AutoField(primary_key=True) ... class Book(models.Model): book_id = models.AutoField(primary_key=True) ... class BookReview(Book, Article): pass
Trong đoạn code trên model BookReview
thừa kế từ 2 model cha là Article
và Book
, cả 2 lớp cha đều có tên thuộc tính khóa chính khác nhau.
Hoặc nếu không muốn phải khai báo các khóa chính thì chúng ta có thể khai báo các model cha kế thừa lại từ một model khác để dùng chung khóa chính:
class Piece(models.Model): pass class Article(Piece): ... class Book(Piece): ... class BookReview(Book, Article): pass