このセクションの内容
クエリセット
スライスすることができます
制限するために、Pythonのスライス構文を使用した查询集
レコードの数を。これは、SQLと同等であるLIMIT
とOFFSET
句。
>>> Entry.objects.``all``()[:5] # (LIMIT 5)
>>> Entry.objects.all()[5:10] # (OFFSET 5 LIMIT 5)
負のインデックス(例えば、サポートしていませんEntry.objects.all()[-1]
)。一般的に、查询集
スライスは新しいを返します查询集
-それはクエリを実行しません。
反復可能な
articleList=models.Article.objects.all()
for article in articleList:
print(article.title)
不活性問い合わせ
查询集
不活性が実行-作成し查询集
たデータベースへのアクセスをもたらすことはありません。あなたはされるまで、その日にフィルタを保持することができ查询集
、要求値、Djangoは本当にこのクエリを実行する時期。(ローカル慣性でそれを聞いたイテレータではありません)
queryResult=models.Article.objects.``all``() # ``not
hits ``database,通过看到的打印的翻译出来的sql语句记录,你会发现单纯的这句话并没有sql语句打印
print(queryResult) # hits ``database
for
article ``in
queryResult:`` ``print(article.title) # hits ``database
判決はまた、実行された場合と、QueryResultでは場合:パス
一般的には、唯一の「要求」查询集
彼らは結果を取得するときにデータベースに入ります。あなたは、結果が必要なのですとき查询集
介してデータベースにアクセスするための評価。発生の正確な時間が評価さについて、参照クエリセットを計算するとき。
キャッシュ機構
それぞれの查询集
データベースへのアクセスを最小限にするためにキャッシュが含まれています。それがどのように動作するか理解することは、最も効率的なコードを書くことができます。クエリセットは、キャッシュ・スペースと呼ばれます
新しく作成されたでは查询集
、キャッシュが空です。初めて查询集
評価さ-同時データベースは、に、クエリの結果を保存する--Djangoを照会查询集(非简单查询的查询结果,简单查询往下看。)
キャッシュと(あなたが反復処理されている場合、たとえば、明示的に要求された結果を返し查询集
、次の結果が返されます)。次に、查询集
評価は、キャッシュされた結果を再利用します。
以下のため、このキャッシュ動作することに注意してください查询集
言葉の不適切な使用、それはあなたをピットます。たとえば、次の文は、2を作成し查询集
、それらを評価し、それを捨てます:
print([a.title ``for` `a ``in` `models.Article.objects.``all``()])``print([a.create_time ``for` `a ``in` `models.Article.objects.``all``()])
これは、同じデータベースクエリは明らかに、データベースの負荷を倍増、2回実行されることを意味します。同時に、2つの可能性のある結果は、追加または削除されまし条の間に2つの要求を持つことが可能であるため、リストには、同じデータベースレコードが含まれていませんがあります。この問題を回避するには、単に保存查询集
し、それを再使用します。
queryResult=models.Article.objects.``all``()``print([a.title ``for` `a ``in` `queryResult])``print([a.create_time ``for` `a ``in` `queryResult])
クエリセットはキャッシュされません場合は?
クエリセットは、常に彼らの結果をキャッシュしません。チェックキャッシュのクエリセットの一部にのみ評価すると一部がキャッシュにない場合は、クエリによって返された次のレコードはキャッシュされません。だから、それはキャッシュに移入しませんクエリセットを制限するために、スライスまたはインデックスを使用することを意味します。
例えば、クエリは、常にデータベースを照会する特定のインデックス内のオブジェクトのセットを得るために繰り返されます。
>>> 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)
注意:単純にキャッシュを移入しませんクエリセットを印刷します。
queryResult=models.Article.objects.all()
print(queryResult) # hits database
print(queryResult) # hits database
存在()とイテレータ()メソッド
存在しています:
文は全体のクエリセットの完全な実施を判断し、キャッシュにデータを置く場合は、データを必要としないものの、シンプルを使用して!これを避けるためには、データは()メソッドが存在するかどうかを確認することが可能です。
if queryResult.exists():
#SELECT (1) AS "a" FROM "blog_article" LIMIT 1; args=()
print("exists...")
イテレータ:
非常に大規模なクエリセットすると、キャッシュが問題になる可能性があります。
何千ものレコードを処理すると、それらがメモリにロードされたら無駄です。さらに悪いことに、巨大なクエリセットは、システムプロセス、崩壊の危機に瀕してので、あなたのプログラムをロックすることができます。データを通じて同時にクエリセットキャッシュを回避するために、データを取得するイテレータ()メソッドを使用することができ、処理されたデータは破棄されます。
objs = Book.objects.all().iterator() --- objs变成了一个生成器,生成器也是迭代器,但是生成器有个特点,就是取完值就不能再取了
# iterator()可以一次只从数据库获取少量数据,这样可以节省内存
for obj in objs:
print(obj.title)
#BUT,再次遍历没有打印,因为迭代器已经在上一次遍历(next)到最后一次了,没得遍历了
for obj in objs:
print(obj.title)
もちろん、キャッシュの形成を防止するために、イテレータ()メソッドを使用して、クエリが同じクエリセットを横断意味繰り返すことになります。だから、注意する#反復子()時間の経過とともに、大規模なクエリセットを操作しながら、あなたのコードは、クエリを繰り返さないようにしてください。
要約:
キャッシュは、プログラムを削減するために使用されるクエリセットは、通常の使用では、データベースを照会する必要なときだけデータベースを照会することを保証します。使用は、(存在)とイテレータ()メソッドは、プログラムメモリの使用を最適化することができます。彼らはクエリセットのキャッシュを生成しないので、しかし、追加のデータベースクエリを引き起こす可能性があります。
二つの仲介モデル
標準を使用して、このような単純な、多くの関係と類似しており、トッピングのピザを扱うときManyToManyField
、それの上に。しかし、時には、次の2つのモデル間の関係にデータを関連付ける必要があるかもしれません。
例えば、ミュージシャンが所属する音楽グループが記録されたアプリケーションがあります。我々は使用することができますManyToManyField
し、多くのチームとメンバー間の関係の表現を。しかし、時にはあなたは、このようなメンバーがグループに参加するときなど、会員の詳細を、知りたいことがあります。
このような状況のために、Djangoはあなたが多くの関係を定義する仲介モデルを指定することができます。あなたは内部の仲介モデルに他のフィールドを追加することができます。ソース・モデルのManyToManyField
フィールドが使用するthrough
仲介モデルにパラメータ点を。上記例えば音楽グループ、次のように
from` `django.db ``import` `models` `class` `Person(models.Model):`` ``name ``=` `models.CharField(max_length``=``128``)` ` ``def` `__str__(``self``): ``# __unicode__ on Python 2`` ``return` `self``.name` `class` `Group(models.Model):`` ``name ``=` `models.CharField(max_length``=``128``)`` ``members ``=` `models.ManyToManyField(Person, through``=``'Membership'``)` ` ``def` `__str__(``self``): ``# __unicode__ on Python 2`` ``return` `self``.name` `class` `Membership(models.Model):`` ``person ``=` `models.ForeignKey(Person)`` ``group ``=` `models.ForeignKey(Group)`` ``date_joined ``=` `models.DateField()`` ``invite_reason ``=` `models.CharField(max_length``=``64``)
今あなたが設定されていることManyToManyField
(つまり、この例では調停モデルを使用するようにMembership
)、あなたは、多くの関係の作成を開始する必要があります。あなたがやっていることは、仲介モデルのインスタンスを作成します:
>>> ringo ``=` `Person.objects.create(name``=``"Ringo Starr"``)``>>> paul ``=` `Person.objects.create(name``=``"Paul McCartney"``)``>>> beatles ``=` `Group.objects.create(name``=``"The Beatles"``)``>>> m1 ``=` `Membership(person``=``ringo, group``=``beatles,``... date_joined``=``date(``1962``, ``8``, ``16``),``... invite_reason``=``"Needed a new drummer."``)``>>> m1.save()``>>> beatles.members.``all``()``[<Person: Ringo Starr>]``>>> ringo.group_set.``all``()``[<Group: The Beatles>]``>>> m2 ``=` `Membership.objects.create(person``=``paul, group``=``beatles,``... date_joined``=``date(``1960``, ``8``, ``1``),``... invite_reason``=``"Wanted to form a band."``)``>>> beatles.members.``all``()``[<Person: Ringo Starr>, <Person: Paul McCartney>]
通常、多くの異なる分野、あなたが使用することはできませんadd
、create
と代入文(例えば、beatles.members = [...]
関係を作成します):
# THIS WILL NOT WORK``>>> beatles.members.add(john)``# NEITHER WILL THIS``>>> beatles.members.create(name``=``"George Harrison"``)``# AND NEITHER WILL THIS``>>> beatles.members ``=` `[john, paul, ringo, george]
なぜそれをしませんか?あなたが作成できるだけでなくので、これはあるPerson
とGroup
の間の関係、あなたが指定する必要がMembership
モデルに必要なすべての情報を、そしてシンプルadd
、create
および割り当てはこれを行うことはできません。そこで彼らは、調停のモデルを使用して、多くの関係の中で使用することはできません。この時点では、唯一の方法は、仲介モデルのインスタンスを作成することです。
remove()
この方法は、同様の理由で無効になっています。しかし、clear()
この方法が利用可能です。これは、例えば、多くの関係のすべてをクリアすることができます。
>>> ``# Beatles have broken up``>>> beatles.members.clear()``>>> ``# Note that this deletes the intermediate model instances``>>> Membership.objects.``all``()``[]
三つのクエリの最適化
表データ
コードの表示
select_related
使用するのは簡単
1つのフィールド(OneToOneField)と外部キー・フィールド(ForeignKeyの)のために、あなたはselect_relatedクエリセットを最適化するために使用することができます。
リターンをselect_related QuerySet
それが外部キーの関係に沿ってクエリデータオブジェクトに関連付けられたそのクエリを実行すると、。これは、パフォーマンスの複雑なクエリと原因損失が発生しますが、後で外部キー関係を使用すると、データベースクエリを必要としません。
もはや、データベースクエリの後に必要でないとき簡単に言えば、クエリセット()関数のselect_related使用後に、Djangoはそう、対応する外部キーに対応するオブジェクトを取得します。
次の例では、一般的なクエリを示し、select_related()
クエリを区別する。
クエリID =カテゴリ名の記事2は、以下の標準的なクエリです:
# Hits the database.``article``=``models.Article.objects.get(nid``=``2``)` `# Hits the database again to get the related Blog object.``print``(article.category.title)
''``'` `SELECT`` ``"blog_article"."nid",`` ``"blog_article"."title",`` ``"blog_article"."desc",`` ``"blog_article"."read_count",`` ``"blog_article"."comment_count",`` ``"blog_article"."up_count",`` ``"blog_article"."down_count",`` ``"blog_article"."category_id",`` ``"blog_article"."create_time",`` ``"blog_article"."blog_id",`` ``"blog_article"."article_type_id"`` ``FROM "blog_article"`` ``WHERE "blog_article"."nid" = 2; args=(2,)` `SELECT`` ``"blog_category"."nid",`` ``"blog_category"."title",`` ``"blog_category"."blog_id"`` ``FROM "blog_category"`` ``WHERE "blog_category"."nid" = 4; args=(4,)` `'``''
我々はselect_related()関数を使用する場合:
articleList=models.Article.objects.select_related(``"category"``).``all``()` ` ``for` `article_obj ``in` `articleList:`` ``# Doesn't hit the ``database``, because article_obj.category`` ``# has been prepopulated ``in` `the previous query.`` ``print(article_obj.category.title)
SELECT`` ``"blog_article"``.``"nid"``,`` ``"blog_article"``.``"title"``,`` ``"blog_article"``.``"desc"``,`` ``"blog_article"``.``"read_count"``,`` ``"blog_article"``.``"comment_count"``,`` ``"blog_article"``.``"up_count"``,`` ``"blog_article"``.``"down_count"``,`` ``"blog_article"``.``"category_id"``,`` ``"blog_article"``.``"create_time"``,`` ``"blog_article"``.``"blog_id"``,`` ``"blog_article"``.``"article_type_id"``,` ` ``"blog_category"``.``"nid"``,`` ``"blog_category"``.``"title"``,`` ``"blog_category"``.``"blog_id"` `FROM` `"blog_article"``LEFT` `OUTER` `JOIN` `"blog_category"` `ON` `(``"blog_article"``.``"category_id"` `= ``"blog_category"``.``"nid"``);
多くの外部キークエリ
それは別の外部キーである場合、これは、クエリの外部キーカテゴリのですか?さんが一緒に見てみましょう:
article=models.Article.objects.select_related(``"category"``).get(nid=1)``print(article.articledetail)
観測のロギング結果、それが二回チェックする必要があり、まだ発見された、それは変更する必要があります。
article=models.Article.objects.select_related(``"category"``,``"articledetail"``).get(nid=1)``print(article.articledetail)
または:
article=models.Article.objects
.select_related("category")
.select_related("articledetail")
.get(nid=1) # django 1.7 支持链式操作
print(article.articledetail)
SELECT` ` ``"blog_article"``.``"nid"``,`` ``"blog_article"``.``"title"``,`` ``......` ` ``"blog_category"``.``"nid"``,`` ``"blog_category"``.``"title"``,`` ``"blog_category"``.``"blog_id"``,` ` ``"blog_articledetail"``.``"nid"``,`` ``"blog_articledetail"``.``"content"``,`` ``"blog_articledetail"``.``"article_id"` ` ``FROM` `"blog_article"`` ``LEFT` `OUTER` `JOIN` `"blog_category"` `ON` `(``"blog_article"``.``"category_id"` `= ``"blog_category"``.``"nid"``)`` ``LEFT` `OUTER` `JOIN` `"blog_articledetail"` `ON` `(``"blog_article"``.``"nid"` `= ``"blog_articledetail"``.``"article_id"``)`` ``WHERE` `"blog_article"``.``"nid"` `= 1; args=(1,)
ディープクエリ
# 查询id=1的文章的用户姓名` ` ``article=models.Article.objects.select_related(``"blog"``).get(nid=1)`` ``print(article.blog.``user``.username)
それでも二回チェックする必要があります。
SELECT`` ``"blog_article"``.``"nid"``,`` ``"blog_article"``.``"title"``,`` ``......` ` ``"blog_blog"``.``"nid"``,`` ``"blog_blog"``.``"title"``,` ` ``FROM` `"blog_article"` `INNER` `JOIN` `"blog_blog"` `ON` `(``"blog_article"``.``"blog_id"` `= ``"blog_blog"``.``"nid"``)`` ``WHERE` `"blog_article"``.``"nid"` `= 1;` `SELECT`` ``"blog_userinfo"``.``"password"``,`` ``"blog_userinfo"``.``"last_login"``,`` ``......` `FROM` `"blog_userinfo"``WHERE` `"blog_userinfo"``.``"nid"` `= 1;
ユーザ情報テーブルへのクエリは、したがって、として改正しないことが最初のクエリは、以下からです。
article=models.Article.objects.select_related(``"blog__user"``).get(nid=1)``print(article.blog.``user``.username)
SELECT` `"blog_article"``.``"nid"``, ``"blog_article"``.``"title"``,``......` ` ``"blog_blog"``.``"nid"``, ``"blog_blog"``.``"title"``,``......` ` ``"blog_userinfo"``.``"password"``, ``"blog_userinfo"``.``"last_login"``,``......` `FROM` `"blog_article"` `INNER` `JOIN` `"blog_blog"` `ON` `(``"blog_article"``.``"blog_id"` `= ``"blog_blog"``.``"nid"``)` `INNER` `JOIN` `"blog_userinfo"` `ON` `(``"blog_blog"``.``"user_id"` `= ``"blog_userinfo"``.``"nid"``)``WHERE` `"blog_article"``.``"nid"` `= 1;
概要
- select_related主要な針-1および多の関係を最適化します。
- SQLのselect_related使用は、SQLクエリの数を減らすことによって、最適化し、パフォーマンスを向上させることが、文の最適化に参加します。
- あなたは、フィールド名は、可変長パラメータによってselect_related必要指定することができます。また、「__」二重下線を使用して、指定したフィールド名再帰クエリを達成するために接続することができます。
- 指定しないフィールドがキャッシュされないことはありません、指定されていない深さは、アクセスしたい場合は、Djangoは再びSQLクエリを行います、キャッシュされません。
- Djangoが自動的に指定された深さの範囲内のすべてのフィールドをキャッシュし、再帰の深さパラメータの深さによって指定することができます。あなたは深さの指定したフィールドの外にアクセスしたい場合は、Djangoは再びSQLクエリを行います。
- また、パラメータなしの呼び出しを受け入れる、Djangoはすべてのフィールドに深い再帰クエリをしようとします。しかし、廃棄物があることに注意してDjangoの再帰の性能を制限。
- Djangoは> = 1.7、連鎖呼び出しが可変長のパラメータを使用するのと同じselect_related。ジャンゴ<1.7、チェーンのコールは最後のものだけを残して、失敗select_relatedフロントにつながります。
prefetch_related()
多くの分野(のManyToManyField)と多くの分野に多くの場合、あなたは、最適化する)(prefetch_related使用することができます。
prefetch_related()とselect_related()、非常に類似した設計されたSQLクエリの数ではなく、達成するのと同じ方法を減らすことです。後者は、によって、JOINステートメントSQLクエリ内の問題を解決しています。しかし、多くの関係のために、非常に長くなり、テーブルの結果、JOINので、ビット賢明に対処するためのSQL文を使用し、それが増加メモリフットプリントにつながるとSQL文の実行している時間が長くなります。n個のオブジェクト場合、各オブジェクトは、フィールド多くミ物品、[シグマ結果テーブルが生成される(N)ミ行に対応します。
prefetch_related()この問題を回避するには、個別のテーブルを照会し、その後のPythonとの関係に対処することです。
# 查询所有文章关联的所有标签`` ``article_obj=models.Article.objects.``all``()`` ``for` `i ``in` `article_obj:` ` ``print(i.tags.``all``()) #4篇文章: hits ``database` `5
prefetch_related読みます:
# 查询所有文章关联的所有标签`` ``article_obj=models.Article.objects.prefetch_related(``"tags"``).``all``()`` ``for` `i ``in` `article_obj:` ` ``print(i.tags.``all``()) #4篇文章: hits ``database` `2
SELECT` `"blog_article"``.``"nid"``,`` ``"blog_article"``.``"title"``,`` ``......` `FROM` `"blog_article"``;` `SELECT`` ``(``"blog_article2tag"``.``"article_id"``) ``AS` `"_prefetch_related_val_article_id"``,`` ``"blog_tag"``.``"nid"``,`` ``"blog_tag"``.``"title"``,`` ``"blog_tag"``.``"blog_id"`` ``FROM` `"blog_tag"`` ``INNER` `JOIN` `"blog_article2tag"` `ON` `(``"blog_tag"``.``"nid"` `= ``"blog_article2tag"``.``"tag_id"``)`` ``WHERE` `"blog_article2tag"``.``"article_id"` `IN` `(1, 2, 3, 4);``
四余分
なし、のparams = Noneを=しない= Noneを選択しない(余分な、
tables=None, order_by=None, select_params=None)
いくつかのケースでは、複雑なのDjangoのクエリ構文困難単純な式WHERE
の句は、このケースでは、Djangoは提供してextra()
QuerySet
修正するためのメカニズムを-それができるようにQuerySet
生成されたSQL句に新しい句を注入します
あなたは、一つ以上の余分指定することができ参数
、例えばselect
、where
またはtables
これらのパラメータは必要ありません。しかし、少なくとも、あなたは可能な方法に異なるデータベースエンジンでこれらの余分な移植性の問題を注意を払うように!使いたい。(あなたのために明示的に)SQL文を書き、最後のない限り、そうしないようにしてください
選択パラメータ
select
パラメータは、あなたがすることを可能にするSELECT
情報句の追加フィールドを追加し、それは辞書、マップのSQL句に属性名を格納する必要があります。
queryResult=models.Article
.objects.extra(select={'is_recent': "create_time > '2017-09-05'"})
各エントリオブジェクトの結果セットは、追加のプロパティis_recentを有し、それは、オブジェクトが後CREATE_TIME文書2017年9月5日よりも大きいかどうかを示すブール値です。
演習:
# in sqlite:
article_obj=models.Article.objects
.filter(nid=1)
.extra(select={"standard_time":"strftime('%%Y-%%m-%%d',create_time)"})
.values("standard_time","nid","title")
print(article_obj)
# <QuerySet [{'title': 'MongoDb 入门教程', 'standard_time': '2017-09-03', 'nid': 1}]>
パラメータwhere
/tables
あなたはできるwhere
、明示的なSQLの定義WHERE
条項を-おそらく非明示的な接続を行うために。あなたはできるtables
手動でのSQLにテーブルを追加するFROM
句。
where
そして、tables
文字列のリストを受け入れます。すべてwhere
のパラメータは、他の検索条件です「と」。
の点では例えば:
queryResult=models.Article
.objects.extra(where=['nid in (1,3) OR title like "py%" ','nid>2'])
全体的に、インサート
あなたは、SQLクエリの数を減らすために)bulk_createを(使用して可能な限り、オブジェクトを作成するとき。例えば:
Entry.objects.bulk_create([
Entry(headline="Python 3.0 Released"),
Entry(headline="Python 3.1 Planned")
])
...より:
Entry.objects.create(headline="Python 3.0 Released")
Entry.objects.create(headline="Python 3.1 Planned")
この方法は多くの注意を持っていることに注意してください、それはあなたの状況に適していることを確認してください。
また、ManyToManyFieldsでそう、で使用することができます。
my_band.members.add(me, my_friend)
...より:
my_band.members.add(me)
my_band.members.add(my_friend)
...多くの関連性を持っているバンドとアーティスト。
ファイブXXX
六のXXX
七のXXX
八のXXX
スライスすることができます
制限するために、Pythonのスライス構文を使用した查询集
レコードの数を。これは、SQLと同等であるLIMIT
とOFFSET
句。
>>> Entry.objects.``all``()[:5] # (LIMIT 5)
>>> Entry.objects.all()[5:10] # (OFFSET 5 LIMIT 5)
負のインデックス(例えば、サポートしていませんEntry.objects.all()[-1]
)。一般的に、查询集
スライスは新しいを返します查询集
-それはクエリを実行しません。
反復可能な
articleList=models.Article.objects.all()
for article in articleList:
print(article.title)
不活性問い合わせ
查询集
不活性が実行-作成し查询集
たデータベースへのアクセスをもたらすことはありません。あなたはされるまで、その日にフィルタを保持することができ查询集
、要求値、Djangoは本当にこのクエリを実行する時期。
queryResult=models.Article.objects.``all``() # ``not` `hits ``database` `print(queryResult) # hits ``database` `for` `article ``in` `queryResult:`` ``print(article.title) # hits ``database
一般的には、唯一の「要求」查询集
彼らは結果を取得するときにデータベースに入ります。あなたは、結果が必要なのですとき查询集
介してデータベースにアクセスするための評価。発生の正確な時間が評価さについて、参照クエリセットを計算するとき。