News

Making Query Django

MAKING QUERIES

Chúng ta sẽ đề cập đến các mô hình sau, bao gồm một ứng dụng Weblog:
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=200)
    email = models.EmailField()

    def __str__(self):
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    mod_date = models.DateField()
    authors = models.ManyToManyField(Author)
    number_of_comments = models.IntegerField()
    number_of_pingbacks = models.IntegerField()
    rating = models.IntegerField()

    def __str__(self):
        return self.headline

Tạo đối tượng

Để tạo một đối tượng, hãy khởi tạo nó bằng cách sử dụng các đối số từ khóa cho lớp mô hình, sau đó gọi save()để lưu nó vào cơ sở dữ liệu.
>>> from blog.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
>>> b.save()

Lưu các thay đổi đối với các đối tượng 

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

Lưu các trường Foreignkey và ManytoManyField 

>>> from blog.models import Blog, Entry
>>> entry = Entry.objects.get(pk=1)
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()
Cập nhật một ManyToManyField hoạt động hơi khác một chút – sử dụng add() phương pháp trên trường để thêm bản ghi vào mối quan hệ. Ví dụ này thêm Author thể hiện joe vào entry đối tượng:
>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe)
Để thêm nhiều bản ghi vào một ManytoManyField , hãy bao gồm nhiều đối số trong lệnh gọi tới add(), như sau:
>>> john = Author.objects.create(name="John")
>>> paul = Author.objects.create(name="Paul")
>>> george = Author.objects.create(name="George")
>>> ringo = Author.objects.create(name="Ringo")
>>> entry.authors.add(john, paul, george, ringo)
Django sẽ cảnh báo nếu bạn cố gắng gán hoặc thêm một đối tượng không đúng loại.

Truy xuất các đối tượng cụ thể bằng bộ lọc 

Để tạo một tập hợp con như vậy, bạn tinh chỉnh ban đầu QuerySet, thêm các điều kiện lọc. Hai cách phổ biến nhất để tinh chỉnh QuerySet :
Ví dụ: để có được một QuerySet số mục blog từ năm 2006, hãy sử dụng filter() như sau:
Entry.objects.filter(pub_date__year=2006)
Với lớp người quản lý mặc định, nó giống như:
Entry.objects.all().filter(pub_date__year=2006)

Bộ lọc (filter)

>>> Entry.objects.filter(
...     headline__startswith='What'
... ).exclude(
...     pub_date__gte=datetime.date.today()
... ).filter(
...     pub_date__gte=datetime.date(2005, 1, 30)
... )
Thao tác này lấy đầu tiên QuerySet của tất cả các mục nhập trong cơ sở dữ liệu, thêm một bộ lọc, sau đó là một loại trừ, sau đó là một bộ lọc khác. Kết quả cuối cùng là QuerySet chứa tất cả các mục nhập có tiêu đề bắt đầu bằng “What”, được xuất bản từ ngày 30 tháng 1 năm 2005 đến ngày hôm nay.

Truy xuất một đối tượng duy nhất bằng get()

filter() sẽ luôn cung cấp cho bạn QuerySet, ngay cả khi chỉ một đối tượng phù hợp với truy vấn – trong trường hợp này, nó sẽ là một QuerySet chứa một phần tử duy nhất.
Nếu bạn biết chỉ có một đối tượng phù hợp với truy vấn của mình, bạn có thể sử dụng get(), phương thức sẽ trả về đối tượng trực tiếp:
>>> one_entry = Entry.objects.get(pk=1)

Giới hạn QuerySets

Ví dụ, nó trả về 5 đối tượng đầu tiên ( ):  LIMIT 5
>>> Entry.objects.all()[:5]
Điều này trả về các đối tượng thứ sáu đến thứ mười ( ):OFFSET 5 LIMIT 5
Ví dụ: điều này sẽ thực sự thực thi truy vấn để trả về danh sách mọi đối tượng thứ hai của 10 đối tượng đầu tiên:
>>> Entry.objects.all()[:10:2]

Các tra cứu mở rộng các mối quan hệ

Ví dụ này lấy tất cả các Entry đối tượng với một Blog mà name là :'Beatles Blog'
>>> Entry.objects.filter(blog__name='Beatles Blog')
Ví dụ này lấy tất cả các Blog đối tượng đó có ít nhất một Entry người có headline chứa 'Lennon':
>>> Blog.objects.filter(entry__headline__contains='Lennon')
Nếu bạn đang lọc qua nhiều mối quan hệ và một trong các mô hình trung gian không có giá trị đáp ứng điều kiện bộ lọc, Django sẽ coi nó như thể có một đối tượng trống  (tất cả các giá trị NULL). Ví dụ, trong bộ lọc này:
Blog.objects.filter(entry__authors__name='Lennon')

Kéo dài các mối quan hệ đa giá trị

ví dụ để chọn tất cả các blog có các mục có cả “Lennon” trong tiêu đề và được xuất bản vào năm 2008 (cùng một mục thỏa mãn cả hai điều kiện), chúng ta sẽ viết:
Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)
Để chọn tất cả các blog có mục nhập với “Lennon” trong tiêu đề cũng như mục nhập đã được xuất bản vào năm 2008, chúng tôi sẽ viết:
Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)

Bộ lọc có thể tham chiếu các trường trên mô hình

Trong các ví dụ được đưa ra cho đến nay, chúng ta đã xây dựng các bộ lọc so sánh giá trị của trường mô hình với một hằng số. Nhưng nếu bạn muốn so sánh giá trị của một trường mô hình với một trường khác trên cùng một mô hình thì sao? Django cung cấp để cho phép so sánh như vậy. Các phiên bản hoạt động như một tham chiếu đến trường mô hình trong một truy vấn. Sau đó, các tham chiếu này có thể được sử dụng trong các bộ lọc truy vấn để so sánh các giá trị của hai trường khác nhau trên cùng một trường hợp mô hình.F expressionsF() Ví dụ: để tìm danh sách tất cả các mục blog có nhiều nhận xét hơn pingback, chúng tôi xây dựng một F()đối tượng để tham chiếu đến số lượng pingback và sử dụng F()đối tượng đó trong truy vấn:
>>> from django.db.models import F
>>> Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks'))
Bạn cũng có thể sử dụng ký hiệu gạch dưới kép để mở rộng các mối quan hệ trong một F()đối tượng. Một F()đối tượng có dấu gạch dưới kép sẽ giới thiệu bất kỳ phép nối nào cần thiết để truy cập đối tượng liên quan. Ví dụ: để truy xuất tất cả các mục nhập có tên tác giả giống với tên blog, chúng tôi có thể đưa ra truy vấn:
>>> Entry.objects.filter(authors__name=F('blog__name'))
Đối với các trường ngày tháng và thời gian, bạn có thể thêm hoặc bớt một timedelta đối tượng. Phần sau sẽ trả về tất cả các mục đã được sửa đổi sau hơn 3 ngày kể từ khi chúng được xuất bản:

Biểu thức có thể tham chiếu các phép biến đổi 

Django hỗ trợ sử dụng các phép biến đổi trong biểu thức.
Ví dụ: để tìm tất cả Entry các đối tượng được xuất bản trong cùng năm với lần sửa đổi gần đây nhất:
>>> Entry.objects.filter(pub_date__year=F('mod_date__year'))
Để tìm năm sớm nhất một mục được xuất bản, chúng ta có thể đưa ra truy vấn:
>>> Entry.objects.aggregate(first_published_year=Min('pub_date__year'))
Ví dụ này tìm giá trị của bài viết được xếp hạng cao nhất và tổng số nhận xét trên tất cả các bài viết cho mỗi năm:
>>> Entry.objects.values('pub_date__year').annotate(
...     top_rating=Subquery(
...         Entry.objects.filter(
...             pub_date__year=OuterRef('pub_date__year'),
...         ).order_by('-rating').values('rating')[:1]
...     ),
...     total_comments=Sum('number_of_comments'),
... )

Bộ nhớ đệm và QuerySets

Mỗi QuerySetchứa một bộ nhớ cache để giảm thiểu truy cập cơ sở dữ liệu. Hiểu cách thức hoạt động của nó sẽ cho phép bạn viết mã hiệu quả nhất.
Trong một tạo mới QuerySet, bộ nhớ cache trống. Lần đầu tiên a QuerySetđược đánh giá – và do đó, một truy vấn cơ sở dữ liệu sẽ xảy ra – Django lưu kết quả truy vấn trong QuerySetbộ đệm của ‘và trả về kết quả đã được yêu cầu rõ ràng (ví dụ: phần tử tiếp theo, nếu phần tử QuerySetđang được lặp lại). Các đánh giá tiếp theo về việc QuerySetsử dụng lại các kết quả đã lưu trong bộ nhớ cache.
Hãy ghi nhớ hành vi lưu vào bộ nhớ đệm này, vì nó có thể cắn bạn nếu bạn không sử dụng QuerySetđúng cách. Ví dụ: phần sau sẽ tạo hai QuerySets, đánh giá chúng và loại bỏ chúng:
>>> print([e.headline for e in Entry.objects.all()])
>>> print([e.pub_date for e in Entry.objects.all()])
iều đó có nghĩa là cùng một truy vấn cơ sở dữ liệu sẽ được thực thi hai lần, tăng gấp đôi hiệu quả tải cơ sở dữ liệu của bạn.
 Ngoài ra, có khả năng hai danh sách có thể không bao gồm các bản ghi cơ sở dữ liệu giống nhau, bởi vì một danh sách có Entry thể đã được thêm hoặc xóa trong tích tắc giữa hai yêu cầu.
Để tránh sự cố này, hãy lưu QuerySetvà sử dụng lại:
>>> queryset = Entry.objects.all()
>>> print([p.headline for p in queryset]) # Evaluate the query set.
>>> print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.

Khi QuerySets không được lưu trong bộ nhớ cache

Bộ truy vấn không phải lúc nào cũng lưu vào bộ nhớ cache kết quả của chúng. Khi chỉ đánh giá một phần của bộ truy vấn, bộ đệm sẽ được kiểm tra, nhưng nếu nó không được điền thì các mục được trả về bởi truy vấn tiếp theo sẽ không được lưu vào bộ đệm. Cụ thể, điều này có nghĩa là giới hạn bộ truy vấn bằng cách sử dụng một lát mảng hoặc một chỉ mục sẽ không điền vào bộ nhớ cache. Ví dụ: liên tục nhận được một chỉ mục nhất định trong một đối tượng tập hợp truy vấn sẽ truy vấn cơ sở dữ liệu mỗi lần:
>>> queryset = Entry.objects.all()
>>> print(queryset[5]) # Queries the database
>>> print(queryset[5]) # Queries the database again
Tuy nhiên, nếu toàn bộ bộ truy vấn đã được đánh giá, bộ đệm ẩn sẽ được kiểm tra thay thế:
>>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # Queries the database
>>> print(queryset[5]) # Uses cache
>>> print(queryset[5]) # Uses cache

Truy vấn JSONField

Việc triển khai tra cứu là khác nhau JSONField, chủ yếu là do sự tồn tại của các phép biến đổi chính. Để chứng minh, chúng tôi sẽ sử dụng mô hình ví dụ sau:
from django.db import models

class Dog(models.Model):
    name = models.CharField(max_length=200)
    data = models.JSONField(null=True)

    def __str__(self):
        return self.name

Lưu trữ và truy vấn cho None

Như với các trường khác, lưu trữ None dưới dạng giá trị của trường sẽ lưu trữ nó dưới dạng SQL NULL. Mặc dù không được khuyến khích, nhưng có thể lưu trữ vô hướng JSON null thay vì SQL NULLbằng cách sử dụng Value('null'). Bất kể giá trị nào được lưu trữ, khi được truy xuất từ ​​cơ sở dữ liệu, biểu diễn Python của vô hướng JSON null cũng giống như SQL NULL, tức là None. Vì vậy, có thể khó để phân biệt giữa chúng. Điều này chỉ áp dụng cho None giá trị cấp cao nhất của trường. Nếu None nằm trong dấu list hoặc dict, nó sẽ luôn được hiểu là JSON null. Khi truy vấn, None giá trị sẽ luôn được hiểu là JSON null. Để truy vấn SQL NULL, hãy sử dụng isnull:
>>> Dog.objects.create(name='Max', data=None)  # SQL NULL.
<Dog: Max>
>>> Dog.objects.create(name='Archie', data=Value('null'))  # JSON null.
<Dog: Archie>
>>> Dog.objects.filter(data=None)
<QuerySet [<Dog: Archie>]>
>>> Dog.objects.filter(data=Value('null'))
<QuerySet [<Dog: Archie>]>
>>> Dog.objects.filter(data__isnull=True)
<QuerySet [<Dog: Max>]>
>>> Dog.objects.filter(data__isnull=False)
<QuerySet [<Dog: Archie>]>

Lưu trữ và truy vấn cho None

Như với các trường khác, lưu trữ None dưới dạng giá trị của trường sẽ lưu trữ nó dưới dạng SQL NULL. Mặc dù không được khuyến khích, nhưng có thể lưu trữ vô hướng JSON null thay vì SQL NULLbằng cách sử dụng Value('null'). Bất kể giá trị nào được lưu trữ, khi được truy xuất từ ​​cơ sở dữ liệu, biểu diễn Python của vô hướng JSON nullcũng giống như SQL NULL, tức là NoneVì vậy, có thể khó để phân biệt giữa chúng. Điều này chỉ áp dụng cho None giá trị cấp cao nhất của trường. Nếu None nằm trong dấu list hoặc dict, nó sẽ luôn được hiểu là JSON null. Khi truy vấn, Nonegiá trị sẽ luôn được hiểu là JSON null. Để truy vấn SQL NULL, hãy sử dụng isnull:
>>> Dog.objects.create(name='Max', data=None)  # SQL NULL.
<Dog: Max>
>>> Dog.objects.create(name='Archie', data=Value('null'))  # JSON null.
<Dog: Archie>
>>> Dog.objects.filter(data=None)
<QuerySet [<Dog: Archie>]>
>>> Dog.objects.filter(data=Value('null'))
<QuerySet [<Dog: Archie>]>
>>> Dog.objects.filter(data__isnull=True)
<QuerySet [<Dog: Max>]>
>>> Dog.objects.filter(data__isnull=False)
<QuerySet [<Dog: Archie>]>
rừ khi bạn chắc chắn muốn làm việc với các NULL giá trị SQL , hãy xem xét việc đặt null=False và cung cấp một giá trị mặc định phù hợp cho các giá trị trống, chẳng hạn như default=dict.

Biến đổi khóa, chỉ mục và đường dẫn

Để truy vấn dựa trên một khóa từ điển nhất định, hãy sử dụng khóa đó làm tên tra cứu:
>>> Dog.objects.create(name='Rufus', data={
...     'breed': 'labrador',
...     'owner': {
...         'name': 'Bob',
...         'other_pets': [{
...             'name': 'Fishy',
...         }],
...     },
... })
<Dog: Rufus>
>>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': None})
<Dog: Meg>
>>> Dog.objects.filter(data__breed='collie')
<QuerySet [<Dog: Meg>]>
Nhiều khóa có thể được xâu chuỗi với nhau để tạo thành một tra cứu đường dẫn:
>>> Dog.objects.filter(data__owner__name='Bob')
<QuerySet [<Dog: Rufus>]>
Nếu khóa là một số nguyên, nó sẽ được hiểu là một phép biến đổi chỉ mục trong một mảng:
>>> Dog.objects.filter(data__owner__other_pets__0__name='Fishy')
<QuerySet [<Dog: Rufus>]>
Nếu khóa bạn muốn truy vấn bằng cách trùng với tên của tra cứu khác, hãy sử dụng contains tìm kiếm thay thế. Để truy vấn các khóa bị thiếu, hãy sử dụng isnull tra cứu
>>> Dog.objects.create(name='Shep', data={'breed': 'collie'})
<Dog: Shep>
>>> Dog.objects.filter(data__owner__isnull=True)
<QuerySet [<Dog: Shep>]>

Lưu trữ và tra cứu khóa

Các contains tra cứu được ghi đè vào JSONField. Các đối tượng được trả về là những đối tượng mà các dict cặp khóa-giá trị đã cho đều được chứa trong cấp cao nhất của trường. Ví dụ:
>>> Dog.objects.create(name='Rufus', data={'breed': 'labrador', 'owner': 'Bob'})
<Dog: Rufus>
>>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})
<Dog: Meg>
>>> Dog.objects.create(name='Fred', data={})
<Dog: Fred>
>>> Dog.objects.filter(data__contains={'owner': 'Bob'})
<QuerySet [<Dog: Rufus>, <Dog: Meg>]>
>>> Dog.objects.filter(data__contains={'breed': 'collie'})
<QuerySet [<Dog: Meg>]>

contained_by

 
Đây là nghịch đảo của contains tra cứu – các đối tượng được trả về sẽ là những đối tượng mà các cặp khóa-giá trị trên đối tượng là một tập hợp con của các cặp trong giá trị được truyền vào. Ví dụ:
>>> Dog.objects.create(name='Rufus', data={'breed': 'labrador', 'owner': 'Bob'})
<Dog: Rufus>
>>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})
<Dog: Meg>
>>> Dog.objects.create(name='Fred', data={})
<Dog: Fred>
>>> Dog.objects.filter(data__contained_by={'breed': 'collie', 'owner': 'Bob'})
<QuerySet [<Dog: Meg>, <Dog: Fred>]>
>>> Dog.objects.filter(data__contained_by={'breed': 'collie'})
<QuerySet [<Dog: Fred>]>

has_key

>>> Dog.objects.create(name='Rufus', data={'breed': 'labrador'})
<Dog: Rufus>
>>> Dog.objects.create(name='Meg', data={'breed': 'collie', 'owner': 'Bob'})
<Dog: Meg>
>>> Dog.objects.filter(data__has_key='owner')
<QuerySet [<Dog: Meg>]>

has_keys

Trả về các đối tượng có bất kỳ khóa nào trong số các khóa đã cho ở cấp cao nhất của dữ liệu. Ví dụ:
>>> Dog.objects.create(name='Rufus', data={'breed': 'labrador'})
<Dog: Rufus>
>>> Dog.objects.create(name='Meg', data={'owner': 'Bob'})
<Dog: Meg>
>>> Dog.objects.filter(data__has_any_keys=['owner', 'breed'])
<QuerySet [<Dog: Rufus>, <Dog: Meg>]>

Tra cứu phức tạp với Q

Các truy vấn đối số từ khóa – trong filter(), v.v. – được “AND” kết hợp với nhau. Nếu bạn cần thực hiện các truy vấn phức tạp hơn (ví dụ: truy vấn với các ORcâu lệnh), bạn có thể sử dụng .Q objects
Ví dụ: Q đối tượng này đóng gói một LIKEtruy vấn:
from django.db.models import Q
Q(question__startswith='What')
Q object có thể được kết hợp bằng cách sử dụng toán tử &và |Khi một toán tử được sử dụng trên hai Q object, nó sinh ra một Q object mới
Ví dụ: câu lệnh này tạo ra một Q object đại diện cho “OR” của hai "question__startswith" truy vấn:
Q(question__startswith='Who') | Q(question__startswith='What')
Mỗi chức năng tra cứu mà có từ khóa đối số (ví dụ filter()exclude()get()) cũng có thể được thông qua một hoặc nhiều Q objects như vị trí đối số (không tên). Nếu bạn cung cấp nhiều Q objects cho một hàm tra cứu, các đối số sẽ là “AND” cùng nhau. Ví dụ:
Poll.objects.get(
    Q(question__startswith='Who'),
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)

Xóa đối tượng

>>> e.delete()
(1, {'weblog.Entry': 1})
>>> Entry.objects.filter(pub_date__year=2005).delete()
(5, {'webapp.Entry': 5})
b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()

Cập nhật nhiều đối tượng cùng một lúc

Đôi khi bạn muốn đặt một trường thành một giá trị cụ thể cho tất cả các đối tượng trong a QuerySet. Bạn có thể làm điều này với update()phương pháp. Ví dụ:
# Update all the headlines with pub_date in 2007.
Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')
Bạn chỉ có thể đặt các trường và ForeignKey trường không quan hệ bằng cách sử dụng phương pháp này. Để cập nhật trường không quan hệ, hãy cung cấp giá trị mới dưới dạng hằng số. Để cập nhật ForeignKey các trường, hãy đặt giá trị mới là phiên bản mô hình mới mà bạn muốn trỏ tới. Ví dụ:
>>> b = Blog.objects.get(pk=1)

# Change every Entry so that it belongs to this Blog.
>>> Entry.objects.all().update(blog=b)
Các lệnh gọi cập nhật cũng có thể sử dụng để cập nhật một trường dựa trên giá trị của một trường khác trong mô hình. Điều này đặc biệt hữu ích cho các bộ đếm tăng dần dựa trên giá trị hiện tại của chúng. Ví dụ: để tăng số lượng pingback cho mọi mục nhập trong blog:F expressions
>>> Entry.objects.all().update(number_of_pingbacks=F('number_of_pingbacks') + 1)
Tuy nhiên, không giống như F() các đối tượng trong mệnh đề bộ lọc và loại trừ, bạn không thể giới thiệu các phép nối khi bạn sử dụng F() các đối tượng trong bản cập nhật – bạn chỉ có thể tham chiếu các trường cục bộ cho mô hình đang được cập nhật. Nếu bạn cố gắng giới thiệu một phép nối với một F() đối tượng, một FieldError sẽ được nâng lên:
>>> Entry.objects.update(headline=F('blog__name'))

Sử dụng trình quản lý đảo ngược tùy chỉnh 

Theo mặc định, RelatedManager được sử dụng cho quan hệ đảo ngược là một lớp con của trình quản lý mặc định cho mô hình đó. Nếu bạn muốn chỉ định một người quản lý khác cho một truy vấn nhất định, bạn có thể sử dụng cú pháp sau:
from django.db import models

class Entry(models.Model):
    #...
    objects = models.Manager()  # Default Manager
    entries = EntryManager()    # Custom Manager

b = Blog.objects.get(id=1)
b.entry_set(manager='entries').all()
Nếu được EntryManagerthực hiện lọc mặc định trong get_queryset() phương pháp của nó , bộ lọc đó sẽ áp dụng cho all()cuộc gọi.
Việc chỉ định trình quản lý đảo ngược tùy chỉnh cũng cho phép bạn gọi các phương thức tùy chỉnh của nó:
b.entry_set(manager='entries').is_published()

Các phương pháp bổ sung để xử lý các đối tượng liên quan 

add(obj1, obj2, …): Thêm các đối tượng mô hình được chỉ định vào tập đối tượng liên quan.
create(**kwargs): Tạo một đối tượng mới, lưu nó và đặt nó vào tập đối tượng liên quan. Trả về đối tượng mới được tạo.
remove(obj1, obj2, …): Loại bỏ các đối tượng mô hình được chỉ định khỏi tập đối tượng liên quan.
clear(): Loại bỏ tất cả các đối tượng khỏi tập đối tượng liên quan.
set(objs): Thay thế tập hợp các đối tượng liên quan.