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

3.8/5 - (47 votes)

Sau khi chúng ta đã tạo các lớp model, Django sẽ tự động tạo các phương thức mới trong model đó cho phép bạn thao tác với CSDL, bạn có thể tạo, sửa, xóa, cập nhật dữ liệu.

Trong bài này chúng ta sẽ tạo và sử dụng các lớp model sau đây để thực hành với các phương thức do Django cung cấp.

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __str__(self): 
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=50)
    email = models.EmailField()

    def __str__(self): 
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    mod_date = models.DateField()
    authors = models.ManyToManyField(Author)
    n_comments = models.IntegerField()
    n_pingbacks = models.IntegerField()
    rating = models.IntegerField()

    def __str__(self): 
        return self.headline

Ở đây chúng ta có 3 model là Blog, AuthorEntry, các model này đại diện cho các lớp model cơ bản của một website blog.

Sau khi bạn đã tạo các lớp model trên thì bạn chạy 2 lệnh python manage.py makemigrationspython manage.py migrate để Django cập nhật các bảng mới trong CSDL. Sau đó bạn chạy trình shell bằng lệnh python manage.py shell để bắt đầu thao tác với CSDL (nếu bạn muốn chạy trong file .py riêng thì phải thiết lập biến môi trường DJANGO_SETTINGS_MODULE đến mysite.settings trước).

Tạo đối tượng

Mỗi bản ghi hay mỗi dòng trong từng bảng CSDL sẽ tương ứng với một đối tượng cụ thể. Để tạo một bản ghi trong CSDL thì chúng ta chỉ cần tạo một đối tượng tương ứng với trong Python rồi gọi phương thức save() là xong. Vd:

>>> from blog.models import Blog
>>> b = Blog(name='J.R.R Blog', tagline='All the latest J.R.R news.')
>>> b.save()

Đoạn code trên sẽ thực hiện câu lệnh INSERT, bạn phải gọi phương thức save() thì dữ liệu mới được cập nhật lên CSDL.

Ngoài ra nếu bạn muốn vừa tạo đối tượng vừa cập nhật thẳng lên CSDL luôn thì dùng phương thức create() trong đối tượng objects, đối tượng này là một đối tượng tĩnh do Django tạo ra cho chúng ta để đơn giản hóa việc thao tác với CSDL. Vd:

>>> Blog.objects.create(name='J.R.R Blog', tagline='All the latest J.R.R news.')

Cập nhật đối tượng

Bạn có thể thay đổi giá trị của các đối tượng trong Python rồi chỉ cần gọi phương thức save() là Django sẽ cập nhật mới trong CSDL.

>>> b.name = 'New name'
>>> b.save()

Cập nhật các thuộc tính khóa ngoại

Chúng ta tạo đối tượng Author mới như sau:

>>> from blog.models import Entry, Author
>>> a = Author(name='J.R.R. Tolkien', email='jrr@middleeast.com')
>>> a.save()

Bây giờ chúng ta sẽ tạo đối tượng Entry, đối tượng này chứa 2 thuộc tính khóa ngoại, một thuộc tính many-to-oneblogmany-to-manyauthors.

>>> from django.utils import timezone
>>> e = Entry(blog=b,
... headline='The lord of the rings', 
... body_text='Chapter 0', 
... pub_date=timezone.now(), 
... mod_date=timezone.now(), 
... n_comments=0, 
... n_pingbacks=0, 
... rating=0)
>>> e.save()

Việc gán giá trị cho các thuộc tính khóa ngoại many-to-one (ForeignKey) rất đơn giản, bạn chỉ cần gán thuộc tính khóa ngoại với đối tượng cần trỏ đến là xong, trong đoạn code trên chúng ta gán khóa ngoại blog = b, trong đó b là đối tượng Blog mà chúng ta đã tạo ra ở đầu bài.

>>> e.blog = Blog.create(...)
>>> e.save()

Khi cần thay đổi chúng ta cũng chỉ cần dùng phép gán như thường là được.

Đối với các thuộc tính khóa ngoại many-to-many thì chúng ta không được phép gán trực tiếp như đối với ForeignKey mà phải dùng phương thức riêng của thuộc tính khóa ngoại đó, bởi vì khóa ngoại ForeignKey chỉ lưu trữ các đối tượng đơn lẻ trong khi khóa ngoại ManyToManyField thì lại lưu trữ một danh sách các đối tượng khác nhau:

>>> e.authors.add(a)
>>> e.save()

Chúng ta lưu đối tượng a vào trong khóa ngoại authors, trong đó a là đối tượng Author mà chúng ta đã tạo ở trên.

Truy xuất dữ liệu

Khi chúng ta tạo một đối tượng thuộc lớp model do chúng ta tự định nghĩa, chẳng hạn như b=Blog(...), thì mặc định đối tượng b sẽ được Django cung cấp cho các phương thức để thao tác với CSDL trên chính bản ghi của b, chúng ta đã học một số phương thức này trong các ví dụ trên.

Ngoài cách thao tác với dữ liệu thông qua từng bản ghi như trên thì Django còn cung cấp cho chúng ta một đối tượng thuộc lớp django.db.models.manager.Manager để chúng ta thao tác với chính bảng được tạo ra đó, đối tượng này mặc định được đặt tên là objects.

>>> Blog.objects
>>> <django.db.models.manager.Manager object at 0x....>

Lấy toàn bộ dữ liệu

Khi chúng ta lấy dữ liệu từ CSDL về thì objects sẽ trả về một đối tượng QuerySet.

>>> Blog.objects.all()
>>> [<Blog: New name>]

Để lấy toàn bộ bản ghi hiện có trong bảng thì chúng ta dùng phương thức all().

>>> type(Blog.objects.all())
<class 'django.db.models.query.QuerySet'>

Như mình đã nói, danh sách được trả về là một đối tượng QuerySet.

Lọc dữ liệu

Có 2 phương thức hỗ trợ lọc dữ liệu là filter()exclude(). Tham số của 2 phương thức là một biểu thức tìm kiếm, trong đó phương thức filter() sẽ trả về dữ liệu khớp với biểu thức tìm kiếm, còn exclude() sẽ trả về dữ liệu không khớp. Chúng ta sẽ tìm hiểu thêm về các biểu thức tìm kiếm ở cuối bài.

Ví dụ:

>>> Entry.objects.filter(pub_date__year==2016)

Đoạn code trên sẽ lọc những bản ghi Entrypub_date (ngày đăng) vào năm 2016.

Bạn cũng có thể lọc theo nhiều điều kiện liên tiếp như sau:

>>> Entry.objects.filter(headline__startswith='What')
... .exclude(pub_date__gte=datetime.date.today())
... .filter(pub_date__gte=datetime(2016, 1, 30))

Đoạn code trên lọc các bản ghi có chuỗi bắt đầu là “What” và có pub_date nằm trong phạm vi từ ngày 30/1/2016 đến thời điểm hiện tại.

Lấy một dòng dữ liệu

Django cung cấp phương thức get() giúp bạn lấy một bản ghi duy nhất, tham số của phương thức này cũng là một biểu thức tìm kiếm như 2 phương thức filter()exclude(). Bạn cũng chỉ dùng get() khi biết dữ liệu trả về chỉ có 1 bản ghi thôi, nếu có nhiều bản ghi khớp với biểu thức tìm kiếm thì get() sẽ báo lỗi exception MultipleObjectsReturned.

>>> one_entry = Entry.objects.get(pk=1)

Một sự khác nhau nữa giữa get()filter()/exclude() là nếu get() không tìm thấy bản ghi nào thì sẽ trả về một lỗi exception là DoesNotExist còn 2 phương thức kia sẽ trả về một đối tượng QuerySet rỗng.

Ngoài 3 phương thức lọc dữ liệu trên là get(), filter()exclude() thì bạn có thể tìm hiểu thêm các phương thức khác tại đây.

Lọc số lượng bản ghi

Bạn có thể lọc số lượng bản ghi cần lấy theo cú pháp của Python. Ví dụ:

>>> Entry.objects.all()[:5]     # lấy 5 phần tử đầu tiên
>>> Entry.objects.all()[5:10]   # lấy các phần tử từ vị trí 5 đến 10

Python không hỗ trợ lọc theo chỉ số âm.

>>> Entry.objects.all()[-1]

Đoạn code trên sẽ báo lỗi.

Biểu thức tìm kiếm

Các biểu thức tìm kiếm sẽ thực hiện câu truy vấn SQL WHERE trong CSDL. Biểu thức tìm kiếm là tham số cho các phương thức lọc dữ liệu filter(), exclude()get().

Cú pháp lọc có dạng <tên thuộc tính>__<kiểu tìm kiếm>=<giá trị> (lưu ý ở đây có 2 dấu gạch dưới).

Ví dụ

>>> Entry.objects.filter(pub_date__lte='2016-01-01')

Đoạn code trên tương đương với câu lệnh SQL sau:

SELECT * FROM blog_entry WHERE pub_date <= '2016-01-01';

Trong biểu thức tìm kiếm thì <tên thuộc tính> bao giờ cũng là tên do chúng ta đặt khi định nghĩa model, ngoại trừ thuộc tính khóa ngoại phải thêm vào _id ở cuối tên. Ví dụ:

>>> Entry.objects.filter(blog_id=4)

Django cung cấp rất nhiều cú pháp <kiểu tìm kiếm> khác nhau, bạn có thể xem thêm ở đây. Ở đây mình giới thiệu một số.

  • exact: dữ liệu tìm được phải giống chính xác với giá trị cần tìm. Vd Entry.objects.get(headline__exact='Cat bites dog') chỉ trả về những bản ghi có headline“Cat bites dog“.
  • iexact: dữ liệu tìm được chỉ cần giống kí tự với giá trị cần tìm, không phân biệt chữ HOA-thường. Vd Blog.objects.get(name__iexact='beatles blog'), những bản ghi có headline“Beatles Blog”, “beatles blog” hoặc “BeAtlES blOG” đều được nhận.
  • contains: dữ liệu chỉ cần chứa chuỗi giá trị là được. Vd Entry.objects.get(headlines__contains='Lennon'). Ngoài ra còn có icontains cũng có chức năng như contains nhưng không phân biệt chữ HOA-thường.
  • startswith, endswith:  dữ liệu có chuỗi bắt đầu hoặc kết thúc giống với giá trị.

Tìm kiếm đa bảng

Các bảng trong CSDL có quan hệ với nhau nhờ vào các khóa ngoại, khi truy vấn dữ liệu, SQL cho phép bạn truy vấn các bản ghi có liên quan với nhau bằng cách nối các bảng lại bằng câu lệnh JOIN, Django cũng cho phép bạn làm điều đó, bạn chỉ cần đưa vào biểu thức tìm kiếm cú pháp <tên khóa ngoại>__<tên thuộc tính của bảng khác>=<giá trị>. Ví dụ:

>>> Entry.objects.filter(blog__name='Beatles Blog')

Câu lệnh trên sẽ tìm các bản ghi Entry có quan hệ với bảng Blog với name là “Beatles Blog”.

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

4 Comments
Inline Feedbacks
View all comments
newbie
newbie
7 năm trước

chào bạn,
DATABASE API này chỉ áp dụng cho CSDL mặc định của Django là SQLite hay là dùng cho cả các CSDL khác?

khanh
khanh
6 năm trước
kaysekay
kaysekay
2 năm trước

chỗ Lọc số lượng bản ghimình muốn lọc với điều kiện thời gian thêm vào mới nhất thì làm sao ạ?