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ằngNULL
.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épNULL
hayNOT 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ườngAutoField
để 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 name
là Apple, 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 name
là Pear.
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 Person
và Group
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 ManyToManyField
là through
.
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
: person
và group
.
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 Group
là beatles
, trong đó bao gồm 2 Person
là ringo
và paul
, mối quan hệ giữa ringo
và beatles
được lưu trong m1
, mối quan hệ giữa paul
và beatles
đượ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, )