Django – Model nâng cao – Phần 2

4.8/5 - (26 votes)

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, agehome_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à PlaceRestaurant 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à ArticleBook, 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
4.6 5 votes
Article Rating
Subscribe
Thông báo cho tôi qua email khi
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

5 Comments
Inline Feedbacks
View all comments
Đức PhạmTrung
Đức PhạmTrung
7 năm trước

Bài viết rất hay, mong anh viết tiếp các phần tiếp theo 🙂

doanhkiet2
doanhkiet2
3 năm trước

hay quá, trình hạn chế đọc doc tiếng anh không hiểu lắm xem được những bài như vầy thất tuyệt vời. cảm ơn tác giả.

hùng
2 năm trước
Reply to  Phở Code

mình tự tạo trường field riêng được không