クエリを作成する¶
一度 データモデル を作成すれば、Django はデータオブジェクトの作成、取得、更新および削除を行えるようにデータベースを抽象化した API を自動的に提供します。本ドキュメントではこの API をどのように用いるかを説明します。多様なモデル探索オプション全てに関する詳細については データモデルの項目 を参照ください。
本項( および参照する文章 )では、以下に定義されたブログアプリケーションを構成するモデル定義を利用します:
from datetime import date
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(default=date.today)
authors = models.ManyToManyField(Author)
number_of_comments = models.IntegerField(default=0)
number_of_pingbacks = models.IntegerField(default=0)
rating = models.IntegerField(default=5)
def __str__(self):
return self.headline
オブジェクトを作成する¶
データベースのテーブル上のデータを Python オブジェクトに対応付けるため、 Django は直観的なシステムを利用しています: 1 つのモデルクラスが 1 つのデータベーステーブルに対応し、そのモデルクラスの 1 インスタンスが対応するデータベーステーブルの特定のレコードに対応します。
オブジェクトを生成するためには、作成するモデルのクラスにキーワード引数を渡してインスタンス化し、そのデータをデータベースに保存するために save()
を呼び出します。
Assuming models live in a models.py
file inside a blog
Django app, here
is an example:
>>> from blog.models import Blog
>>> b = Blog(name="Beatles Blog", tagline="All the latest Beatles news.")
>>> b.save()
この例では内部で INSERT
SQL 文が処理されます。明示的に save()
を呼ぶまで Django はデータベースを操作しません。
save()
メソッドは値を返しません。
オブジェクトに対する変更を保存する¶
既にデータベース上に存在する 1 つのオブジェクトに対する変更を保存するには、 save()
を利用します。
すでにデータベースに保存されている Blog
インスタンス b5
がある場合、この例ではその名前を変更し、データベース内のレコードを更新します:
>>> b5.name = "New name"
>>> b5.save()
この例では内部で UPDATE
SQL 文が処理されます。明示的に save()
が呼ばれるまで Django はデータベースを操作しません。
ForeignKey
と ManyToManyField
フィールドを扱う¶
ForeignKey
フィールドを更新する方法は、通常のフィールドを保存するのと全く同じです ― 該当のフィールドに適切なタイプのオブジェクトを割り当てます。以下の例では、 Entry
インスタンス entry
の blog
属性を更新しています。ここでは、 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
はデータベース上のオブジェクトの集合を表しています。多数の フィルタ を持つことができます。フィルタは与えられたパラメータに基づいてクエリの検索結果を絞り込みます。SQL 文においては、 QuerySet
は SELECT
句、フィルタは WHERE
や LIMIT
のような絞り込みに用いる句に対応しています。
モデルの QuerySet
を取得するには、モデルの Manager
を使用します。各モデルには少なくとも1つの Manager
があり、デフォルトでは objects
という名前がついています。モデルクラスから直接アクセスしてください:
>>> 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."
注釈
Manager
はモデルのインスタンスでなく、モデルのクラスを経由してのみアクセスでき、それは "テーブル水準" の処理と "レコード水準" の処理とで責任を明確に分離するためです。
Manager
はモデルの QuerySet
の主な取得元になります。たとえば、 Blog.objects.all()
はデータベース内の Blog
オブジェクト全てを含んだ QuerySet
を返します。
すべてのオブジェクトを取得する¶
テーブルからオブジェクトを取得する最も簡単な方法は、全てのオブジェクトを取得することです。これを行うには、 Manager
の all()
メソッドを使います:
>>> all_entries = Entry.objects.all()
フィルタを使って特定のオブジェクトを取得する¶
all()
が返す QuerySet
には、データベーステーブルのすべてのオブジェクトが含まれています。しかし、ふつう必要になるのはオブジェクト全体の集合ではなく、その部分集合でしょう。
そのような部分集合を作るには、条件フィルタを追加して最初の QuerySet
を絞り込みます。 QuerySet
を絞り込む代表的な方法として次の2つのものがあります。
filter(**kwargs)
与えられたルックアップパラメータにマッチする新しい
QuerySet
を返します。exclude(**kwargs)
与えられたルックアップパラメータにマッチ しない 新しい
QuerySet
を返します。
ルックアップパラメータ (上の関数定義における **kwargs
) は、以下の Field lookups で説明するフォーマットに従わなければなりません。
たとえば、2006年以降のブログエントリーの QuerySet
を取得するには、 filter()
を次のように使用します。
Entry.objects.filter(pub_date__year=2006)
デフォルトの manager クラスの場合、これは次のコードと等価です。
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
をとり、フィルタを追加し、除外フィルタを追加し、さらにもう1つのフィルタを追加しています。最終的な結果は、"What" で始まるヘッドラインを持ち、2005年1月30日から今日までに公開されたすべてのエントリーを含んだ 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番目は基本の QuerySet
で、"What" で始まるヘッドラインを持つ全てのエントリーを含みます。2番めは1番目の部分集合で、 pub_date
が今日または未来の日付であるレコードを除外する追加条件を持ちます。3番目も1番目の部分集合で、 pub_date
が今日または未来の日付であるレコードだけを選択する追加条件を持ちます。1番目の QuerySet
(q1
) は、絞り込みの過程において何ら影響を受けません。
QuerySet
は遅延評価される¶
QuerySets
は遅延評価されます。 QuerySet
を作る行為はいかなるデータベース操作も引き起こしません。たとえあなたが 1 日中フィルタのスタックを積み上げたとしても、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
は 評価 され、データベースへのアクセスが発生します。評価が起こる正確なタイミングの詳細については、 QuerySet が評価されるタイミング を参照してください。
get()
を使って1つのオブジェクトを取得する¶
filter()
は、たとえクエリーにマッチしたのが1つのオブジェクトだけだったとしても、常に QuerySet
を返します。この場合、 QuerySet
には1つの要素だけが含まれることになります。
クエリーにマッチするのは1つのオブジェクトだけだと分かっている場合、 Manager
の get()
メソッドを呼べば、そのオブジェクトが直接返されます。
>>> one_entry = Entry.objects.get(pk=1)
filter()
と同じように、 get()
には任意のクエリー表現が使用できます。 繰り返しますが、詳しくはあとで説明する Field lookups を見てください。
get()
と filter()
を [0]
でスライスすることには、次のような違いがあることに注意してください。クエリにマッチする結果が存在しない場合、 get()
は DoesNotExist
例外を起こします。この例外はクエリーが実行されるモデルクラスの属性です。たとえば上のコードでは、1というプライマリーキーを持つ Entry
オブジェクトがなければ、Django は Entry.DoesNotExist
例外を起こします。
同様に get()
のクエリーが2つ以上のアイテムにマッチした場合にも、Djangoは文句を言います。この場合には、やはり同じクエリのモデルクラスの属性の MultipleObjectsReturned
例外が起こります。
その他の QuerySet
メソッド¶
データベースからオブジェクトを検索する必要がある大抵の場合は、 all()
, get()
, filter()
および exclude()
のいずれかを使うことになるでしょう。しかしこれらのメソッドだけでは不十分な場合は、さまざまな QuerySet
メソッドの全リストが掲載されている QuerySet API リファレンス を参照してください。
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]
スライスされたクエリーセットのフィルタリングやソートは、動作が曖昧なため禁止されています。
リスト (例: SELECT foo FROM bar LIMIT 1
) ではなく 1つの オブジェクトを取得するには、スライスではなくリストのインデックスを使用してください。たとえば、次のコードは、ヘッドラインでアルファベット順にソートしたあと、データベースの1番目の Entry
を返します。
>>> Entry.objects.order_by("headline")[0]
上の例は次のコードとほとんど同じです。
>>> Entry.objects.order_by("headline")[0:1].get()
ただし、与えられた条件を満たすオブジェクトが存在しない場合に、前者は IndexError
を起こすのに対して、後者は DoesNotExist
を起こすことに注意してください。詳細については get()
を参照してください。
フィールドルックアップ¶
フィールドルックアップは、SQL の WHERE
句の内容を指定する手段です。 QuerySet
メソッド、 filter()
、 exclude()
および get()
にキーワード引数として指定します。
基本のルックアップキーワード引数は field__lookuptype=value
という形を取ります (2文字連続するアンダースコアです)。たとえば、
>>> Entry.objects.filter(pub_date__lte="2006-01-01")
というコードは、(だいたい) 次の SQL 文に変換されます。
SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';
動作のしくみ
Python には任意の name-value 形式の引数をとる関数を定義する能力があり、name と value の値を実行時に評価します。詳しい情報については、公式の Python チュートリアルの Keyword Arguments を参照してください。
ルックアップに指定するフィールドはモデルが持つフィールド名でなければなりません。ただし1つだけ例外があり、 ForeignKey
の場合にはフィールド名の末尾に _id
を付けた名前を指定できます。その場合、value パラメータには外部モデルのプライマリーキーの生の値を書くことが期待されます。
>>> Entry.objects.filter(blog_id=4)
無効なキーワード引数を指定すると、ルックアップ関数は TypeError
を起こします。
データベース API は約30個のルックアップタイプをサポートしており、完全なガイドは フィールドルックアップのリファレンス で見ることができます。ルックアップを使って何ができるのかがよく分かるように、以下によく使う一般的なルックアップをいくつか挙げます。
exact
完全な ("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
ルックアップが最もよく使われるため、利便性のためにこのようになっています。iexact
case-insensitive なマッチを行います。したがって、次のクエリ
>>> Blog.objects.get(name__iexact="beatles blog")
は
"Beatles Blog"
、"beatles blog"
、あるいは"BeAtlES blOG"
というタイトルを持つBlog
にもマッチします。contains
case-sensitive な部分一致テストを行います。たとえば、
Entry.objects.get(headline__contains="Lennon")
はだいたい次のような SQL に変換されます。
SELECT ... WHERE headline LIKE '%Lennon%';
この例では、ヘッドライン
'Today Lennon honored'
にはマッチしても'today lennon honored'
にはマッチしないことに注意してください。case-insensitive バージョンの
icontains
もあります。startswith
とendswith
それぞれ starts-with と ends-with 検索を行います。case-insensitive バージョン
istartswith
とiendswith
もあります。
繰り返しになりますが、以上はルックアップの表面をさらったに過ぎません。完全なリファレンスは フィールドルックアップのリファレンス を参照してください。
リレーションを横断するルックアップ¶
Django はルックアップの中でリレーションを「横断する」強力で直感的な方法を提供します。あなたのために、背後で SQL の JOIN
を自動的に実行しています。リレーションを横断するには、使いたいフィールドにたどり着くまで、モデル間を横断する関連フィールドのフィールド名をダブルアンダースコアで繋ぎます。
次の例は、name
に 'Beatles Blog'
を持つ Blog
のすべての Entry
オブジェクトを取得します。
>>> Entry.objects.filter(blog__name="Beatles Blog")
この横断は好きなだけ深くすることができます。
逆方向にも動作します。 カスタマイズすることもできます
が、デフォルトでは、モデルの小文字の名前を使用して、ルックアップで「逆」リレーションシップを参照します。
次の例は、少なくとも1つの headline
が 'Lennon'
を含む Entry
を持つ、すべての Blog
オブジェクトを取得します。
>>> Blog.objects.filter(entry__headline__contains="Lennon")
複数のリレーションにまたがってフィルタリングをしていて、仲介するどれかが条件に合致しない場合、Django は空 (すべての値が NULL
) だけど有効なオブジェクトとして扱います これが意味するのは、エラーが投げられないと言うことです。例えば、以下のフィルタでは:
Blog.objects.filter(entry__authors__name="Lennon")
(関係づけられた Author
モデルがあった場合で) entry に関係づけられた author
がない場合、 name
がなかったかのように扱われ、author
がないという理由でエラーを投げることはありません。通常、これは必要とされる動作です。もし混乱するとしたら、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
(例えば Blog
から Entry
へ) をまたぐ場合、複数の属性でフィルタリングを行うと、それぞれの属性がリレーション先の同じオブジェクトの中で一致する必要があるかどうかが問題になります。見出しに "Lennon" が含まれる2008年のエントリーを持つブログを探すかもしれませんし、見出しに "Lennon" が含まれる2008年のエントリーと、それより新しいかまたは古いエントリーを持つブログを探すかもしれません。
少なくとも1つの2008年のエントリーを含み、かつそのエントリーに「Lennon」というヘッドラインが含まれている(同じエントリーが両方の条件を満たしている)ブログをすべて取得する場合、このように書きます:
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番目のフィルタによって選択されたエントリーが1番目のフィルタのエントリーと違っていても構わないからです。私たちはそれぞれのフィルタ文で、 Entry
アイテムではなく、 Blog
アイテムをフィルタしているのです)。つまり、各条件がリレーション先の同じオブジェクトにマッチする必要がある場合、それぞれを 1 つの filter()
呼び出しに含める必要があります。
注釈
2番目の (より寛容な) クエリは複数のフィルタを連鎖させるので、最初のモデルに複数の結合を実行し、重複を生成することがあります。
>>> from datetime import date
>>> beatles = Blog.objects.create(name="Beatles Blog")
>>> pop = Blog.objects.create(name="Pop Music Blog")
>>> Entry.objects.create(
... blog=beatles,
... headline="New Lennon Biography",
... pub_date=date(2008, 6, 1),
... )
<Entry: New Lennon Biography>
>>> Entry.objects.create(
... blog=beatles,
... headline="New Lennon Biography in Paperback",
... pub_date=date(2009, 6, 1),
... )
<Entry: New Lennon Biography in Paperback>
>>> Entry.objects.create(
... blog=pop,
... headline="Best Albums of 2008",
... pub_date=date(2008, 12, 15),
... )
<Entry: Best Albums of 2008>
>>> Entry.objects.create(
... blog=pop,
... headline="Lennon Would Have Loved Hip Hop",
... pub_date=date(2020, 4, 1),
... )
<Entry: Lennon Would Have Loved Hip Hop>
>>> Blog.objects.filter(
... entry__headline__contains="Lennon",
... entry__pub_date__year=2008,
... )
<QuerySet [<Blog: Beatles Blog>]>
>>> Blog.objects.filter(
... entry__headline__contains="Lennon",
... ).filter(
... entry__pub_date__year=2008,
... )
<QuerySet [<Blog: Beatles Blog>, <Blog: Beatles Blog>, <Blog: Pop Music Blog]>
注釈
上記のような複数の値のリレーションシップをまたぐクエリに対する filter()
の動作は、 exclude()
と同等には実装されていません。その代わり、1つの exclude()
呼び出しにおける条件は必ずしも同じアイテムを参照するとは限りません。
例えば、以下のクエリは、見出しに "Lennon" を含むエントリー と 、2008年に公開されたエントリーの 両方 を含むブログを除外します:
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つのフィールドの値を比較できます。
たとえば、pingback の数よりコメントの数のほうが多いすべてのブログエントリーのリストを検索するには、pingback の数を参照する 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()
オブジェクトのリレーションシップに 2重アンダースコア表記を使うこともできます。 F()
オブジェクトに2重アンダースコアを付けると、リレーション先オブジェクトにアクセスするために必要な結合を行うことができます。例えば、作者名がブログ名と同じすべてのエントリを取得するには、次のようなクエリを発行します:
>>> 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ビット演算をサポートしていません。
式はトランスフォーム(Transform)を参照できる¶
Django は式でトランスフォーム(変換)を使うことをサポートしています。
たとえば、次のコードは最終更新年と同じ年に発行されたすべてのEntryオブジェクトを検索します:
>>> from django.db.models import F
>>> Entry.objects.filter(pub_date__year=F("mod_date__year"))
最も古い出版年を調べるには、次のクエリを実行します:
>>> from django.db.models import Min
>>> Entry.objects.aggregate(first_published_year=Min("pub_date__year"))
たとえばこの例では、各年の最高評価エントリーの値と、全エントリーに対するコメントの総数を求めています:
>>> from django.db.models import OuterRef, Subquery, Sum
>>> 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
ルックアップショートカットを用意しています。pk とは "primary key" を表します。
例の 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 は以下のように、あなたの代わりにクォート処理をしてくれます:
SELECT ... WHERE headline LIKE '%\%%';
アンダースコアも同様です。パーセント記号もアンダースコアも透過的に処理されます。
キャッシュと QuerySet
¶
それぞれの QuerySet
には、データベースへのアクセスを最小にするために内部にキャッシュがあります。キャッシュのしくみを理解すれば、最も効率の良いコードが書けるようになります。
新しく作成された QuerySet
では、キャッシュは空です。初めて QuerySet
が評価されたとき、つまりデータベースへのクエリが発生したとき、Django はクエリの結果を QuerySet
のキャッシュに保存し、明示的に要求された結果 (例えば、 QuerySet
が繰り返し処理されている場合は次の要素) を返します。それ以降の QuerySet
の評価はキャッシュされた結果を再利用します。
このキャッシュの動作は覚えておきましょう。 QuerySet
を正しく使わないと、痛い目を見るかもしれません。たとえば、次のようにしてしまうと QuerySet
が 2 つ作成され、評価され、そして破棄されます:
>>> print([e.headline for e in Entry.objects.all()])
>>> print([e.pub_date for e in Entry.objects.all()])
つまり、同じデータベースクエリが2回実行されることになり、事実上データベースの負荷が2倍になります。また、2つのリクエストの間の一瞬の間に Entry
が追加されたり削除されたりして、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]) # Reuse the cache from the evaluation.
QuerySet
がキャッシュされないケース¶
クエリセットは常に結果をキャッシュするわけではありません。 クエリセットの 一部 だけを評価する場合、キャッシュがチェックされますが、もしキャッシュに値が入力されていなければ、後続のクエリで返される項目はキャッシュされません。具体的には、配列のスライスやインデックスを使用して クエリセットを制限 しても、キャッシュは生成されません。
たとえば、クエリセットオブジェクトで特定のインデックスを繰り返し取得すると、その度にデータベースにクエリが送られます:
>>> 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__()
の呼び出しがクエリセット全体のスライスを返すだけだからです。
非同期クエリ¶
非同期ビューやコードを書いている場合、上記で説明した方法でORMをクエリに使用することはできません。非同期コードから ブロッキングな 同期コードを呼び出すことはできず、イベントループをブロックしてしまいます(それか、大抵はDjangoがそのことを検出し、 SynchronousOnlyOperation
を発生させて防ぐでしょう)。
幸い、Django の非同期クエリ API を使えば、多くのクエリを実行できます。 get()
や delete()
のようなブロッキングされる可能性のあるメソッドにはすべて非同期型 (aget()
や adelete()
) があり、結果を反復処理するときには、代わりに非同期反復処理 (async for
) が使えます。
クエリの繰り返し(イテレーション)¶
クエリを繰り返し処理するデフォルトの方法 for
では、 Django が繰り返し処理時に結果をロードするため、裏でデータベースのクエリがブロッキングされます。これを修正するには、 async for
に変更します:
async for entry in Authors.objects.filter(name__startswith="A"):
...
例えば、 list()
をクエリセットにラップして強制的にクエリセットを評価するようなことはできません (必要であれば、内包表記の中で async for
を使うことができます)。
filter()
や exclude()
のような QuerySet
メソッドは実際にクエリを実行するわけではなく、クエリセットがイテレートされたときに実行されるように指定するものなので、非同期コードで自由に使用できます。どのメソッドがこのように使い続けられるか、またどのメソッドに非同期バージョンがあるかについては、次のセクションを読んでください。
QuerySet
とマネージャメソッド¶
マネージャやクエリセットに関するメソッドの中には、 get()
や first()
のように、クエリセットを強制的に実行し、ブロッキングするものがあります。 filter()
や exclude()
のように、実行を強制しないメソッドもあります。しかし、どうやって見分けるのでしょうか?
メソッドに a
で始まるバージョンがあるかどうかを探ることもできます(例えば、aget()
はありますが、 afilter()
はありません)。しかし、もっとロジカルな方法があります。それは、 QuerySet リファレンス でそのメソッドがどのような種類のものかを調べることです。
そこでは、QuerySet に関するメソッドを2つのセクションに分けています:
新しいクエリセットを返すメソッド: これらはノンブロッキングのもので、非同期バージョンはありません。これらはどんな状況でも自由に使うことができますが、使う前に
defer()
とonly()
に関する注意事項を読んでください。クエリセットを返さないメソッド: これらはブロッキングメソッドで、非同期バージョンもあります。それぞれの非同期名はドキュメントに書かれていますが、私たちの標準的なパターンは接頭辞に
a
をつけることです。
この区別を使えば、非同期バージョンを使う必要があるときとないときを判断できます。たとえば、これは有効な非同期クエリです:
user = await User.objects.filter(username=my_input).afirst()
filter()
はクエリセットを返すため、非同期環境内で連結して使用しても問題ありません。一方で first()
は評価してモデルインスタンスを返すため、 afirst()
に変更し、非同期に適した方法で呼び出すために、式全体の前に await
を使用します。
注釈
もし await
の部分を入れ忘れると、 "coroutine object has no attribute x " や "<coroutine ...>" のようなエラーがモデルインスタンスの代わりに表示されるかもしれません。このようなエラーが表示される場合は、コルーチンを実際の値に変換するための await
部分が不足しています。
トランザクション¶
トランザクションは現在、非同期クエリや更新ではサポートされていません。これを使用しようとすると SynchronousOnlyOperation
が発生します。
トランザクションを使用したい場合は、ORMコードを別の同期関数の中に記述し、 sync_to_async
を使用して呼び出すことをおすすめします。詳しくは 非同期サポート を参照してください。
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(None, JSONField())
を使用することで、 SQL の NULL
の代わりに JSON のスカラー値 null
を格納できます。
どちらの値が格納されていても、データベースから取得したとき、JSON のスカラー null
の Python 表現は SQL の NULL
と同じ、つまり None
になります。そのため、両者を区別するのは難しいでしょう。
これはフィールドのトップレベルの値としての None
にだけ適用されます。もし None
が list
や dict
の中にある場合、常に JSON の null
として解釈されます。
クエリの際、 None
値は常に JSON null
として解釈されます。SQL の NULL
をクエリするには isnull
を使用します:
>>> Dog.objects.create(name="Max", data=None) # SQL NULL.
<Dog: Max>
>>> Dog.objects.create(name="Archie", data=Value(None, JSONField())) # JSON null.
<Dog: Archie>
>>> Dog.objects.filter(data=None)
<QuerySet [<Dog: Archie>]>
>>> Dog.objects.filter(data=Value(None, JSONField()))
<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
のように空の値に対して適切なデフォルト値を指定することを検討してください。
注釈
JSON スカラー値の null
を格納することは null=False
に反しません。
キー、インデックス、パス(経路)のトランスフォーム(変換)¶
指定された辞書のキーに基づいてクエリを実行するには、そのキーをルックアップ名として使います:
>>> 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>]>
複数のキーを連結して1本のルックアップパス(経路)を形成することもできます。
>>> Dog.objects.filter(data__owner__name="Bob")
<QuerySet [<Dog: Rufus>]>
キーが整数の場合は、配列のインデックスのトランスフォームとして解釈されます:
>>> Dog.objects.filter(data__owner__other_pets__0__name="Fishy")
<QuerySet [<Dog: Rufus>]>
クエリしたいキーが他のルックアップ名と衝突する場合は、代わりに contains
ルックアップを使用してください。
欠損したキーをクエリで探したいときは、 isnull
ルックアップを使用します:
>>> Dog.objects.create(name="Shep", data={"breed": "collie"})
<Dog: Shep>
>>> Dog.objects.filter(data__owner__isnull=True)
<QuerySet [<Dog: Shep>]>
注釈
上記のルックアップ例は暗黙的に exact
ルックアップを使用しています。キー、インデックス、およびパス(経路)のトランスフォーム(変換)は、 icontains
, endswith
, iendswith
, iexact
, regex
, iregex
, startswith
, istartswith
, lt
, lte
, gt
, および gte
に加え、 contain の概念とキーのルックアップ とも連結できます。
KT()
式¶
- class KT(lookup)¶
JSONField
のキー、インデックス、パス(経路)のトランスフォーム(変換)のテキスト値を表します。lookup
で2重アンダースコア表記を使用すると、辞書のキーやインデックス変換を連結できます。例えば次のようにします:
>>> from django.db.models.fields.json import KT >>> Dog.objects.create( ... name="Shep", ... data={ ... "owner": {"name": "Bob"}, ... "breed": ["collie", "lhasa apso"], ... }, ... ) <Dog: Shep> >>> Dogs.objects.annotate( ... first_breed=KT("data__breed__1"), owner_name=KT("data__owner__name") ... ).filter(first_breed__startswith="lhasa", owner_name="Bob") <QuerySet [<Dog: Shep>]>
注釈
キーパスクエリの動作上、 exclude()
と filter()
は網羅的なセットを生成することを保証しません。パスを持たないオブジェクトを含めたい場合は、 isnull
ルックアップを追加してください。
警告
JSON オブジェクトでは、どのような文字列もキーになる可能性があるため、以下に示す以外のルックアップはキーのルックアップとして解釈されます。エラーは発生しません。タイプミスに十分注意し、クエリが意図したとおりに動作することを常に確認してください。
MariaDB または Oracle ユーザーの場合
キー、インデックス、パスのトランスフォームで order_by()
を使用すると、値の文字列表現を使ってオブジェクトをソートします。これは、MariaDB と Oracle Database が JSON 値を同等の SQL 値に変換する関数を提供していないからです。
Oracle ユーザーの場合
Oracle Databaseで、 exclude()
クエリ内でルックアップ値として None
を使用すると、指定されたパスで null
でない値を持つオブジェクト、およびパスを持たないオブジェクトが返されます。他のデータベースバックエンドでは、クエリはパスを持ち、かつ値が null
でないオブジェクトを返します。
PostgreSQL の場合
PostgreSQLでは、キーやインデックスが1つしか使用されない場合、SQL演算子 ->
が使用されます。複数の演算子を使用する場合は #>
演算子が使用されます。
SQLite ユーザーの場合
SQLite では、文字列 "true"
, "false"
, "null"
は常にそれぞれ True
, False
, JSON null
として解釈されます。
contain
の概念とキーのルックアップ¶
contains
¶
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
¶
これは contains
ルックアップの逆です。そのオブジェクト上のキーと値のペアが、引数で渡されたJSON値のサブセットになるようなオブジェクトを返します。例えば:
>>> 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
オブジェクトは、1つの LIKE
クエリをカプセル化しています。
from django.db.models import Q
Q(question__startswith="What")
Q
オブジェクトは &
, |
, ^
演算子を使って結合できます。演算子を2つの Q
オブジェクトに使用すると、新しい Q
オブジェクトが生成されます。
たとえば、次の文は2つの "question__startswith"
の "OR" を表す、1つの 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",
)
...上記は有効なクエリで、1つ前の例と同じです。しかし、:
# INVALID QUERY
Poll.objects.get(
question__startswith="Who",
Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
)
...これは無効なクエリです。
参考
Django のユニットテストにある OR ルックアップの例 は Q
の可能な使い方をいくつか示しています。
オブジェクトを比較する¶
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, {'blog.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
で一括の delete()
を使用するのではなく、そのモデルのインスタンスを "手動で" 削除する必要があります (例えば、QuerySet
を繰り返し処理し、各オブジェクトで個別に 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()
を誤ってリクエストして すべての entry を削除してしまうことを防ぐ安全上の仕組みです。すべてのオブジェクトを削除 したい 場合、明示的に完全なクエリセットをリクエストする必要があります:
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
は Author
への ManyToManyField
を持ちます。entry の複製後、新しい entry に対して多対多のリレーションをセットする必要があります:
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.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()
を実行することは一切なく、(save()
を呼び出した結果である) pre_save
や post_save
のシグナルも出しません。 auto_now
フィールドオプションも実施しません。 QuerySet
のすべてのアイテムを保存し、各インスタンスで save()
メソッドが確実に呼ばれるようにしたい場合、特別な機能は不要です。これらをループし、 save()
を呼び出してください:
for item in my_queryset:
item.save()
update の呼び出しでは、 F 式
を使ってモデル内の別のフィールドの値に基づいてフィールドを更新することもできます。これは、現在値に基づいてカウンタを増加させる場合に特に有用です。例えば、ブログの各 entry のpingback カウントを増加させるには:
>>> Entry.objects.update(number_of_pingbacks=F("number_of_pingbacks") + 1)
しかし、filter および exclude 句での F()
オブジェクトとは異なり、更新で F()
オブジェクトを使うときには join を導入することはできません -- できるのは、更新されるモデルに関係付いたフィールドを参照することだけです。 F()
オブジェクトで join の導入を試みた場合、 FieldError
が送出されます:
# This will raise a FieldError
>>> Entry.objects.update(headline=F("blog__name"))
素の SQL にフォールバックする¶
Django のデータベースマッパが扱うには複雑すぎる SQL クエリを記述する必要がある場合、手書きで SQL を書くことができます。Django には、素の SQL クエリを記述するための方法がいくつかあります; 素の SQL 文の実行 を参照してください。
最後に、覚えておいてほしい重要な点は、Django のデータベースレイヤはあなたのデータベースに対する単なるインターフェースでしかないということです。あなたは、他のツール、プログラミング言語、データベースフレームワークなどを通じてデータベースを操作することもできます; データベースに関して Django 特有のことは何もありません。