Django – Model

4.9/5 - (145 votes)

Trong phần này chúng ta sẽ học cách tạo các Model để có thể tương tác với cơ sở dữ liệu.

Thiết lập cơ sở dữ liệu

Trong thư mục mysite chứa một file tên là settings.py, file này chứa các thông tin cấu hình server của bạn.

Mặc định thì Django server sử dụng cơ sở dữ liệu SQLite, và trong bài này mình cũng sẽ sử dụng SQLite cho đơn giản, nếu bạn dùng SQLite thì bạn không cần quan tâm đến việc tạo CSDL và cấu hình user, nhưng nếu bạn muốn sử dụng CSDL khác thì trong file settings.py, bạn tìm đến đối tượng DATABASES và thay đổi các giá trị sau:

  • ENGINE: tên module dành cho từng CSDL, mặc định sử dụng SQLite
    • django.db.backends.sqlite3 – cơ sở dữ liệu  SQLite
    • django.db.backends.postgresql – cơ sở dữ liệu PostgreSQL
    • django.db.backends.mysql – cơ sở dữ liệu MySQL
    • django.db.backends.oracle – cơ sở dữ liệu Oracle
  • NAME: tên CSDL, mặc định là file db.sqlite3 được tạo ra ở thư mục gốc của server. Nếu bạn không dùng CSDL SQLite thì bạn phải tạo CSDL với tên trùng với NAME trong CSDL mà bạn dùng (bằng câu lệnh CREATE DATABASE <name>)

Ngoài ra nếu bạn không dùng SQLite thì bạn cũng phải cung cấp thêm các thông tin USER, PASSWORD, HOST nữa và user phải có quyền truy cập CSDL cũng như một số quyền như ghi, xem…

Trong file settings.py còn có một list có tên là INSTALLED_APPS, mặc định khi tạo một project, Django cung cấp cho chúng ta một số ứng dụng thường dùng trong list này. Trong bài sau chúng ta sẽ dùng một ứng dụng trong list này đó là django.contrib.admin.

Mỗi ứng dụng trong list này sẽ giúp Django tìm các model để tạo bảng tương ứng trong file CSDL, nhưng khi tạo project thì các bảng này không tự động được tạo trong file db.sqlite3, để tạo các bảng này thì chúng ta chạy lệnh sau:

C:\Project\mysite>python manage.py migrate

Lệnh migrate sẽ tìm các module được liệt kê trong list INSTALLED_APPS (trong file mysite/settings.py) và tạo các bảng CSDL tương ứng.

Tạo mô hình dữ liệu cho ứng dụng

Mô hình ở đây giống như lớp trong lập trình hướng đối tượng hoặc Model trong mô hình MVC vậy thôi, không có gì khó hiểu cả 🙂

Mặc định khi tạo một ứng dụng web trong một project Django thì Django đã tạo sẵn cho chúng ta một file có tên là models.py để chúng ta khai báo các mô hình trong này rồi nhưng bạn cũng có thể viết trong các file khác nếu thích.

from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

Chúng ta sẽ tạo 2 mô hình (2 lớp…) là QuestionChoice.

class Question(models.Model):
...
class Choice(models.Model):

Mỗi mô hình được tạo ra phải được kế thừa từ lớp django.db.models.Model.

question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
...
votes = models.IntegerField(default=0)

Mỗi thuộc tính trong mô hình được kế thừa từ một lớp Field, đây là một lớp ảo trong Django, lớp này lại có nhiều lớp kế thừa khác đại diện cho mỗi kiểu dữ liệu ví dụ như CharField là kiểu text, DateTimeField là kiểu DateTime

Tham số max_length là số lượng ký tự tối đa, đây là tham số bắt buộc phải có. Các tham số còn lại như default… là tham số tùy chọn, không có cũng được.

question = models.ForeignKey(Question, on_delete=models.CASCADE)

Ngoài các kiểu dữ liệu thường dùng thì chúng ta còn có kiểu khóa ngoại được định nghĩa trong lớp ForeignKey, tham số đầu tiên là bảng mà khóa ngoại này tham chiếu tới, on_delete=models.CASCADE tức là khi dữ liệu trong bảng cha có sự thay đổi thì dữ liệu trong bảng con cũng sẽ thay đổi theo, chẳng hạn như bản ghi trong bảng Question bị xóa thì các bản ghi trong bảng Choice có tham chiếu tới bản ghi trong bảng Question này cũng sẽ bị xóa.

Tạo bảng trong CSDL từ Model

Sau khi đã tạo các lớp mô hình trong Python, Django sẽ nhìn các thuộc tính trong từng lớp để tạo các bảng tương ứng trong CSDL và tạo thêm các lớp mới trong Python cung cấp các hàm để bạn thao tác với các bảng trong CSDL.

INSTALLED_APPS = [
    'polls.apps.PollsConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

Để Django tạo các bảng mới trong CSDL thì chúng ta phải khai báo trước trong list INSTALLED_APPS đã bằng cách thêm dòng polls.apps.PollsConfig.

C:\Project\mysite>python manage.py makemigrations polls

Tiếp theo bạn chạy lệnh makemigration polls để báo cho Django biết là bạn đã thay đổi một số mô hình, ở đây là thêm 2 lớp mới trong gói polls.

Migrations for 'polls':
  0001_initial.py:
    - Create model Choice
    - Create model Question
    - Add field question to choice

Django sẽ tạo một file python để lưu các thông tin về sự thay đổi này, ở đây là file 0001_initial.py.

C:\Project\mysite>python manage.py migrate
Operations to perform: 
Apply all migrations: admin, contenttypes, polls, auth, sessions
Running migrations:
Rendering model states... DONE
Applying polls.0001_initial... OK

Chúng ta chạy lại lệnh migrate để Django cập nhật lại CSDL. Lúc này CSDL chúng ta sẽ có thêm 2 bảng mới là polls_choicepolls_question (tên bảng được đặt theo cú pháp <tên package>_<tên lớp>).

Thao tác với các bảng

Sau khi Django đã tạo các bảng bạn có thể bắt đầu thực hiện các công việc thường dùng như thêm-sửa-xóa… bản ghi.

C:\Project\mysite>python manage.py shell

Bạn có thể sử dụng trình shell mà Django cung cấp sẵn trong file manage.py. Nếu bạn không muốn chạy trực tiếp trong Command Prompt mà muốn chạy trong file .py riêng thì trong file .py đấy bạn phải khai báo các dòng dưới đây:

import os
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings");
django.setup()

Các dòng này sẽ thiết lập biến môi trường DJANGO_SETTINGS_MODULE trỏ đến module mysite.settings, mục đích là để chỉ cho Python biết đường dẫn import. Mặc định khi chạy file manage.py từ cmd thì biến này sẽ được Django thiết lập luôn.

>>> from polls.models import Question, Choice
>>> Question.objects.all()
[]

Chúng ta import 2 lớp QuestionChoice. Phương thức Question.objects.all() liệt kê toàn bộ đối tượng Question đang có trong CSDL, hiện tại là không có bản ghi nào nên kết quả trả về list rỗng.

>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())

Chúng ta tạo một đối tượng Question và thiết lập pub_date là ngày giờ hiện tại của máy tính, chúng ta lấy thông tin này từ phương thức timezone.now() trong module django.utils.timezone.

>>> q.save()

Để lưu đối tượng Question mới này vào CSDL thì chỉ cần gọi phương thức save() là được.

>>> q.id
1

Khi viết lớp QuestionChoice chúng ta không cung cấp thuộc tính id hay bất cứ thuộc tính nào để làm khóa chính, vì vậy khi tạo CSDL Django sẽ tự động thêm 1 thuộc tính id để làm khóa chính và thuộc tính này sẽ tự động tăng. 

>>> q.question_text
What's new?
>>> q.question_text = "What's up?"
>>> q.save()

Bạn có thể thay đổi trực tiếp giá trị trên các đối tượng này sau đó gọi phương thức save() là dữ liệu trên CSDL sẽ được cập nhật.

from django.db import models
class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')
    def __str__(self):
        return self.question_text
class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    def __str__(self):
        return self.choice_text

Chúng ta override phương thức __str__(), nếu không khi gọi phương thức str() mặc định sẽ cho output khá kì quái.

Sau đó chúng ta tắt và mở lại shell, rồi chạy thử đoạn lệnh sau sẽ được kết quả khác:

>>> from polls.models import Question, Choice
>>> Question.objects.all()
[<Question: What's up?>]

Phương thức objects.all() sẽ gọi đến phương thức __str__().

>>> Question.objects.filter(id=1)
[<Question: What's up?>]
>>> Question.objects.filter(question_text__startswith='What') 
[<Question: What's up?>]

Bạn có thể lọc các bản ghi theo thuộc tính bằng phương thức filter().

>>> q = Question.objects.get(pk=1)

Bạn cũng có thể lấy từng bản ghi đơn lẻ bằng phương thức get(), ở trên chúng ta lấy theo khóa chính pk (Primary Key).

>>> q.choice_set.all()
[]

Bất cứ bảng nào có một bảng khác chứa khóa ngoại tham chiếu đến khóa chính của nó đều sẽ có một thuộc tính được tạo tự động là một tập hợp các đối tượng của bảng kia. Ở đây bảng Choice chứa khóa ngoại tham chiếu đến bảng Question, do đó bảng Question sẽ có một danh sách các đối tượng Choice, chúng ta có thể lấy danh sách này qua choice_set.all() (tên danh sách được đặt theo <tên bảng>_set). Ở đây chúng ta vẫn chưa tạo đối tượng Choice nào nên danh sách trả về rỗng.

>>> q.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> q.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky> 
>>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)

Chúng ta tạo 3 đối tượng Choice bằng phương thức choice_set.create().

>>> q.choice_set.all()
[<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]
>>> q.choice_set.count()
3

Chúng ta có thể lấy số lượng các bản ghi trong bảng bằng phương thức count().

>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()

Để xóa một bản ghi trong bảng thì chúng ta dùng phương thức delete().

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

15 Comments
Inline Feedbacks
View all comments
Do Quyet
Do Quyet
7 năm trước

xin chào admin,trước tiên rất cảm ơn ad đã làm những bài này giúp người đọc thấy dễ hiểu,rõ ràng.tuy nhiên phần cuối model này mình làm mãi không ra vấn đề đoạn

>>>from polls.models import Question, Choice

>>> Question.objects.all()
kết quả ko ra [] mặc dù đã override __str__.mong ad kiểm tra giúp.cảm ơn ad

Do Quyet
Do Quyet
7 năm trước
Reply to  Do Quyet

cảm ơn bạn,mình đã làm được.nhưng có 1 chút vấn đề khác là hiện tại mình chỉ mới view được 5 question mới nhất,vậy để view được choice khi click vào question ấy thì làm thế nào,mong ad hướng dẫn cụ thể.cảm ơn add

Phở Code
Phở Code
7 năm trước
Reply to  Do Quyet

click Question trong trang admin thì bạn có thể định nghĩa một lớp ModelAdmin rồi truyền vào trong phương thức admin.site.register(), bạn xem bài này https://phocode.com/django/django-tuy-chinh-trang-admin/

Loc Vu
Loc Vu
7 năm trước
Reply to  Do Quyet

vấn đề của bạn mình cũng gặp trong python2

và mình thêm dòng
@python_2_unicode_compatibletrước mỗi class trong polls/models.py thì ok
admin cập nhật thêm cho những ai khác có thể gặp lỗi này nhé

minh
minh
7 năm trước

Admin oi! khi mình chạy lệnh python “manage.py makemigrations polls”. Thì báo lỗi này:

File “manage.py”, line 10, in
execute_from_command_line(sys.argv)
……………………………
…………………..
Minh google nhung chưa fix được lỗi này!!

minh
minh
7 năm trước
Reply to  Phở Code

Thanks bac admin!!

abc
abc
6 năm trước

file 0001_initial.py
tạo ra như nào, nội dung, path?

AJackTi
AJackTi
6 năm trước

ad xem giúp e với khi e gõ vào như vầy: q.choice_set.create(choice_text=’Not much’, votes=0)
thì nó bị lỗi như vầy 🙁
TypeError Traceback (most recent call last)
in ()
—-> 1 q.choice_set.create(choice_text=”Not much”, votes=0)

Em không biết lỗi gì mong ad và các bạn khác giúp đỡ. Cảm ơn.

HUy
HUy
6 năm trước

from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField(‘date published’)
def __str__(self):
return self.question_text
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
def __str__(self):
return self.choice_text

Phần này thiếu đoạn code của votes này! Bạn nào mà copy thì chú ý không lỗi nhé!

DUY
DUY
3 năm trước
Reply to  HUy

Vậy phải thêm vào chỗ nào vậy bạn ?