サーチ…


前書き

Querysetは、基本的に、 Modelから派生したオブジェクトのリストであり、データベースクエリのコンパイルによって作成されます。

スタンドアロンモデルでの簡単なクエリ

ここでは、いくつかのテストクエリを実行するために使用する単純なモデルを示します。

class MyModel(models.Model):
    name = models.CharField(max_length=10)
    model_num = models.IntegerField()
    flag = models.NullBooleanField(default=False)

id / pkが4の単一のモデルオブジェクトを取得する:
(IDが4のアイテムがなく、複数のアイテムがある場合は、例外がスローされます。)

MyModel.objects.get(pk=4)

すべてのモデルオブジェクト:

MyModel.objects.all()

flagTrue設定されているモデルオブジェクト:

MyModel.objects.filter(flag=True)

model_numが25より大きいモデルオブジェクト:

MyModel.objects.filter(model_num__gt=25)

「Cheap Item」 nameflagFalse設定されたflag持つモデルオブジェクト:

MyModel.objects.filter(name="Cheap Item", flag=False)

特定の文字列のモデル検索name (大文字と小文字を区別):

MyModel.objects.filter(name__contains="ch")

特定の文字列のモデル検索name (大文字小文字を区別しない):

MyModel.objects.filter(name__icontains="ch")

Qオブジェクトによる高度なクエリ

モデルの場合:

class MyModel(models.Model):
    name = models.CharField(max_length=10)
    model_num = models.IntegerField()
    flag = models.NullBooleanField(default=False)

Qオブジェクトを使用して、検索クエリでANDOR条件を作成できます。たとえば、 flag=True または model_num>15すべてのオブジェクトが必要であるとします。

from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15))

上記のことは、あなたがやるANDの場合と同様に、 WHERE flag=True OR model_num > 15に変換されます。

MyModel.objects.filter(Q(flag=True) & Q(model_num__gt=15))

Qオブジェクトは~を使ってNOTクエリを作ることも可能にします。我々が持っているすべてのオブジェクトを取得したいとしましょうflag=False AND model_num!=15私たちはどうなります:

MyModel.objects.filter(Q(flag=True) & ~Q(model_num=15)) 

filter()でQオブジェクトと "normal"パラメータを使用する場合、 Qオブジェクトが最初に来る必要があります。次のクエリは、( flagTrueまたは15より大きいモデル番号に設定されている)モデルと、 "H"で始まる名前を検索します。

from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15), name__startswith="H")

注: Qオブジェクトは、 filterexcludegetなどのキーワード引数を取るルックアップ関数で使用できます。 getを使って使うときは、オブジェクトを1つだけ返すか、 MultipleObjectsReturned例外が発生するようにしてください。

ManyToManyFieldのクエリ数を減らす(n + 1の問題)

問題

# models.py:
class Library(models.Model):
    name = models.CharField(max_length=100)
    books = models.ManyToManyField(Book)

class Book(models.Model):
    title = models.CharField(max_length=100)
# views.py
def myview(request):
    # Query the database.
    libraries = Library.objects.all()

    # Query the database on each iteration (len(author) times)
    # if there is 100 librairies, there will have 100 queries plus the initial query
    for library in libraries:
        books = library.books.all()
        books[0].title
        # ...

    # total : 101 queries

溶液

後でManyToManyFieldフィールドであるフィールドにアクセスする必要があることがわかっている場合は、 ManyToManyField prefetch_relatedを使用してください。

# views.py
def myview(request):
    # Query the database.
    libraries = Library.objects.prefetch_related('books').all()
    
    # Does not query the database again, since `books` is pre-populated
    for library in libraries:
        books = library.books.all()
        books[0].title
        # ...

    # total : 2 queries - 1 for libraries, 1 for books

prefetch_relatedは、参照フィールドでも使用できます。

# models.py:
class User(models.Model):
    name = models.CharField(max_length=100)

class Library(models.Model):
    name = models.CharField(max_length=100)
    books = models.ManyToManyField(Book)

class Book(models.Model):
    title = models.CharField(max_length=100)
    readers = models.ManyToManyField(User)
 # views.py
def myview(request):
    # Query the database.
    libraries = Library.objects.prefetch_related('books', 'books__readers').all()
    
    # Does not query the database again, since `books` and `readers` is pre-populated
    for library in libraries:
        for book in library.books.all():
            for user in book.readers.all():
                user.name
                # ...

    # total : 3 queries - 1 for libraries, 1 for books, 1 for readers

しかし、クエリーセットが実行されると、フェッチされたデータはデータベースを再起動することなく変更することができません。次の例では、追加のクエリを実行します。

 # views.py
def myview(request):
    # Query the database.
    libraries = Library.objects.prefetch_related('books').all()
    for library in libraries:
        for book in library.books.filter(title__contains="Django"):
            print(book.name)

Django 1.7で導入されたPrefetchオブジェクトを使用して、以下を最適化することができます:

from django.db.models import Prefetch
# views.py
def myview(request):
    # Query the database.
    libraries = Library.objects.prefetch_related(
        Prefetch('books', queryset=Book.objects.filter(title__contains="Django")
    ).all()
    for library in libraries:
        for book in library.books.all():
            print(book.name)  # Will print only books containing Django for each library

ForeignKeyフィールドのクエリ数を減らす(n + 1問題)

問題

Djangoクエリーセットは、怠惰な方法で評価されます。例えば:

# models.py:
class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    author = models.ForeignKey(Author, related_name='books')
    title = models.CharField(max_length=100)
# views.py
def myview(request):
    # Query the database
    books = Book.objects.all()

    for book in books:
        # Query the database on each iteration to get author (len(books) times)
        # if there is 100 books, there will have 100 queries plus the initial query
        book.author
        # ...

    # total : 101 queries

上記のコードは、djangoが各書籍の作者のためにデータベースに問い合わせるようにします。これは非効率的であり、単一のクエリしか持たないほうがよい。

溶液

後でForeignKeyフィールドにアクセスする必要があることがわかっている場合は、 ForeignKey select_relatedを使用してください。

# views.py
def myview(request):
    # Query the database.
    books = Books.objects.select_related('author').all()
    
    for book in books:
        # Does not query the database again, since `author` is pre-populated
        book.author
        # ...

    # total : 1 query

select_relatedは参照フィールドでも使用できます。

# models.py:
class AuthorProfile(models.Model):
    city = models.CharField(max_length=100)

class Author(models.Model):
    name = models.CharField(max_length=100)
    profile = models.OneToOneField(AuthorProfile)

class Book(models.Model):
    author = models.ForeignKey(Author, related_name='books')
    title = models.CharField(max_length=100)    
# views.py
def myview(request):
    books = Book.objects.select_related('author')\
                        .select_related('author__profile').all()
   
    for book in books:
        # Does not query database
        book.author.name
        # or
        book.author.profile.city
        # ...

    # total : 1 query

DjangoクエリーセットのSQLを取得する

queryセットのquery属性は、あなたのクエリーのSQLと同等の構文を提供します。

>>> queryset = MyModel.objects.all()
>>> print(queryset.query)
SELECT "myapp_mymodel"."id", ... FROM "myapp_mymodel"

警告:

この出力は、デバッグ目的でのみ使用する必要があります。生成されたクエリはバックエンド固有ではありません。したがって、パラメータは適切に引用されず、SQLインジェクションに対して脆弱になります。クエリは、データベースバックエンドで実行可能でない場合もあります。

QuerySetから最初と最後のレコードを取得する

最初のオブジェクトを取得するには:

MyModel.objects.first()

最後のオブジェクトを取得するには:

MyModel.objects.last()

フィルタを使用する最初のオブジェクト:

MyModel.objects.filter(name='simple').first()

フィルタを使用する最後のオブジェクト:

MyModel.objects.filter(name='simple').last()

Fオブジェクトによる高度なクエリ

F()オブジェクトは、モデルフィールドまたは注釈付き列の値を表します。モデルフィールドの値を参照し、実際にデータベースからPythonメモリにプルする必要なしに、モデルフィールド値を参照してデータベース操作を実行することができます。 - F()式

クエリで別のフィールドの値を参照する必要があるときはいつでもF()オブジェクトを使用するのが適切です。それ自身では、 F()オブジェクトは何も意味するものではなく、クエリーセットの外で呼び出すことはできません。それらは、同じクエリーセットでフィールドの値を参照するために使用されます。

例えば、与えられたモデル...

SomeModel(models.Model):
    ...
    some_field = models.IntegerField()

...ユーザは、 F()を使って次のようにフィルタリングしながらidフィールドの値参照することによって、 some_field値がid 2倍のオブジェクトを照会できます。

SomeModel.objects.filter(some_field=F('id') * 2)

F('id')は同じインスタンスのid値を参照するだけです。 Djangoはそれを使って対応するSQL文を作成します。この場合、これによく似た何か:

SELECT * FROM some_app_some_model 
WHERE some_field = ((id * 2))

F()式がなければ、これは未加工SQLまたはPythonでのフィルタリング(これは特にオブジェクトがたくさんある場合のパフォーマンスを低下させるF()で達成されます。


参考文献:

F()クラス定義から:

既存のクエリオブジェクトへの参照を解決できるオブジェクト。 - Fソース

注:この例はTinyInstanceからの同意を得て上記の回答から得たものです。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow