<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # 執行查詢 # 一旦你建立好*數據模型*之后,django會自動生成一套數據庫抽象的API,可以讓你執行增刪改查的操作。這篇文檔闡述了如何使用這些API。關于所有模型檢索選項的詳細內容,請見*[數據模型參考](https://docs.djangoproject.com/en/1.8/ref/models/)*。 在整個文檔(以及參考)中,我們會大量使用下面的模型,它構成了一個博客應用。 ``` from django.db import models class Blog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField() def __str__(self): # __unicode__ on Python 2 return self.name class Author(models.Model): name = models.CharField(max_length=50) email = models.EmailField() def __str__(self): # __unicode__ on Python 2 return self.name class Entry(models.Model): blog = models.ForeignKey(Blog) headline = models.CharField(max_length=255) body_text = models.TextField() pub_date = models.DateField() mod_date = models.DateField() authors = models.ManyToManyField(Author) n_comments = models.IntegerField() n_pingbacks = models.IntegerField() rating = models.IntegerField() def __str__(self): # __unicode__ on Python 2 return self.headline ``` ## 創建對象 ## 為了把數據庫表中的數據表示成python對象,django使用一種直觀的方式:一個模型類代表數據庫的一個表,一個模型的實例代表數據庫表中的一條特定的記錄。 使用關鍵詞參數實例化一個對象來創建它,然后調用**save()**把它保存到數據庫中。 假設模型存放于文件**mysite/blog/models.py**中,下面是一個例子: ``` >>> from blog.models import Blog >>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.') >>> b.save() ``` 上面的代碼在背后執行了sql的**INSERT**操作。在你顯式調用**save()**之前,django不會訪問數據庫。 **save()**方法沒有返回值。 > 請參見 > > **save()**方法帶有一些高級選項,它們沒有在這里給出,完整的細節請見**save()**文檔。 > > 如果你想只用一條語句創建并保存一個對象,使用**create()**方法。 ## 保存對象的改動 ## 調用**save()**方法,來保存已經存在于數據庫中的對象的改動。 假設一個**Blog**的實例**b5**已經被保存在數據庫中,這個例子更改了它的名字,并且在數據庫中更新它的記錄: ``` >>> b5.name = 'New name' >>> b5.save() ``` 上面的代碼在背后執行了sql的**UPDATE**操作。在你顯式調用**save()**之前,django不會訪問數據庫。 ### 保存**ForeignKey**和**ManyToManyField**字段 ### 更新**ForeignKey**字段的方式和保存普通字段相同--只是簡單地把一個類型正確的對象賦值到字段中。下面的例子更新了**Entry**類的實例**entry**的**blog**屬性,假設**Entry**的一個合適的實例以及**Blog**已經保存在數據庫中(我們可以像下面那樣獲取他們): ``` >>> from blog.models import Entry >>> entry = Entry.objects.get(pk=1) >>> cheese_blog = Blog.objects.get(name="Cheddar Talk") >>> entry.blog = cheese_blog >>> entry.save() ``` 更新**ManyToManyField**的方式有一些不同--使用字段的**add()**方法來增加關系的記錄。這個例子向**entry**對象添加**Author**類的實例**joe**: ``` >>> 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**構造一個**QuertSet**,來從你的數據庫中獲取對象。 **QuerySet**表示你數據庫中取出來的一個對象的集合。它可以含有零個、一個或者多個過濾器,過濾器根據所給的參數限制查詢結果的范圍。在sql的角度,**QuerySet**和**SELECT**命令等價,過濾器是像**WHERE**和**LIMIT**一樣的限制子句。 你可以從模型的**Manager**那里取得**QuerySet**。每個模型都至少有一個**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**是**QuerySet**的主要來源。例如,** Blog.objects.all() **會返回持有數據庫中所有**Blog**對象的一個**QuerySet**。 ### 獲取所有對象 ### 獲取一個表中所有對象的最簡單的方式是全部獲取。使用**Manager**的**all()**方法: ``` >>> all_entries = Entry.objects.all() ``` **all()**方法返回包含數據庫中所有對象的**QuerySet**。 ### 使用過濾器獲取特定對象 ### **all()**方法返回的結果集中包含全部對象,但是更普遍的情況是你需要獲取完整集合的一個子集。 要創建這樣一個子集,需要精煉上面的結果集,增加一些過濾器作為條件。兩個最普遍的途徑是: **filter(\*\*kwargs)** 返回一個包含對象的集合,它們滿足參數中所給的條件。 **exclude(\*\*kwargs)** 返回一個包含對象的集合,它們***不***滿足參數中所給的條件。 查詢參數(上面函數定義中的**\*\*kwargs**)需要滿足特定的格式,*字段檢索*一節中會提到。 舉個例子,要獲取年份為2006的所有文章的結果集,可以這樣使用**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(2005, 1, 30) ... ) ``` 最開始的**QuerySet**包含數據庫中的所有對象,之后增加一個過濾器去掉一部分,在之后又是另外一個過濾器。最后的結果的一個**QuerySet**,包含所有標題以”word“開頭的記錄,并且日期是2005年一月,日為當天的值。 #### 過濾后的結果集是獨立的 #### 每次你篩選一個結果集,得到的都是全新的另一個結果集,它和之前的結果集之間沒有任何綁定關系。每次篩選都會創建一個獨立的結果集,可以被存儲及反復使用。 例如: ``` >>> 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()) ``` 這三個 QuerySets 是不同的。 第一個 QuerySet 包含大標題以"What"開頭的所有記錄。第二個則是第一個的子集,用一個附加的條件排除了出版日期 pub_date 是今天的記錄。 第三個也是第一個的子集,它只保留出版日期 pub_date 是今天的記錄。 最初的 QuerySet (q1) 沒有受到篩選的影響。 ## 查詢集是延遲的 ## QuerySets 是惰性的 -- 創建 QuerySet 的動作不涉及任何數據庫操作。你可以一直添加過濾器,在這個過程中,Django 不會執行任何數據庫查詢,除非 QuerySet 被執行. 看看下面這個例子: ``` >>> q = Entry.objects.filter(headline__startswith="What") >>> q = q.filter(pub_date__lte=datetime.now()) >>> q = q.exclude(body_text__icontains="food") >>> print q ``` 雖然上面的代碼看上去象是三個數據庫操作,但實際上只在最后一行 (print q) 執行了一次數據庫操作,。一般情況下, QuerySet 不能從數據庫中主動地獲得數據,得被動地由你來請求。對 QuerySet 求值就意味著 Django 會訪問數據庫。想了解對查詢集何時求值,請查看 何時對查詢集求值 (When QuerySets are evaluated). ## 其他查詢集方法 ## 大多數情況使用 all(), filter() 和 exclude() 就足夠了。 但也有一些不常用的;請查看 查詢API參考 (QuerySet API Reference) 中完整的 QuerySet 方法列表。 ## 限制查詢集范圍 ## 可以用 python 的數組切片語法來限制你的 QuerySet 以得到一部分結果。它等價于SQL中的 LIMIT 和 OFFSET 。 例如,下面的這個例子返回前五個對象 (LIMIT 5): ``` >>> Entry.objects.all()[:5] ``` 這個例子返回第六到第十之間的對象 (OFFSET 5 LIMIT 5): ``` >>> Entry.objects.all()[5:10] ``` Django 不支持對查詢集做負數索引 (例如 Entry.objects.all()[-1]) 。 一般來說,對 QuerySet 切片會返回新的 QuerySet -- 這個過程中不會對運行查詢。不過也有例外,如果你在切片時使用了 "step" 參數,查詢集就會被求值,就在數據庫中運行查詢。舉個例子,使用下面這個這個查詢集返回前十個對象中的偶數次對象,就會運行數據庫查詢: ``` >>> Entry.objects.all()[:10:2] ``` 要檢索單獨的對象,而非列表 (比如 SELECT foo FROM bar LIMIT 1),可以直接使用索引來代替切片。舉個例子,下面這段代碼將返回大標題排序后的第一條記錄 Entry: ``` >>> Entry.objects.order_by('headline')[0] ``` 大約等價于: ``` >>> Entry.objects.order_by('headline')[0:1].get() ``` 要注意的是:如果找不到符合條件的對象,第一種方法會拋出 IndexError ,而第二種方法會拋出 DoesNotExist。 詳看 get() 。 ## 字段篩選條件 ## 字段篩選條件就是 SQL 語句中的 WHERE 從句。就是 Django 中的 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 允許函式接受任意多 name-value 形式的參數,并在運行時才確定name和value的值。詳情請參閱官方Python教程中的 關鍵字參數(Keyword Arguments)。 如果你傳遞了一個無效的關鍵字參數,會拋出 TypeError 導常。 數據庫 API 支持24種查詢類型;可以在 字段篩選參考(field lookup reference) 查看詳細的列表。為了給您一個直觀的認識,這里我們列出一些常用的查詢類型: **exact** "exact" 匹配。例如: ``` >>> Entry.objects.get(headline__exact="Man bites dog") ``` 會生成如下的 SQL 語句: ``` SELECT ... WHERE headline = 'Man bites dog'; ``` 如果你沒有提供查詢類型 -- 也就是說關鍵字參數中沒有雙下劃線,那么查詢類型就會被指定為 exact。 舉個例子,這兩個語句是相等的: ``` >>> Blog.objects.get(id__exact=14) # Explicit form >>> Blog.objects.get(id=14) # __exact is implied ``` 這樣做很方便,因為 exact 是最常用的。 **iexact** 忽略大小寫的匹配。所以下面的這個查詢: ``` >>> Blog.objects.get(name__iexact="beatles blog") ``` 會匹配標題是 "Beatles Blog", "beatles blog", 甚至 "BeAtlES blOG" 的 Blog **contains** 大小寫敏感的模糊匹配。 例如: ``` Entry.objects.get(headline__contains='Lennon') ``` 大體可以翻譯為如下的 SQL: ``` SELECT ... WHERE headline LIKE '%Lennon%'; ``` 要注意這段代碼匹配大標題 'Today Lennon honored' ,而不能匹配 'today lennon honored'。 它也有一個忽略大小寫的版本,就是 icontains。 **startswith, endswith** 分別匹配開頭和結尾,同樣也有忽略大小寫的版本 istartswith 和 iendswith。 再強調一次,這僅僅是簡短介紹。完整的參考請參見 字段篩選條件參考(field lookup reference)。 ## 跨關系查詢 ## Django 提供了一種直觀而高效的方式在查詢(lookups)中表示關聯關系,它能自動確認 SQL JOIN 聯系。要做跨關系查詢,就使用兩個下劃線來鏈接模型(model)間關聯字段的名稱,直到最終鏈接到你想要的 model 為止。 這個例子檢索所有關聯 Blog 的 name 值為 'Beatles Blog' 的所有 Entry 對象: ``` >>> Entry.objects.filter(blog__name__exact='Beatles Blog') ``` 跨關系的篩選條件可以一直延展。 關系也是可逆的。可以在目標 model 上使用源 model 名稱的小寫形式得到反向關聯。 下面這個例子檢索至少關聯一個 Entry 且大標題 headline 包含 'Lennon' 的所有 Blog 對象: ``` >>> Blog.objects.filter(entry__headline__contains='Lennon') ``` 如果在某個關聯 model 中找不到符合過濾條件的對象,Django 將視它為一個空的 (所有的值都是 NULL), 但是可用的對象。這意味著不會有異常拋出,在這個例子中: ``` Blog.objects.filter(entry__author__name='Lennon') ``` (假設關聯到 Author 類), 如果沒有哪個 author 與 entry 相關聯,Django 會認為它沒有 name 屬性,而不會因為不存在 author 拋出異常。通常來說,這正是你所希望的機制。唯一的例外是使用 isnull 的情況。如下: ``` Blog.objects.filter(entry__author__name__isnull=True) ``` 這段代碼會得到 author 的 name 為空的 Blog 或 entry 的 author為空的 Blog。 如果不嫌麻煩,可以這樣寫: ``` Blog.objects.filter (entry__author__isnull=False, entry__author__name__isnull=True) ``` 跨一對多/多對多關系(Spanning multi-valued relationships) 這部分是Django 1.0中新增的: 請查看版本記錄 如果你的過濾是基于 ManyToManyField 或是逆向 ForeignKeyField 的,你可能會對下面這兩種情況感興趣。回顧 Blog/Entry 的關系(Blog 到 Entry 是一對多關系),如果要查找這樣的 blog:它關聯一個大標題包含"Lennon",且在2008年出版的 entry ;或者要查找這樣的 blogs:它關聯一個大標題包含"Lennon"的 entry ,同時它又關聯另外一個在2008年出版的 entry 。因為一個 Blog 會關聯多個的Entry,所以上述兩種情況在現實應用中是很有可能出現的。 同樣的情形也出現在 ManyToManyField 上。例如,如果 Entry 有一個 ManyToManyField 字段,名字是 tags,我們想得到 tags 是"music"和"bands"的 entries,或者我們想得到包含名為"music" 的標簽而狀態是"public"的 entry。 針對這兩種情況,Django 用一種很方便的方式來使用 filter() 和 exclude()。對于包含在同一個 filter() 中的篩選條件,查詢集要同時滿足所有篩選條件。而對于連續的 filter() ,查詢集的范圍是依次限定的。但對于跨一對多/多對多關系查詢來說,在第二種情況下,篩選條件針對的是主 model 所有的關聯對象,而不是被前面的 filter() 過濾后的關聯對象。 這聽起來會讓人迷糊,舉個例子會講得更清楚。要檢索這樣的 blog:它要關系一個大標題中含有 "Lennon" 并且在2008年出版的 entry (這個 entry 同時滿足這兩個條件),可以這樣寫: ``` Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008) ``` 要檢索另外一種 blog:它關聯一個大標題含有"Lennon"的 entry ,又關聯一個在2008年出版的 entry (一個 entry 的大標題含有 Lennon,同一個或另一個 entry 是在2008年出版的)。可以這樣寫: ``` Blog.objects.filter(entry__headline__contains='Lennon').filter( entry__pub_date__year=2008) ``` 在第二個例子中,第一個過濾器(filter)先檢索與符合條件的 entry 的相關聯的所有 blogs。第二個過濾器在此基礎上從這些 blogs 中檢索與第二種 entry 也相關聯的 blog。第二個過濾器選擇的 entry 可能與第一個過濾器所選擇的完全相同,也可能不同。 因為過濾項過濾的是 Blog,而不是 Entry。 上述原則同樣適用于 exclude():一個單獨 exclude() 中的所有篩選條件都是作用于同一個實例 (如果這些條件都是針對同一個一對多/多對多的關系)。連續的 filter() 或 exclude() 卻根據同樣的篩選條件,作用于不同的關聯對象。 在過濾器中引用 model 中的字段(Filters can reference fields on the model) 這部分是 Django 1.1 新增的: 請查看版本記錄 在上面所有的例子中,我們構造的過濾器都只是將字段值與某個常量做比較。如果我們要對兩個字段的值做比較,那該怎么做呢? Django 提供 F() 來做這樣的比較。F() 的實例可以在查詢中引用字段,來比較同一個 model 實例中兩個不同字段的值。 例如:要查詢回復數(comments)大于廣播數(pingbacks)的博文(blog entries),可以構造一個 F() 對象在查詢中引用評論數量: ``` >>> from django.db.models import F >>> Entry.objects.filter(n_pingbacks__lt=F('n_comments')) ``` Django 支持 F() 對象之間以及 F() 對象和常數之間的加減乘除和取模的操作。例如,要找到廣播數等于評論數兩倍的博文,可以這樣修改查詢語句: ``` >>> Entry.objects.filter(n_pingbacks__lt=F('n_comments') * 2) ``` 要查找閱讀數量小于評論數與廣播數之和的博文,查詢如下: ``` >>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks')) ``` 你也可以在 F() 對象中使用兩個下劃線做跨關系查詢。F() 對象使用兩個下劃線引入必要的關聯對象。例如,要查詢博客(blog)名稱與作者(author)名稱相同的博文(entry),查詢就可以這樣寫: ``` >>> Entry.objects.filter(author__name=F('blog__name')) ``` ## 主鍵查詢的簡捷方式 ## 為使用方便考慮,Django 用 pk 代表主鍵"primary key"。 以 Blog 為例, 主鍵是 id 字段,所以下面三個語句都是等價的: ``` >>> 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 查詢也可以跨關系,下面三個語句是等價的: ``` >>> 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 語句中,百分號%表示多字符匹配,而下劃線_表示單字符匹配。) 這就意味著我們可以直接使用這兩個字符,而不用考慮他們的 SQL 語義。例如,要查詢大標題中含有一個百分號%的 entry: ``` >>> Entry.objects.filter(headline__contains='%') ``` Django 會處理轉義;最終的 SQL 看起來會是這樣: ``` SELECT ... WHERE headline LIKE '%\%%'; ``` 下劃線_和百分號%的處理方式相同,Django 都會自動轉義。 ## 緩存和查詢 ## 每個 QuerySet 都包含一個緩存,以減少對數據庫的訪問。要編寫高效代碼,就要理解緩存是如何工作的。 一個 QuerySet 時剛剛創建的時候,緩存是空的。 QuerySet 第一次運行時,會執行數據庫查詢,接下來 Django 就在 QuerySet 的緩存中保存查詢的結果,并根據請求返回這些結果(比如,后面再次調用這個 QuerySet 的時候)。再次運行 QuerySet 時就會重用這些緩存結果。 要牢住上面所說的緩存行為,否則在使用 QuerySet 時可能會給你造成不小的麻煩。例如,創建下面兩個 QuerySet ,并對它們求值,然后釋放: ``` >>> print [e.headline for e in Entry.objects.all()] >>> print [e.pub_date for e in Entry.objects.all()] ``` 這就意味著相同的數據庫查詢將執行兩次,事實上讀取了兩次數據庫。而且,這兩次讀出來的列表可能并不完全相同,因為存在這種可能:在兩次讀取之間,某個 Entry 被添加到數據庫中,或是被刪除了。 要避免這個問題,只要簡單地保存 QuerySet 然后重用即可: ``` >>> queryset = Poll.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. ``` 用 Q 對象實現復雜查找 (Complex lookups with Q objects) 在 filter() 等函式中關鍵字參數彼此之間都是 "AND" 關系。如果你要執行更復雜的查詢(比如,實現篩選條件的 OR 關系),可以使用 Q 對象。 Q 對象(django.db.models.Q)是用來封裝一組查詢關鍵字的對象。這里提到的查詢關鍵字請查看上面的 "Field lookups"。 例如,下面這個 Q 對象封裝了一個單獨的 LIKE 查詢: ``` Q(question__startswith='What') ``` Q 對象可以用 & 和 | 運算符進行連接。當某個操作連接兩個 Q 對象時,就會產生一個新的等價的 Q 對象。 例如,下面這段語句就產生了一個 Q ,這是用 "OR" 關系連接的兩個 "question__startswith" 查詢: ``` 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()) 除了能接收關鍵字參數以外,也能以位置參數的形式接受一個或多個 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))) ``` ... 但這個查詢卻是無效的。 > 參見 > > 在 Django 的單元測試 OR查詢實例(OR lookups examples) 中展示了 Q 的用例。 ## 對象比較 ## 要比較兩個對象,就和 Python 一樣,使用雙等號運算符:==。實際上比較的是兩個 model 的主鍵值。 以上面的 Entry 為例,下面兩個語句是等價的: ``` >>> some_entry == other_entry >>> some_entry.id == other_entry.id ``` 如果 model 的主鍵名稱不是 id,也沒關系。Django 會自動比較主鍵的值,而不管他們的名稱是什么。例如,如果一個 model 的主鍵字段名稱是 name,那么下面兩個語句是等價的: ``` >>> some_obj == other_obj >>> some_obj.name == other_obj.name ``` ## 對象刪除 ## 刪除方法就是 delete()。它運行時立即刪除對象而不返回任何值。例如: ``` e.delete() ``` 你也可以一次性刪除多個對象。每個 QuerySet 都有一個 delete() 方法,它一次性刪除 QuerySet 中所有的對象。 例如,下面的代碼將刪除 pub_date 是2005年的 Entry 對象: ``` Entry.objects.filter(pub_date__year=2005).delete() ``` 要牢記這一點:無論在什么情況下,QuerySet 中的 delete() 方法都只使用一條 SQL 語句一次性刪除所有對象,而并不是分別刪除每個對象。如果你想使用在 model 中自定義的 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() ``` 要注意的是: delete() 方法是 QuerySet 上的方法,但并不適用于 Manager 本身。這是一種保護機制,是為了避免意外地調用 Entry.objects.delete() 方法導致 所有的 記錄被誤刪除。如果你確認要刪除所有的對象,那么你必須顯式地調用: ``` Entry.objects.all().delete() ``` 一次更新多個對象 (Updating multiple objects at once) 這部分是 Django 1.0 中新增的: 請查看版本文檔 有時你想對 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() 方法也是即時生效,不返回任何值的(與 delete() 相似)。 在 QuerySet 進行更新時,唯一的限制就是一次只能更新一個數據表,就是當前 model 的主表。所以不要嘗試更新關聯表和與此類似的操作,因為這是不可能運行的。 要小心的是: update() 方法是直接翻譯成一條 SQL 語句的。因此它是直接地一次完成所有更新。它不會調用你的 model 中的 save() 方法,也不會發出 pre_save 和 post_save 信號(這些信號在調用 save() 方法時產生)。如果你想保存 QuerySet 中的每個對象,并且調用每個對象各自的 save() 方法,那么你不必另外多寫一個函式。只要遍歷這些對象,依次調用 save() 方法即可: ``` for item in my_queryset: item.save() ``` 這部分是在 Django 1.1 中新增的: 請查看版本文檔 在調用 update 時可以使用 F() 對象 來把某個字段的值更新為另一個字段的值。這對于自增記數器是非常有用的。例如,給所有的博文 (entry) 的廣播數 (pingback) 加一: ``` >>> Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1) ``` 但是,與 F() 對象在查詢時所不同的是,在filter 和 exclude子句中,你不能在 F() 對象中引入關聯關系(NO-Join),你只能引用當前 model 中要更新的字段。如果你在 F() 對象引入了Join 關系object,就會拋出 FieldError 異常: ``` # THIS WILL RAISE A FieldError >>> Entry.objects.update(headline=F('blog__name')) ``` ## 對象關聯 ## 當你定義在 model 定義關系時 (例如, ForeignKey, OneToOneField, 或 ManyToManyField),model 的實例自帶一套很方便的API以獲取關聯的對象。 以最上面的 models 為例,一個 Entry 對象 e 能通過 blog 屬性獲得相關聯的 Blog 對象: e.blog。 (在場景背后,這個功能是由 Python 的 descriptors 實現的。如果你對此感興趣,可以了解一下。) Django 也提供反向獲取關聯對象的 API,就是由從被關聯的對象得到其定義關系的主對象。例如,一個 Blog 類的實例 b 對象通過 entry_set 屬性得到所有相關聯的 Entry 對象列表: b.entry_set.all()。 這一節所有的例子都使用本頁頂部所列出的 Blog, Author 和 Entry model。 ## 一對多關系 ## ### 正向 ### 如果一個 model 有一個 ForeignKey字段,我們只要通過使用關聯 model 的名稱就可以得到相關聯的外鍵對象。 例如: ``` >>> e = Entry.objects.get(id=2) >>> e.blog # Returns the related Blog object. ``` 你可以設置和獲得外鍵屬性。正如你所期望的,改變外鍵的行為并不引發數據庫操作,直到你調用 save()方法時,才會保存到數據庫。例如: ``` >>> e = Entry.objects.get(id=2) >>> e.blog = some_blog >>> e.save() ``` 如果外鍵字段 ForeignKey 有一個 null=True 的設置(它允許外鍵接受空值 NULL),你可以賦給它空值 None 。例如: ``` >>> e = Entry.objects.get(id=2) >>> e.blog = None >>> e.save() # "UPDATE blog_entry SET blog_id = NULL ...;" ``` 在一對多關系中,第一次正向獲取關聯對象時,關聯對象會被緩存。其后根據外鍵訪問時這個實例,就會從緩存中獲得它。例如: ``` >>> e = Entry.objects.get(id=2) >>> print e.blog # Hits the database to retrieve the associated Blog. >>> print e.blog # Doesn't hit the database; uses cached version. ``` 要注意的是,QuerySet 的 select_related() 方法提前將所有的一對多關系放入緩存中。例如: ``` >>> e = Entry.objects.select_related().get(id=2) >>> print e.blog # Doesn't hit the database; uses cached version. >>> print e.blog # Doesn't hit the database; uses cached version. ``` ### 逆向關聯 ### 如果 model 有一個 ForeignKey外鍵字段,那么外聯 model 的實例可以通過訪問 Manager 來得到所有相關聯的源 model 的實例。默認情況下,這個 Manager 被命名為 FOO_set, 這里面的 FOO 就是源 model 的小寫名稱。這個 Manager 返回 QuerySets,它是可過濾和可操作的,在上面 "對象獲取(Retrieving objects)" 有提及。 例如: ``` >>> b = Blog.objects.get(id=1) >>> b.entry_set.all() # Returns all Entry objects related to Blog. # b.entry_set is a Manager that returns QuerySets. >>> b.entry_set.filter(headline__contains='Lennon') >>> b.entry_set.count() ``` 你可以通過在 ForeignKey() 的定義中設置 related_name 的值來覆寫 FOO_set 的名稱。例如,如果 Entry model 中做一下更改: blog = ForeignKey(Blog, related_name='entries'),那么接下來就會如我們看到這般: ``` >>> b = Blog.objects.get(id=1) >>> b.entries.all() # Returns all Entry objects related to Blog. # b.entries is a Manager that returns QuerySets. >>> b.entries.filter(headline__contains='Lennon') >>> b.entries.count() ``` 你不能在一個類當中訪問 ForeignKey Manager ;而必須通過類的實例來訪問: ``` >>> Blog.entry_set Traceback: ... AttributeError: "Manager must be accessed via instance". ``` 除了在上面 "對象獲取Retrieving objects" 一節中提到的 QuerySet 方法之外,ForeignKey Manager 還有如下一些方法。下面僅僅對它們做一個簡短介紹,詳情請查看 related objects reference。 `add(obj1, obj2, ...)` 將某個特定的 model 對象添加到被關聯對象集合中。 `create(**kwargs)` 創建并保存一個新對象,然后將這個對象加被關聯對象的集合中,然后返回這個新對象。 `remove(obj1, obj2, ...)` 將某個特定的對象從被關聯對象集合中去除。 `clear()` 清空被關聯對象集合。 想一次指定關聯集合的成員,那么只要給關聯集合分配一個可迭代的對象即可。它可以包含對象的實例,也可以只包含主鍵的值。例如: ``` b = Blog.objects.get(id=1) b.entry_set = [e1, e2] ``` 在這個例子中,e1 和 e2 可以是完整的 Entry 實例,也可以是整型的主鍵值。 如果 clear() 方法是可用的,在迭代器(上例中就是一個列表)中的對象加入到 entry_set 之前,已存在于關聯集合中的所有對象將被清空。如果 clear() 方法 不可用,原有的關聯集合中的對象就不受影響,繼續存在。 這一節提到的每一個 "reverse" 操作都是實時操作數據庫的,每一個添加,創建,刪除操作都會及時保存將結果保存到數據庫中。 ## 多對多關系 ## 在多對多關系的任何一方都可以使用 API 訪問相關聯的另一方。多對多的 API 用起來和上面提到的 "逆向" 一對多關系關系非常相象。 唯一的差雖就在于屬性的命名: ManyToManyField 所在的 model (為了方便,我稱之為源model A) 使用字段本身的名稱來訪問關聯對象;而被關聯的另一方則使用 A 的小寫名稱加上 '_set' 后綴(這與逆向的一對多關系非常相象)。 下面這個例子會讓人更容易理解: ``` e = Entry.objects.get(id=3) e.authors.all() # Returns all Author objects for this Entry. e.authors.count() e.authors.filter(name__contains='John') a = Author.objects.get(id=5) a.entry_set.all() # Returns all Entry objects for this Author. ``` 與 ForeignKey 一樣, ManyToManyField 也可以指定 related_name。在上面的例子中,如果 Entry 中的 ManyToManyField 指定 related_name='entries',那么接下來每個 Author 實例的 entry_set 屬性都被 entries 所代替。 ### 一對一關系 ### 相對于多對一關系而言,一對一關系不是非常簡單的。如果你在 model 中定義了一個 OneToOneField 關系,那么你就可以用這個字段的名稱做為屬性來訪問其所關聯的對象。 例如: ``` class EntryDetail(models.Model): entry = models.OneToOneField(Entry) details = models.TextField() ed = EntryDetail.objects.get(id=2) ed.entry # Returns the related Entry object. ``` 與 "reverse" 查詢不同的是,一對一關系的關聯對象也可以訪問 Manager 對象,但是這個 Manager 表現一個單獨的對象,而不是一個列表: ``` e = Entry.objects.get(id=2) e.entrydetail # returns the related EntryDetail object ``` 如果一個空對象被賦予關聯關系,Django 就會拋出一個 DoesNotExist 異常。 和你定義正向關聯所用的方式一樣,類的實例也可以賦予逆向關聯方系: ``` e.entrydetail = ed ``` ## 關系中的反向連接是如何做到的? ## 其他對象關系的映射(ORM)需要你在關聯雙方都定義關系。而 Django 的開發者則認為這違背了 DRY 原則 (Don't Repeat Yourself),所以 Django 只需要你在一方定義關系即可。 但僅由一個 model 類并不能知道其他 model 類是如何與它關聯的,除非是其他 model 也被載入,那么這是如何辦到的? 答案就在于 INSTALLED_APPS 設置中。任何一個 model 在第一次調用時,Django 就會遍歷所有的 INSTALLED_APPS 的所有 models,并且在內存中創建中必要的反向連接。本質上來說,INSTALLED_APPS 的作用之一就是確認 Django 完整的 model 范圍。 ## 在關聯對象上的查詢 ## 包含關聯對象的查詢與包含普通字段值的查詢都遵循相同的規則。為某個查詢指定某個值的時候,你可以使用一個類實例,也可以使用對象的主鍵值。 例如,如果你有一個 Blog 對象 b ,它的 id=5, 下面三個查詢是一樣的: ``` Entry.objects.filter(blog=b) # Query using object instance Entry.objects.filter(blog=b.id) # Query using id from instance Entry.objects.filter(blog=5) # Query using id directly ``` ## 直接使用SQL ## 如果你發現某個 SQL 查詢用 Django 的數據庫映射來處理會非常復雜的話,你可以使用直接寫 SQL 來完成。 建議的方式是在你的 model 自定義方法或是自定義 model 的 manager 方法來運行查詢。雖然 Django 不要求數據操作必須在 model 層中執行。但是把你的商業邏輯代碼放在一個地方,從代碼組織的角度來看,也是十分明智的。詳情請查看 執行原生SQL查詢(Performing raw SQL queries). 最后,要注意的是,Django的數據操作層僅僅是訪問數據庫的一個接口。你可以用其他的工具,編程語言,數據庫框架來訪問數據庫。對你的數據庫而言,沒什么是非用 Django 不可的。 > 譯者:[Django 文檔協作翻譯小組](http://python.usyiyi.cn/django/index.html),原文:[Executing queries](https://docs.djangoproject.com/en/1.8/topics/db/queries/)。 > > 本文以 [CC BY-NC-SA 3.0](http://creativecommons.org/licenses/by-nc-sa/3.0/cn/) 協議發布,轉載請保留作者署名和文章出處。 > > [Django 文檔協作翻譯小組](http://python.usyiyi.cn/django/index.html)人手緊缺,有興趣的朋友可以加入我們,完全公益性質。交流群:467338606。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看