Django
クエリーセット
サーチ…
前書き
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()
flag
がTrue
設定されているモデルオブジェクト:
MyModel.objects.filter(flag=True)
model_num
が25より大きいモデルオブジェクト:
MyModel.objects.filter(model_num__gt=25)
「Cheap Item」 name
のflag
とFalse
設定された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
オブジェクトを使用して、検索クエリでAND
、 OR
条件を作成できます。たとえば、 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
オブジェクトが最初に来る必要があります。次のクエリは、( flag
がTrue
または15
より大きいモデル番号に設定されている)モデルと、 "H"で始まる名前を検索します。
from django.db.models import Q
MyModel.objects.filter(Q(flag=True) | Q(model_num__gt=15), name__startswith="H")
注: Q
オブジェクトは、 filter
、 exclude
、 get
などのキーワード引数を取るルックアップ関数で使用できます。 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からの同意を得て上記の回答から得たものです。