クエリを作成する

一度 データモデル を作成すれば、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() を呼び出します。

モデルが 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() はここには記述されていない多数の高度なオプションを持ちます。詳細については save() の項目を参照してください。

オブジェクトの作成と保存を一つの処理で行うには、 create() メソッドを利用してください。

オブジェクトに対する変更を保存する

既にデータベース上に存在する 1 つのオブジェクトに対する変更を保存するには、 save() を利用します。

すでにデータベースに保存されている Blog インスタンス b5 がある場合、この例ではその名前を変更し、データベース内のレコードを更新します:

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

この例では内部で UPDATE SQL 文が処理されます。明示的に save() が呼ばれるまで Django はデータベースを操作しません。

ForeignKeyManyToManyField フィールドを扱う

ForeignKey フィールドを更新する方法は、通常のフィールドを保存するのと全く同じです ― 該当のフィールドに適切なタイプのオブジェクトを割り当てます。以下の例では、 Entry インスタンス entryblog 属性を更新しています。ここでは、 EntryBlog の適切なインスタンスが既にデータベースに保存されていると仮定しています(したがって、以下でそれらを取得できます):

>>> 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 インスタンス joeentry オブジェクトに追加しています:

>>> 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 文においては、 QuerySetSELECT 句、フィルタは WHERELIMIT のような絞り込みに用いる句に対応しています。

モデルの 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 を返します。

すべてのオブジェクトを取得する

テーブルからオブジェクトを取得する最も簡単な方法は、全てのオブジェクトを取得することです。これを行うには、 Managerall() メソッドを使います:

>>> all_entries = Entry.objects.all()

all() メソッドは、データベース内のすべてのオブジェクトを含んだ QuerySet を返します。

フィルタを使って特定のオブジェクトを取得する

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つのオブジェクトだけだと分かっている場合、 Managerget() メソッドを呼べば、そのオブジェクトが直接返されます。

>>> 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 の LIMITOFFSET 句に対応します。

たとえば、次のコードは最初の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 もあります。

startswithendswith
それぞれ starts-with と ends-with 検索を行います。case-insensitive バージョン istartswithiendswith もあります。

繰り返しになりますが、以上はルックアップの表面をさらったに過ぎません。完全なリファレンスは フィールドルックアップのリファレンス を参照してください。

リレーションを横断するルックアップ

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 にだけ適用されます。もし Nonelistdict の中にある場合、常に 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})

オブジェクトを一括で削除することもできます。すべての QuerySetdelete() メソッドを持っており、その 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()

モデルのインスタンスを複製する

モデルインスタンスをコピーする組み込みメソッドはありませんが、全てのフィールドの値をコピーした新しいインスタンスは簡単に作ることができます。最も単純なケースでは、 pkNone に設定し、 _state.addingTrue に設定します。ブログの例で説明すると、次のようになります:

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

継承のしくみ上、pkid の両方を None に設定し、 _state.addingTrue に設定する必要があります:

django_blog.pk = None
django_blog.id = None
django_blog._state.adding = True
django_blog.save()  # django_blog.pk == 4

この処理では、モデルのデータベーステーブルの一部ではないリレーションは複製しません。例えば、EntryAuthor への 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_savepost_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 特有のことは何もありません。

Back to Top