クエリを行う
データモデルを作成すると、Djangoは、オブジェクトの作成、取得、更新、削除を可能にするデータベース抽象化APIを自動的に提供します。 このドキュメントでは、このAPIの使用方法について説明します。 さまざまなモデルルックアップオプションすべての詳細については、データモデルリファレンスを参照してください。
このガイド全体(およびリファレンス)では、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
オブジェクトの作成
Djangoは、Pythonオブジェクトでデータベーステーブルデータを表すために、直感的なシステムを使用します。モデルクラスはデータベーステーブルを表し、そのクラスのインスタンスはデータベーステーブルの特定のレコードを表します。
オブジェクトを作成するには、モデルクラスへのキーワード引数を使用してオブジェクトをインスタンス化し、 save()を呼び出してデータベースに保存します。
モデルがファイルmysite/blog/models.py
に存在すると仮定すると、次の例があります。
>>> from blog.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
>>> b.save()
これにより、バックグラウンドでINSERT
SQLステートメントが実行されます。 save()を明示的に呼び出すまで、Djangoはデータベースにアクセスしません。
save()メソッドには戻り値がありません。
オブジェクトへの変更を保存する
すでにデータベースにあるオブジェクトへの変更を保存するには、 save()を使用します。
すでにデータベースに保存されているBlog
インスタンスb5
がある場合、この例では名前を変更し、データベース内のレコードを更新します。
>>> b5.name = 'New name'
>>> b5.save()
これにより、バックグラウンドでUPDATE
SQLステートメントが実行されます。 save()を明示的に呼び出すまで、Djangoはデータベースにアクセスしません。
ForeignKeyおよびManyToManyFieldフィールドの保存
ForeignKey フィールドの更新は、通常のフィールドの保存とまったく同じように機能します。問題のフィールドに適切なタイプのオブジェクトを割り当てます。 この例では、Entry
およびBlog
の適切なインスタンスがすでにデータベースに保存されていると仮定して、Entry
インスタンスentry
のblog
属性を更新します。 (以下でそれらを取得できるように):
>>> 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()
ManyToManyField の更新は、動作が少し異なります。フィールドで add()メソッドを使用して、リレーションにレコードを追加します。 この例では、Author
インスタンスjoe
をentry
オブジェクトに追加します。
>>> from blog.models import Author
>>> joe = Author.objects.create(name="Joe")
>>> entry.authors.add(joe)
ManyToManyField に一度に複数のレコードを追加するには、次のように add()の呼び出しに複数の引数を含めます。
>>> 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は文句を言います。
オブジェクトの取得
データベースからオブジェクトを取得するには、モデルクラスの Manager を介して QuerySet を作成します。
QuerySet は、データベースからのオブジェクトのコレクションを表します。 ゼロ、1つ、または複数のフィルターを持つことができます。 フィルタは、指定されたパラメータに基づいてクエリ結果を絞り込みます。 SQL用語では、 QuerySet はSELECT
ステートメントと同等であり、フィルターはWHERE
やLIMIT
などの制限句です。
モデルの Manager を使用して、 QuerySet を取得します。 各モデルには少なくとも1つの Manager があり、デフォルトではオブジェクトと呼ばれます。 次のように、モデルクラスを介して直接アクセスします。
>>> Blog.objects
<django.db.models.manager.Manager object at ...>
>>> b = Blog(name='Foo', tagline='Bar')
>>> b.objects
Traceback:
...
AttributeError: "Manager isn't accessible via Blog instances."
ノート
Managers
は、モデルインスタンスからではなく、モデルクラスを介してのみアクセス可能であり、「テーブルレベル」の操作と「レコードレベル」の操作を強制的に分離します。
Manager は、モデルのQuerySets
の主なソースです。 たとえば、Blog.objects.all()
は、データベース内のすべてのBlog
オブジェクトを含む QuerySet を返します。
すべてのオブジェクトを取得する
テーブルからオブジェクトを取得する最も簡単な方法は、すべてのオブジェクトを取得することです。 これを行うには、 Manager で all()メソッドを使用します。
>>> all_entries = Entry.objects.all()
all()メソッドは、データベース内のすべてのオブジェクトの QuerySet を返します。
フィルタを使用して特定のオブジェクトを取得する
all()によって返される QuerySet は、データベーステーブル内のすべてのオブジェクトを記述します。 ただし、通常は、オブジェクトの完全なセットのサブセットのみを選択する必要があります。
このようなサブセットを作成するには、最初の QuerySet を改良して、フィルター条件を追加します。 QuerySet を改良する最も一般的な2つの方法は次のとおりです。
filter(**kwargs)
- 指定されたルックアップパラメータに一致するオブジェクトを含む新しい QuerySet を返します。
exclude(**kwargs)
- 指定されたルックアップパラメータに一致しないオブジェクトを含む新しい QuerySet を返します。
ルックアップパラメータ(上記の関数定義の**kwargs
)は、以下のフィールドルックアップで説明されている形式である必要があります。
たとえば、2006年のブログエントリの QuerySet を取得するには、次のように filter()を使用します。
Entry.objects.filter(pub_date__year=2006)
デフォルトのマネージャークラスでは、次と同じです。
Entry.objects.all().filter(pub_date__year=2006)
連鎖フィルター
QuerySet を改良した結果は、それ自体が QuerySet であるため、改良を連鎖させることができます。 例えば:
>>> Entry.objects.filter(
... headline__startswith='What'
... ).exclude(
... pub_date__gte=datetime.date.today()
... ).filter(
... pub_date__gte=datetime.date(2005, 1, 30)
... )
これにより、データベース内のすべてのエントリの最初の QuerySet が取得され、フィルターが追加され、次に除外が追加され、次に別のフィルターが追加されます。 最終結果は、2005年1月30日から当日までに公開された「What」で始まる見出しのすべてのエントリを含む QuerySet です。
フィルタリングされたQuerySetは一意です
QuerySet を改良するたびに、以前の QuerySet にバインドされていないまったく新しい QuerySet が得られます。 それぞれの改良により、保存、使用、再利用できる個別の QuerySet が作成されます。
例:
>>> q1 = Entry.objects.filter(headline__startswith="What")
>>> q2 = q1.exclude(pub_date__gte=datetime.date.today())
>>> q3 = q1.filter(pub_date__gte=datetime.date.today())
これらの3つのQuerySets
は別々です。 1つ目は、「What」で始まる見出しを含むすべてのエントリを含むベース QuerySet です。 2番目は最初のサブセットであり、pub_date
が現在または将来のレコードを除外する追加の基準があります。 3番目は最初のサブセットであり、pub_date
が現在または将来のレコードのみを選択する追加の基準があります。 最初の QuerySet (q1
)は、改良プロセスの影響を受けません。
QuerySetは怠惰です
QuerySets
は怠惰です– QuerySet を作成する行為には、データベースアクティビティは含まれません。 フィルタは一日中積み重ねることができ、 QuerySet が評価されるまでDjangoは実際にクエリを実行しません。 この例を見てください:
>>> q = Entry.objects.filter(headline__startswith="What")
>>> q = q.filter(pub_date__lte=datetime.date.today())
>>> q = q.exclude(body_text__icontains="food")
>>> print(q)
これは3回のデータベースヒットのように見えますが、実際には、最後の行(print(q)
)で1回だけデータベースにヒットします。 一般に、 QuerySet の結果は、ユーザーが「要求」するまでデータベースからフェッチされません。 その場合、データベースにアクセスすることにより、 QuerySet が評価されます。 評価が行われる正確な時期の詳細については、 QuerySetsが評価される時期を参照してください。
get()を使用して単一のオブジェクトを取得する
filter()は、クエリに一致するオブジェクトが1つだけの場合でも、常に QuerySet を提供します。この場合、単一のオブジェクトを含む QuerySet になります。エレメント。
クエリに一致するオブジェクトが1つしかないことがわかっている場合は、 Manager で get()メソッドを使用して、オブジェクトを直接返すことができます。
>>> one_entry = Entry.objects.get(pk=1)
filter()の場合と同様に、 get()で任意のクエリ式を使用できます。ここでも、以下のフィールドルックアップを参照してください。
get()を使用する場合と、[0]
のスライスで filter()を使用する場合には違いがあることに注意してください。 クエリに一致する結果がない場合、 get()はDoesNotExist
例外を発生させます。 この例外は、クエリが実行されているモデルクラスの属性です。したがって、上記のコードでは、主キーが1のEntry
オブジェクトがない場合、DjangoはEntry.DoesNotExist
を発生させます。 。
同様に、複数のアイテムが get()クエリに一致する場合、Djangoは文句を言います。 この場合、 MultipleObjectsReturned が発生します。これも、モデルクラス自体の属性です。
その他のQuerySetメソッド
ほとんどの場合、必要なときに all()、 get()、 filter()、 exclude()を使用しますデータベースからオブジェクトを検索します。 しかし、それだけではありません。 さまざまな QuerySet メソッドすべての完全なリストについては、 QuerySetAPIリファレンスを参照してください。
QuerySetを制限する
Pythonの配列スライシング構文のサブセットを使用して、 QuerySet を特定の数の結果に制限します。 これは、SQLのLIMIT
およびOFFSET
句と同等です。
たとえば、これは最初の5つのオブジェクト(LIMIT 5
)を返します。
>>> Entry.objects.all()[:5]
これにより、6番目から10番目のオブジェクト(OFFSET 5 LIMIT 5
)が返されます。
>>> Entry.objects.all()[5:10]
負のインデックス付け(つまり、 Entry.objects.all()[-1]
)はサポートされていません。
通常、 QuerySet をスライスすると、新しい QuerySet が返されます。クエリは評価されません。 例外は、Pythonスライス構文の「step」パラメーターを使用する場合です。 たとえば、これは実際にクエリを実行して、最初の10個の秒オブジェクトごとのリストを返します。
>>> Entry.objects.all()[:10:2]
スライスされたクエリセットのそれ以上のフィルタリングまたは順序付けは、それがどのように機能するかというあいまいな性質のために禁止されています。
リストではなく single オブジェクトを取得するには(例: SELECT foo FROM bar LIMIT 1
)、スライスの代わりにインデックスを使用します。 たとえば、これは、エントリを見出しのアルファベット順に並べた後、データベースの最初のEntry
を返します。
>>> Entry.objects.order_by('headline')[0]
これはおおよそ次のものと同等です。
>>> Entry.objects.order_by('headline')[0:1].get()
ただし、指定された基準に一致するオブジェクトがない場合、これらの最初のものはIndexError
を発生させ、2番目のものはDoesNotExist
を発生させることに注意してください。 詳細については、 get()を参照してください。
フィールドルックアップ
フィールドルックアップは、SQL WHERE
句の要点を指定する方法です。 これらは、 QuerySet メソッド filter()、 exclude()、および get()へのキーワード引数として指定されます。
基本的なルックアップキーワード引数は、field__lookuptype=value
の形式を取ります。 (これは二重アンダースコアです)。 例えば:
>>> Entry.objects.filter(pub_date__lte='2006-01-01')
(大まかに)次のSQLに変換されます。
SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';
これがどのように可能か
Pythonには、実行時に名前と値が評価される任意の名前と値の引数を受け入れる関数を定義する機能があります。 詳細については、公式Pythonチュートリアルの tut-keywordargs を参照してください。
ルックアップで指定されるフィールドは、モデルフィールドの名前である必要があります。 ただし、例外が1つあります。ただし、 ForeignKey の場合は、_id
の接尾辞が付いたフィールド名を指定できます。 この場合、valueパラメータには、外部モデルの主キーの生の値が含まれていることが期待されます。 例えば:
>>> Entry.objects.filter(blog_id=4)
無効なキーワード引数を渡すと、ルックアップ関数はTypeError
を発生させます。
データベースAPIは、約20種類のルックアップタイプをサポートしています。 完全なリファレンスは、フィールドルックアップリファレンスにあります。 利用可能なものを味わうために、おそらく使用する一般的なルックアップのいくつかを次に示します。
- :lookup: `exact`
「完全一致」。 例えば:
>>> Entry.objects.get(headline__exact="Cat bites dog")
これらの行に沿ってSQLを生成します:
SELECT ... WHERE headline = 'Cat bites dog';
ルックアップタイプを指定しない場合、つまり、キーワード引数に二重アンダースコアが含まれていない場合、ルックアップタイプは
exact
であると見なされます。たとえば、次の2つのステートメントは同等です。
>>> Blog.objects.get(id__exact=14) # Explicit form >>> Blog.objects.get(id=14) # __exact is implied
exact
ルックアップが一般的なケースであるため、これは便宜上です。- :lookup: `iexact`
大文字と小文字を区別しない一致。 したがって、クエリ:
>>> Blog.objects.get(name__iexact="beatles blog")
"Beatles Blog"
、"beatles blog"
、または"BeAtlES blOG"
というタイトルのBlog
と一致します。- :lookup: `contains`
大文字と小文字を区別する封じ込めテスト。 例えば:
Entry.objects.get(headline__contains='Lennon')
大まかに次のSQLに変換されます。
SELECT ... WHERE headline LIKE '%Lennon%';
これは見出し
'Today Lennon honored'
と一致しますが、'today lennon honored'
とは一致しないことに注意してください。大文字と小文字を区別しないバージョン:lookup: `icontains` もあります。
- :lookup: `startswith` 、:lookup:` endswith`
それぞれ検索で開始し、検索で終了します。 :lookup: `istartswith` および:lookup:` iendswith` と呼ばれる大文字と小文字を区別しないバージョンもあります。
繰り返しますが、これは表面を傷つけるだけです。 完全なリファレンスは、フィールドルックアップリファレンスにあります。
関係にまたがるルックアップ
Djangoは、ルックアップで関係を「追跡」するための強力で直感的な方法を提供し、バックグラウンドでSQL JOIN
を自動的に処理します。 関係をスパンするには、目的のフィールドに到達するまで、モデル間で関連するフィールドのフィールド名を二重下線で区切って使用します。
この例では、name
が'Beatles Blog'
であるBlog
を持つすべてのEntry
オブジェクトを取得します。
>>> Entry.objects.filter(blog__name='Beatles Blog')
このスパンは、必要なだけ深くすることができます。
逆方向にも機能します。 はカスタマイズ可能ですが、デフォルトでは、モデルの小文字の名前を使用してルックアップで「逆」の関係を参照します。
この例では、headline
に'Lennon'
が含まれるEntry
が少なくとも1つあるすべてのBlog
オブジェクトを取得します。
>>> Blog.objects.filter(entry__headline__contains='Lennon')
複数の関係をフィルタリングしていて、中間モデルの1つにフィルター条件を満たす値がない場合、Djangoはそれを空(すべての値はNULL
)であるかのように扱いますが、有効です。そこにオブジェクト。 これはすべて、エラーが発生しないことを意味します。 たとえば、このフィルタでは次のようになります。
Blog.objects.filter(entry__authors__name='Lennon')
(関連するAuthor
モデルがあった場合)、エントリに関連付けられたauthor
がなかった場合、name
もアタッチされていないかのように扱われ、 author
がないためにエラーが発生します。 通常、これはまさにあなたが望んでいることです。 混乱する可能性がある唯一のケースは、:lookup: `isnull` を使用している場合です。 したがって:
Blog.objects.filter(entry__authors__name__isnull=True)
author
に空のname
があるBlog
オブジェクトと、entry
に空のauthor
があるオブジェクトを返します。 後者のオブジェクトが必要ない場合は、次のように記述できます。
Blog.objects.filter(entry__authors__isnull=False, entry__authors__name__isnull=True)
複数値の関係にまたがる
ManyToManyField または逆 ForeignKey に基づいてオブジェクトをフィルタリングする場合、関心のある2種類のフィルタがあります。 Blog
/ Entry
の関係を考えてみましょう(Blog
とEntry
は1対多の関係です)。 見出しに「Lennon」の両方があり、2008年に公開されたエントリがあるブログを見つけることに興味があるかもしれません。 または、見出しに「Lennon」が含まれるエントリと、2008年に公開されたエントリがあるブログを検索することもできます。 1つのBlog
に関連付けられたエントリが複数あるため、これらのクエリは両方とも可能であり、状況によっては意味があります。
ManyToManyField でも同じタイプの状況が発生します。 たとえば、Entry
にtags
という ManyToManyField がある場合、「music」およびというタグにリンクされたエントリを検索できます。 ]“ bands” または、名前が“ music” で、ステータスが“ public” のタグを含むエントリが必要な場合があります。
これらの両方の状況を処理するために、Djangoには filter()呼び出しを処理する一貫した方法があります。 1回の filter()呼び出し内のすべてが同時に適用され、これらすべての要件に一致するアイテムが除外されます。 連続する filter()呼び出しは、オブジェクトのセットをさらに制限しますが、複数値の関係の場合、プライマリモデルにリンクされているすべてのオブジェクトに適用されます。必ずしも、以前のフィルターによって選択されたオブジェクトではありません。 ()呼び出し。
それは少し紛らわしいように聞こえるかもしれませんので、うまくいけば例が明らかになるでしょう。 見出しに「Lennon」の両方が含まれ、2008年に公開された(両方の条件を満たす同じエントリ)エントリを含むすべてのブログを選択するには、次のように記述します。
Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)
見出しに「Lennon」が含まれるエントリと、2008年に公開されたエントリを含むすべてのブログを選択するには、次のように記述します。
Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)
「Lennon」を含むエントリと2008年のエントリの両方を含むブログが1つだけあるが、2008年のエントリのいずれにも「Lennon」が含まれていないとします。 最初のクエリはブログを返しませんが、2番目のクエリはその1つのブログを返します。
2番目の例では、最初のフィルターは、見出しに「Lennon」が含まれるエントリにリンクされているすべてのブログにクエリセットを制限します。 2番目のフィルターは、ブログのセットさらにを、2008年に公開されたエントリにもリンクされているブログに制限します。 2番目のフィルターによって選択されたエントリは、最初のフィルターのエントリと同じである場合と同じでない場合があります。 Entry
アイテムではなく、各フィルターステートメントでBlog
アイテムをフィルター処理しています。
ノート
上記のように、複数値の関係にまたがるクエリの filter()の動作は、 exclude()と同等に実装されていません。 代わりに、単一の exclude()呼び出しの条件は、必ずしも同じアイテムを参照するわけではありません。
たとえば、次のクエリでは、2008年に公開されたエントリとエントリの見出しに「Lennon」が含まれる両方のエントリを含むブログが除外されます。
Blog.objects.exclude(
entry__headline__contains='Lennon',
entry__pub_date__year=2008,
)
ただし、 filter()を使用する場合の動作とは異なり、これは両方の条件を満たすエントリに基づいてブログを制限しません。 それをするために、すなわち 2008年に公開された「Lennon」で公開されたエントリを含まないすべてのブログを選択するには、次の2つのクエリを実行する必要があります。
Blog.objects.exclude(
entry__in=Entry.objects.filter(
headline__contains='Lennon',
pub_date__year=2008,
),
)
フィルタはモデルのフィールドを参照できます
これまでの例では、モデルフィールドの値を定数と比較するフィルターを作成しました。 しかし、モデルフィールドの値を同じモデルの別のフィールドと比較したい場合はどうでしょうか。
Djangoはそのような比較を可能にするために F式を提供します。 F()
のインスタンスは、クエリ内のモデルフィールドへの参照として機能します。 これらの参照をクエリフィルターで使用して、同じモデルインスタンス上の2つの異なるフィールドの値を比較できます。
たとえば、ピングバックよりもコメントが多いすべてのブログエントリのリストを見つけるには、F()
オブジェクトを作成してピングバック数を参照し、そのF()
オブジェクトをクエリで使用します。
>>> from django.db.models import F
>>> Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks'))
Djangoは、定数と他のF()
オブジェクトの両方で、F()
オブジェクトでの加算、減算、乗算、除算、モジュロ、および累乗演算の使用をサポートしています。 ピングバックと同じ数のコメントが 2倍を超えるすべてのブログエントリを検索するには、クエリを変更します。
>>> Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks') * 2)
エントリの評価がピンバックカウントとコメントカウントの合計よりも小さいすべてのエントリを見つけるには、次のクエリを発行します。
>>> Entry.objects.filter(rating__lt=F('number_of_comments') + F('number_of_pingbacks'))
二重下線表記を使用して、F()
オブジェクトの関係をスパンすることもできます。 二重下線付きのF()
オブジェクトは、関連するオブジェクトにアクセスするために必要な結合を導入します。 たとえば、作成者の名前がブログの名前と同じであるすべてのエントリを取得するには、次のクエリを発行します。
>>> Entry.objects.filter(authors__name=F('blog__name'))
日付および日付/時刻フィールドの場合、timedelta
オブジェクトを加算または減算できます。 以下は、公開後3日を超えて変更されたすべてのエントリを返します。
>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
F()
オブジェクトは、.bitand()
、.bitor()
、.bitxor()
、.bitrightshift()
、および.bitleftshift()
によるビット演算をサポートします。 例えば:
>>> F('somefield').bitand(16)
オラクル
Oracleはビット単位のXOR演算をサポートしていません。
バージョン3.1で変更: .bitxor()
のサポートが追加されました。
式は変換を参照できます
バージョン3.2の新機能。
Djangoは、式での変換の使用をサポートしています。
たとえば、最後に変更されたのと同じ年に公開されたすべてのEntry
オブジェクトを検索するには、次のようにします。
>>> Entry.objects.filter(pub_date__year=F('mod_date__year'))
エントリが公開された最も早い年を見つけるために、クエリを発行できます。
>>> Entry.objects.aggregate(first_published_year=Min('pub_date__year'))
この例では、最高評価のエントリの値と、各年のすべてのエントリに対するコメントの総数を検索します。
>>> 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'),
... )
pkルックアップショートカット
便宜上、Djangoには「主キー」を表すpk
ルックアップショートカットが用意されています。
Blog
モデルの例では、主キーはid
フィールドであるため、次の3つのステートメントは同等です。
>>> Blog.objects.get(id__exact=14) # Explicit form
>>> Blog.objects.get(id=14) # __exact is implied
>>> Blog.objects.get(pk=14) # pk implies id__exact
pk
の使用は、__exact
クエリに限定されません。任意のクエリ用語をpk
と組み合わせて、モデルの主キーに対してクエリを実行できます。
# Get blogs entries with id 1, 4 and 7
>>> Blog.objects.filter(pk__in=[1,4,7])
# Get all blog entries with id > 14
>>> Blog.objects.filter(pk__gt=14)
pk
ルックアップは、結合間でも機能します。 たとえば、次の3つのステートメントは同等です。
>>> Entry.objects.filter(blog__id__exact=3) # Explicit form
>>> Entry.objects.filter(blog__id=3) # __exact is implied
>>> Entry.objects.filter(blog__pk=3) # __pk implies __id__exact
LIKEステートメントでのパーセント記号とアンダースコアのエスケープ
LIKE
SQLステートメント(iexact
、contains
、icontains
、startswith
、istartswith
、 endswith
およびiendswith
)は、LIKE
ステートメントで使用される2つの特殊文字(パーセント記号とアンダースコア)を自動的にエスケープします。 (LIKE
ステートメントでは、パーセント記号は複数文字のワイルドカードを示し、下線は1文字のワイルドカードを示します。)
これは、物事が直感的に機能する必要があることを意味するため、抽象化が漏れることはありません。 たとえば、パーセント記号を含むすべてのエントリを取得するには、パーセント記号を他の文字と同じように使用します。
>>> Entry.objects.filter(headline__contains='%')
Djangoが見積もりを処理します。 結果のSQLは次のようになります。
SELECT ... WHERE headline LIKE '%\%%';
アンダースコアについても同じことが言えます。 パーセント記号とアンダースコアの両方が透過的に処理されます。
キャッシングとQuerySet
各 QuerySet には、データベースアクセスを最小限に抑えるためのキャッシュが含まれています。 それがどのように機能するかを理解することで、最も効率的なコードを書くことができます。
新しく作成された QuerySet では、キャッシュは空です。 QuerySet が初めて評価されると、つまりデータベースクエリが発生すると、Djangoはクエリ結果を QuerySet のキャッシュに保存し、明示的に要求された結果を返します(たとえば、 QuerySet が繰り返されている場合は、次の要素)。 QuerySet の後続の評価では、キャッシュされた結果が再利用されます。
QuerySet を正しく使用しないと、噛み付く可能性があるため、このキャッシュ動作に注意してください。 たとえば、次の例では、2つの QuerySet を作成し、それらを評価して、破棄します。
>>> print([e.headline for e in Entry.objects.all()])
>>> print([e.pub_date for e in Entry.objects.all()])
つまり、同じデータベースクエリが2回実行され、データベースの負荷が実質的に2倍になります。 また、Entry
が2つのリクエストの間に一瞬で追加または削除された可能性があるため、2つのリストに同じデータベースレコードが含まれていない可能性があります。
この問題を回避するには、 QuerySet を保存して再利用します。
>>> 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.
QuerySetがキャッシュされていない場合
クエリセットは常に結果をキャッシュするとは限りません。 クエリセットの part のみを評価する場合、キャッシュはチェックされますが、キャッシュが設定されていない場合、後続のクエリによって返されるアイテムはキャッシュされません。 具体的には、これは、配列スライスまたはインデックスを使用してクエリセットを制限しても、キャッシュにデータが入力されないことを意味します。
たとえば、クエリセットオブジェクトで特定のインデックスを繰り返し取得すると、毎回データベースにクエリが実行されます。
>>> queryset = Entry.objects.all()
>>> print(queryset[5]) # Queries the database
>>> print(queryset[5]) # Queries the database again
ただし、クエリセット全体がすでに評価されている場合は、代わりにキャッシュがチェックされます。
>>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # Queries the database
>>> print(queryset[5]) # Uses cache
>>> print(queryset[5]) # Uses cache
クエリセット全体が評価され、キャッシュにデータが入力される他のアクションの例を次に示します。
>>> [entry for entry in queryset]
>>> bool(queryset)
>>> entry in queryset
>>> list(queryset)
ノート
クエリセットを印刷するだけでは、キャッシュにデータが入力されません。 これは、__repr__()
を呼び出すと、クエリセット全体のスライスのみが返されるためです。
クエリJSONField
JSONField では、主にキー変換が存在するため、ルックアップの実装が異なります。 実例を示すために、次のモデル例を使用します。
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
Noneの保存とクエリ
他のフィールドと同様に、フィールドの値としてNone
を格納すると、SQL NULL
として格納されます。 推奨されていませんが、 Value(' null ')を使用して、SQL NULL
の代わりにJSONスカラーnull
を保存することができます。
どちらの値が格納されていても、データベースから取得すると、JSONスカラーnull
のPython表現はSQL NULL
と同じになります。 None
。 したがって、それらを区別するのは難しい場合があります。
これは、フィールドの最上位値としてのNone
にのみ適用されます。 None
がlist
またはdict
内にある場合、常にJSON null
として解釈されます。
クエリを実行すると、None
の値は常にJSON null
として解釈されます。 SQL NULL
を照会するには、:lookup: `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>]>
SQL NULL
値を使用することが確実でない限り、null=False
を設定し、default=dict
などの空の値に適切なデフォルトを指定することを検討してください。
キー、インデックス、およびパスの変換
特定の辞書キーに基づいてクエリを実行するには、そのキーをルックアップ名として使用します。
>>> 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>]>
複数のキーをチェーンして、パスルックアップを形成できます。
>>> Dog.objects.filter(data__owner__name='Bob')
<QuerySet [<Dog: Rufus>]>
キーが整数の場合、配列内のインデックス変換として解釈されます。
>>> Dog.objects.filter(data__owner__other_pets__0__name='Fishy')
<QuerySet [<Dog: Rufus>]>
クエリするキーが別のルックアップの名前と衝突する場合は、 :lookup: `含む ` 代わりにルックアップ。
欠落しているキーを照会するには、isnull
ルックアップを使用します。
>>> Dog.objects.create(name='Shep', data={'breed': 'collie'})
<Dog: Shep>
>>> Dog.objects.filter(data__owner__isnull=True)
<QuerySet [<Dog: Shep>]>
ノート
上記のルックアップの例では、:lookup: `exact` ルックアップを暗黙的に使用しています。 キー、インデックス、パスの変換は、:lookup: `icontains` 、:lookup:` endswith` 、:lookup: `iendswith` でチェーンすることもできます。 ]、:lookup: `iexact` 、:lookup:` regex` 、:lookup: `iregex` 、:lookup:` startswith` 、:lookup: `istartswith` 、:lookup:` lt` 、:lookup: `lte` 、:lookup:` gt` 、:lookup: `gte` 、および Containmentとキールックアップ。
ノート
キーパスクエリの動作方法により、 exclude()および filter()が網羅的なセットを生成することは保証されていません。 パスを持たないオブジェクトを含める場合は、isnull
ルックアップを追加します。
警告
任意の文字列がJSONオブジェクトのキーになる可能性があるため、以下にリストされているもの以外のルックアップはキールックアップとして解釈されます。 エラーは発生しません。 入力ミスには特に注意し、クエリが意図したとおりに機能することを常に確認してください。
MariaDBおよびOracleユーザー
キー、インデックス、またはパス変換で order_by()を使用すると、値の文字列表現を使用してオブジェクトが並べ替えられます。 これは、MariaDBとOracle Databaseが、JSON値を同等のSQL値に変換する関数を提供していないためです。
Oracleユーザー
Oracle Databaseでは、 exclude()クエリのルックアップ値としてNone
を使用すると、オブジェクトを含め、指定されたパスの値としてnull
を持たないオブジェクトが返されます。パスがありません。 他のデータベースバックエンドでは、クエリはパスを持ち、値がnull
ではないオブジェクトを返します。
PostgreSQLユーザー
PostgreSQLでは、キーまたはインデックスが1つだけ使用されている場合、SQL演算子->
が使用されます。 複数の演算子を使用する場合は、#>
演算子が使用されます。
封じ込めとキールックアップ
contains
:lookup: `contains` ルックアップはJSONField
でオーバーライドされます。 返されるオブジェクトは、指定されたdict
のキーと値のペアがすべてフィールドの最上位に含まれているオブジェクトです。 例えば:
>>> 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>]>
OracleとSQLite
contains
はOracleとSQLiteではサポートされていません。
contained_by
これは、 :lookup: `含む ` ルックアップ-返されるオブジェクトは、オブジェクトのキーと値のペアが渡された値のサブセットであるオブジェクトになります。 例えば:
>>> 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>]>
OracleとSQLite
contained_by
はOracleとSQLiteではサポートされていません。
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
指定されたすべてのキーがデータの最上位にあるオブジェクトを返します。 例えば:
>>> 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_keys=['breed', 'owner'])
<QuerySet [<Dog: Meg>]>
has_any_keys
指定されたキーのいずれかがデータの最上位にあるオブジェクトを返します。 例えば:
>>> 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>]>
Qオブジェクトを使用した複雑なルックアップ
キーワード引数クエリ– filter()などで。 –一緒に「AND」されます。 より複雑なクエリ(たとえば、OR
ステートメントを使用したクエリ)を実行する必要がある場合は、 Qオブジェクトを使用できます。
Qオブジェクト(django.db.models.Q
)は、キーワード引数のコレクションをカプセル化するために使用されるオブジェクトです。 これらのキーワード引数は、上記の「フィールドルックアップ」のように指定されます。
たとえば、このQ
オブジェクトは、単一のLIKE
クエリをカプセル化します。
from django.db.models import Q
Q(question__startswith='What')
Q
オブジェクトは、&
および|
演算子を使用して組み合わせることができます。 2つのQ
オブジェクトで演算子を使用すると、新しいQ
オブジェクトが生成されます。
たとえば、このステートメントは、2つの"question__startswith"
クエリの「OR」を表す単一のQ
オブジェクトを生成します。
Q(question__startswith='Who') | Q(question__startswith='What')
これは、次のSQL WHERE
句と同等です。
WHERE question LIKE 'Who%' OR question LIKE 'What%'
Q
オブジェクトを&
および|
演算子と組み合わせて、括弧で囲んだグループ化を使用することにより、任意の複雑さのステートメントを作成できます。 また、Q
オブジェクトは、~
演算子を使用して否定でき、通常のクエリと否定された(NOT
)クエリの両方を組み合わせたルックアップを組み合わせることができます。
Q(question__startswith='Who') | ~Q(pub_date__year=2005)
キーワード引数を取る各ルックアップ関数(例: filter()、 exclude()、 get())は、1つ以上のQ
オブジェクトを定位置(名前なし)として渡すこともできます。 )引数。 ルックアップ関数に複数のQ
オブジェクト引数を指定すると、引数は一緒に「AND」されます。 例えば:
Poll.objects.get(
Q(question__startswith='Who'),
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)
…大まかにSQLに変換されます。
SELECT * from polls WHERE question LIKE 'Who%'
AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
ルックアップ関数は、Q
オブジェクトとキーワード引数の使用を混在させることができます。 ルックアップ関数に提供されるすべての引数(キーワード引数またはQ
オブジェクト)は、一緒に「AND」されます。 ただし、Q
オブジェクトを指定する場合は、キーワード引数の定義の前に置く必要があります。 例えば:
Poll.objects.get(
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
question__startswith='Who',
)
…は、前の例と同等の有効なクエリになります。 しかし:
# INVALID QUERY
Poll.objects.get(
question__startswith='Who',
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)
…は有効ではありません。
オブジェクトの比較
2つのモデルインスタンスを比較するには、標準のPython比較演算子である二重等号==
を使用します。 舞台裏では、2つのモデルの主キー値を比較します。
上記のEntry
の例を使用すると、次の2つのステートメントは同等です。
>>> some_entry == other_entry
>>> some_entry.id == other_entry.id
モデルの主キーがid
と呼ばれていなくても、問題ありません。 比較では、呼び出されたものに関係なく、常に主キーが使用されます。 たとえば、モデルの主キーフィールドがname
と呼ばれる場合、これら2つのステートメントは同等です。
>>> some_obj == other_obj
>>> some_obj.name == other_obj.name
オブジェクトの削除
便利な削除メソッドの名前は delete()です。 このメソッドはオブジェクトをすぐに削除し、削除されたオブジェクトの数と、オブジェクトタイプごとの削除数を含むディクショナリを返します。 例:
>>> e.delete()
(1, {'weblog.Entry': 1})
オブジェクトを一括で削除することもできます。 すべての QuerySet には delete()メソッドがあり、その QuerySet のすべてのメンバーを削除します。
たとえば、これにより、pub_date
年が2005年のすべてのEntry
オブジェクトが削除されます。
>>> Entry.objects.filter(pub_date__year=2005).delete()
(5, {'webapp.Entry': 5})
これは可能な限り純粋にSQLで実行されるため、個々のオブジェクトインスタンスのdelete()
メソッドがプロセス中に呼び出されるとは限らないことに注意してください。 モデルクラスにカスタムdelete()
メソッドを提供し、それが確実に呼び出されるようにする場合は、そのモデルのインスタンスを「手動で」削除する必要があります(たとえば、 QuerySetを反復処理することにより)。 QuerySet の一括 delete()メソッドを使用するのではなく、および各オブジェクトでdelete()
を個別に呼び出します)。
Djangoがオブジェクトを削除すると、デフォルトでSQL制約ON DELETE CASCADE
の動作がエミュレートされます。つまり、削除するオブジェクトを指す外部キーを持つオブジェクトも一緒に削除されます。 例えば:
b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()
このカスケード動作は、 ForeignKey への on_delete 引数を介してカスタマイズできます。
delete()は、 Manager 自体に公開されていない唯一の QuerySet メソッドであることに注意してください。 これは、誤ってEntry.objects.delete()
を要求したり、すべてのエントリを削除したりすることを防ぐための安全メカニズムです。 do ですべてのオブジェクトを削除する場合は、完全なクエリセットを明示的に要求する必要があります。
Entry.objects.all().delete()
モデルインスタンスのコピー
モデルインスタンスをコピーするための組み込みの方法はありませんが、すべてのフィールドの値をコピーして新しいインスタンスを簡単に作成することができます。 最も単純なケースでは、pk
をNone
に設定し、 _state.adding をTrue
に設定できます。 ブログの例を使用すると:
blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1
blog.pk = None
blog._state.adding = True
blog.save() # blog.pk == 2
継承を使用すると、事態はさらに複雑になります。 Blog
のサブクラスについて考えてみます。
class ThemeBlog(Blog):
theme = models.CharField(max_length=200)
django_blog = ThemeBlog(name='Django', tagline='Django is easy', theme='python')
django_blog.save() # django_blog.pk == 3
継承の仕組みにより、pk
とid
の両方をNone
に設定し、_state.adding
をTrue
に設定する必要があります。
django_blog.pk = None
django_blog.id = None
django_blog._state.adding = True
django_blog.save() # django_blog.pk == 4
このプロセスは、モデルのデータベーステーブルの一部ではないリレーションをコピーしません。 たとえば、Entry
にはManyToManyField
からAuthor
があります。 エントリを複製した後、新しいエントリに多対多の関係を設定する必要があります。
entry = Entry.objects.all()[0] # some previous entry
old_authors = entry.authors.all()
entry.pk = None
entry._state.adding = True
entry.save()
entry.authors.set(old_authors)
OneToOneField
の場合、1対1の一意性制約に違反しないように、関連するオブジェクトを複製して新しいオブジェクトのフィールドに割り当てる必要があります。 たとえば、entry
がすでに上記のように複製されていると仮定します。
detail = EntryDetail.objects.all()[0]
detail.pk = None
detail._state.adding = True
detail.entry = entry
detail.save()
複数のオブジェクトを一度に更新する
QuerySet 内のすべてのオブジェクトのフィールドを特定の値に設定したい場合があります。 これは、 update()メソッドを使用して実行できます。 例えば:
# Update all the headlines with pub_date in 2007.
Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')
このメソッドを使用して設定できるのは、非関係フィールドと ForeignKey フィールドのみです。 非関係フィールドを更新するには、新しい値を定数として指定します。 ForeignKey フィールドを更新するには、新しい値を、ポイントする新しいモデルインスタンスに設定します。 例えば:
>>> b = Blog.objects.get(pk=1)
# Change every Entry so that it belongs to this Blog.
>>> Entry.objects.all().update(blog=b)
update()
メソッドは即座に適用され、クエリに一致した行の数を返します(一部の行にすでに新しい値がある場合、更新された行の数と等しくない場合があります)。 更新される QuerySet の唯一の制限は、モデルのメインテーブルである1つのデータベーステーブルにしかアクセスできないことです。 関連するフィールドに基づいてフィルタリングできますが、更新できるのはモデルのメインテーブルの列のみです。 例:
>>> b = Blog.objects.get(pk=1)
# Update all the headlines belonging to this Blog.
>>> Entry.objects.filter(blog=b).update(headline='Everything is the same')
update()
メソッドはSQLステートメントに直接変換されることに注意してください。 直接更新の一括操作です。 モデルで save()メソッドを実行したり、pre_save
またはpost_save
シグナルを発行したりしません( save()を呼び出した結果です)。 )、または auto_now フィールドオプションを尊重します。 すべてのアイテムを QuerySet に保存し、 save()メソッドが各インスタンスで呼び出されるようにする場合は、それを処理するための特別な関数は必要ありません。 それらをループして、 save()を呼び出します。
for item in my_queryset:
item.save()
更新の呼び出しでは、 F式を使用して、モデル内の別のフィールドの値に基づいて1つのフィールドを更新することもできます。 これは、現在の値に基づいてカウンターをインクリメントする場合に特に便利です。 たとえば、ブログのすべてのエントリのピングバックカウントをインクリメントするには:
>>> Entry.objects.all().update(number_of_pingbacks=F('number_of_pingbacks') + 1)
ただし、filter句とexclude句のF()
オブジェクトとは異なり、更新でF()
オブジェクトを使用する場合、結合を導入することはできません。参照できるのは、更新されるモデルのローカルフィールドのみです。 F()
オブジェクトとの結合を導入しようとすると、FieldError
が発生します。
# This will raise a FieldError
>>> Entry.objects.update(headline=F('blog__name'))
生のSQLへのフォールバック
Djangoのデータベースマッパーが処理するには複雑すぎるSQLクエリを作成する必要がある場合は、手動でSQLを作成することに頼ることができます。 Djangoには、生のSQLクエリを作成するためのオプションがいくつかあります。 生のSQLクエリの実行を参照してください。
最後に、Djangoデータベースレイヤーはデータベースへの単なるインターフェースであることに注意することが重要です。 他のツール、プログラミング言語、またはデータベースフレームワークを介してデータベースにアクセスできます。 データベースに関してDjango固有のものは何もありません。