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

4.8/5 - (22 votes)

Trong các bài trước chúng ta đã tìm hiểu sơ qua về model trong Django, trong phần này chúng ta sẽ tìm hiểu kĩ hơn.

Model trong Django là một cách định nghĩa về cách lưu trữ thông tin, một model bao gồm các trường và các hành động về dữ liệu mà bạn sẽ lưu trữ, định nghĩa này không khác gì định nghĩa lớp trong lập trình hướng đối tượng cả, nói cách khác Model chính là cách gọi khác của Class trong Django. Mỗi model sẽ đại diện cho một bảng trong cơ sở dữ liệu.

Model trong Django có các tính chất sau:

  • Mỗi model phải được kế thừa từ lớp django.db.models.Model.
  • Mỗi thuộc tính của model đại diện cho một trường trong cơ sở dữ liệu.
  • Từ đó Django sẽ tự động tạo các hàm thao tác với cơ sở dữ liệu cho bạn.

Ví dụ

Chúng ta định nghĩa model (class) Person có 2 thuộc tính là first_name và last_name:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

first_name và last_name là một trường của model – một thuộc tính của một lớp – một cột trong một bảng CSDL.

Tương ứng với model Person trên thì Django sẽ tạo ra một bảng trong CSDL như sau:

CREATE TABLE myapp_person (
    "id" serial NOT NULL PRIMARY KEY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL
);

Lưu ý:

  • Tên của bảng được cấu thành từ <tên app>_<tên model> nhưng chúng ta có thể thiết lập trước để Django đặt theo tên do chúng ta quy định.
  • Trường id được Django tạo tự động và cũng có thể thiết lập trước.
  • Đoạn code SQL trên là theo cú pháp của CSDL PostgreSQL, tùy vào CSDL nào được dùng mà Django sẽ dùng cú pháp tương ứng.

Sử dụng model

Sau khi đã định nghĩa xong các model thì chúng ta phải thiết lập để Django sử dụng các model đó, bằng cách chỉnh sửa biến INSTALLED_APPS trong file settings.py.

Ví dụ nếu bạn định nghĩa model trong module myapp.models (được tạo ra bằng câu lệnh manage.py startapp) thì trong biến INSTALLED_APPS, bạn khai báo đường dẫn dẫn tới lớp cấu hình của ứng dụng, hoặc đường dẫn (theo cú pháp import của Python) dẫn tới package của ứng dụng như sau:

INSTALLED_APPS = [
    #...
    'myapp',
    #...
]

Sau khi đã khai báo trong file settings.py, bạn phải chạy 2 lệnh là manage.py makemigrations để bảo Django là bạn đã thay đổi cái gì đó trong cấu trúc CSDL, sau đó là lệnh manage.py migrate để thực hiện cập nhật lại CSDL.

Các trường

Thành phần quan trọng và cũng là thành phần bắt buộc phải có trong một model là danh sách các trường hay các thuộc tính của một lớp. Chú ý khi đặt tên bạn nhớ tránh đặt tên trùng với các từ khóa có sẵn trong Django.

Ví dụ:

from django.db import models

class Musician(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    instrument = models.CharField(max_length=100)

class Album(models.Model):
    artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)
    release_date = models.DateField()
    num_stars = models.IntegerField()

Mỗi trường phải là một đối tượng kế thừa từ lớp Field, đây là một lớp ảo trong Django. Các lớp Field sẽ cho Django biết kiểu dữ liệu của các cột trong bảng (chẳng hạn như INTEGER, VARCHAR, TEXT) và loại thẻ HTML được dụng để làm form nhập (vd <input type="text">, <select>...).

Django có rất nhiều các lớp kiểu dữ liệu Field có sẵn, bạn có thể xem thêm ở đây. Bạn cũng có thể viết một lớp Field cho riêng mình, nhưng mình sẽ đề cập đến trong bài khác.

Tham số trong Field

Mỗi lớp Field nhận một vài tham số khi khởi tạo. Ví dụ lớp CharField nhận tham số max_length để biết số lượng kí tự sẽ được dùng trong cột VARCHAR trong CSDL, đây là tham số bắt buộc phải có.

Ngoài các tham số bắt buộc thì còn có một số tham số tùy chọn nữa, ở đây mình chỉ giới thiệu một số:

  • null: có giá trị True hoặc False. Tham số này cho Django biết sẽ dùng giá trị NULL thay cho các bản ghi có giá trị rỗng, tức là trong CSDL mà kiểu chuỗi mà có giá trị "" thì sẽ thay bằng NULL.
  • blank: có giá trị True hoặc False. Tham số này cho Django biết cột tương ứng trong bảng CSDL được phép NULL hay NOT NULL.
  • default: thiết lập giá trị mặc định cho trường.
  • help_text: đây một giá trị chuỗi dùng để hiển thị một đoạn text ngắn mô tả về trường đó, vd như khi di chuột vào cột trên form HTML thì hiện ra đoạn text.
  • primary_key: True hoặc False, chỉ định trường đó có phải là khóa chính hay không. Như bạn đã biết, nếu bạn không chỉ định trường nào làm khóa chính thì Django sẽ tự động tạo một trường AutoField để làm khóa chính luôn. Có một điều nữa là khi bạn thay đổi giá trị khóa chính của một model trong Python thì Django không báo lỗi mà thay vào đó là tạo một đối tượng model mới với khóa chính mới, ví dụ:
from django.db import models

class Fruit(models.Model):
    name = models.CharField(max_length=100, primary_key=True)

Chúng ta tạo model Fruit với khóa chính là trường name.

>>> fruit = Fruit.objects.create(name='Apple')
>>> fruit.name = 'Pear'
>>> fruit.save()
>>> Fruit.objects.values_list('name', flat=True)
['Apple', 'Pear']

Sau khi tạo một đối tượng với nameApple, chúng ta thay đổi Apple thành Pear thì Django không đổi mà thay vào đó là tạo một đối tượng mới với namePear.

Khóa chính tự tạo

Khóa chính do Django tự tạo ra có dạng sau:

id = models.AutoField(primary_key=True)

Đây là một giá trị integer tự động tăng. Mỗi model chỉ được phép có một trường làm khóa chính.

Quan hệ

Sức mạnh của các cơ sở dữ liệu quan hệ nằm ở tính năng mối quan hệ giữa các bảng. Django hỗ trợ 3 loại quan hệ phổ biến của CSDL quan hệ là: many-to-one (một-nhiều), many-to-many (nhiều-nhiều), one-to-one (một-một).

Quan hệ Many-to-one

Chúng ta đã làm việc với kiểu quan hệ này trong các bài trước bằng cách dùng lớp django.db.models.ForeignKey. Tham số bắt buộc phải có của ForeignKey là model cha của nó.

Ví dụ chúng ta có một model là Car có model cha là Manufacturer (tiếng Anh nghĩa là nhà sản xuất), quan hệ này có nghĩa là một Manufacturer sản xuất nhiều Car nhưng một Car chỉ có một Manufacturer. Tương ứng chúng ta có đoạn code khai báo trong Django như sau:

from django.db import models

class Manufacturer(models.Models):
    #...
    pass

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    #...

Bạn còn có thể tạo mối quan hệ đệ quy, tức là một bảng tự quan hệ với chính nó.

Quan hệ Many-to-many

Để sử dụng mối quan hệ này thì chúng ta sử dụng lớp ManyToManyField với tham số bắt buộc là tên model mà nó sẽ trỏ đến.

Ví dụ chúng ta có 2 model là Pizza và Topping (nước sốt), một chiếc bánh pizza có thể có nhiều loại sốt và một loại sốt có thể được “xịt” lên nhiều bánh pizza.

from django.db import models

class Topping(models.Model):
    #...
    pass

class Pizza(models.Model):
    #...
    toppings = models.ManyToManyField(Topping)

Cũng giống như với ForeignKey, bạn cũng có thể dùng quan hệ đệ quy với ManyToManyField.

Bạn cũng chỉ nên cho một model trỏ đến model kia chứ không nên để 2 model trỏ đến nhau. Lý do tại sao chúng ta lại cho Pizza trỏ đến Topping chứ không phải ngược lại là vì tùy theo tình huống mà bạn chọn cho phù hợp, ví dụ như khi đi ăn pizza chúng ta thường mua một chiếc bánh rồi “xịt” nhiều loại nước sốt lên chứ không ai mua nhiều chiếc bánh rồi “xịt” mỗi bánh một loại sốt cả 🙂

Thuộc tính của một mối quan hệ many-to-many

Một mối quan hệ không đơn giản chỉ là quan hệ giữa bảng này với bảng kia, mà chúng còn có các thông tin riêng nữa.

Ví dụ chúng ta có model PersonGroup có quan hệ many-to-many, ý nghĩa là một người có thể tham gia nhiều group và một group có thể có nhiều người, ý nghĩa của quan hệ này không chỉ có bao nhiêu đó mà còn có các thông tin khác như người đó tham gia vào nhóm vào ngày nào, lý do tham gia chẳng hạn…

Để thêm các trường vào một mối quan hệ thì chúng ta sẽ dùng đến một tham số tùy chọn trong hàm khởi tạo ManyToManyFieldthrough.

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=128)

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')

class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

Chúng ta phải định nghĩa một model khác lưu trữ các thông tin của một mối quan hệ, tham số through sẽ nhận tên của model này.

Trong Django thì các model kiểu này (Membership) được gọi mà model trung gian (Intermediate model), trong đó bạn phải khai báo rõ ràng các model tham gia vào mối quan hệ này, ở đây là 2 đối tượng ForeignKey: persongroup.

Sau khi đã có lớp trung gian, chúng ta có thể tạo các mối quan hệ many-to-many.

>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
... date_joined=date(1962, 8, 16),
... invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
[<Person: Ringo Starr>]
>>> ringo.group_set.all()
[<Group: The Beatles>]
>>> m2 = Membership.objects.create(person=paul, group=beatles,
... date_joined=date(1960, 8, 1),
... invite_reason="Wanted to form a band.")
>>> beatles.members.all()
[<Person: Ringo Starr>, <Person: Paul McCartney>]

Chúng ta tạo 1 Groupbeatles, trong đó bao gồm 2 Personringopaul, mối quan hệ giữa ringobeatles được lưu trong m1, mối quan hệ giữa paulbeatles được lưu trong m2.

Đối với mối quan hệ many-to-many mà có sử dụng model trung gian thì chúng ta không thể sử dụng các phương thức như add(), create() hoặc dùng toán tử gán =.

# Error
>>> beatles.members.add(john)
# Error
>>> beatles.members.create(name="George Harrison")
# Error
>>> beatles.members = [john, paul, ringo, george]

Bởi vì bạn không thể tự nhiên mà tạo một mối quan hệ cho 2 đối tượng được, bạn phải khai báo rõ ràng các thông tin của mối quan hệ đó ra (bằng cách dùng hàm khởi tạo Membership()).

Sau khi đã tạo lập các mối quan hệ, chúng ta có thể truy vấn chúng như thường.

>>> Group.objects.filter(members__name__startswith='Paul')
[<Group: The Beatles>]
>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'

Mối quan hệ one-to-one

Để định nghĩa một mối quan hệ one-to-one thì chúng ta sử dụng lớp OneToOneField, lớp này cũng nhận tham số là tên của model mà nó sẽ trỏ tới.

Cũng giống như 2 loại mối quan hệ trên, chúng ta cũng có thể dùng quan hệ đệ quy với one-to-one.

Mối quan hệ này cũng ít được dùng nhiều nên mình không đi sâu ở đây.

Tham chiếu model thông qua file

Bạn có thể định nghĩa các lớp model trong các file khác nhau và khi cần tham chiếu đến chúng thì chỉ cần import vô là được.

from django.db import models
from geography.models import ZipCode

class Restaurant(models.Model):
    # ...
    zip_code = models.ForeignKey(
        ZipCode,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
    )

5 4 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.

1 Comment
Inline Feedbacks
View all comments
Lan Vy
Lan Vy
2 năm trước

Mình chưa bao giờ thấy trang web nào về Django mà giải thích hay đối với mình như vậy, cực kì cụ thể và dễ hiểu 1 cách nhanh chóng luôn. Bạn làm nên trang này thật tuyệt vời. Có 1 vài cái lỗi ở 2 bài viết Model nâng cao này là mấy link, tìm hiểu thêm ở ĐÂY đều bị Page not Found hết, mong các bạn có thể khắc phục lại. Cuối câu mình chúc các bạn luôn an lành <3