Django book2 模型 學習筆記

風之諾發表於2014-02-25
Djanjo採用模型的優點:
1 自省(執行時自動識別資料庫)會導致過載和有資料完整性問題
2 把資料模型用程式碼的方式表述來讓你可以容易對它們進行版本控制。 這樣,你可以很容易瞭解資料層 的變
  動情況。
3  SQL只能描述特定型別的資料欄位。 例如,大多數資料庫都沒有專用的欄位型別來描述Email地址
  而用Django的模型可以做到這一點。 好處就是高階的資料型別帶來更高的效率和更好的程式碼複用
  
4  SQL還有在不同資料庫平臺的相容性問題。 釋出Web應用的時候,使用Python模組描述資料庫結構資訊可
  以避免為MySQL, PostgreSQL, and SQLite編寫不同的CREATE TABLE。


要解決的問題:
  Python程式碼和資料庫表的同步問題。 如果你修改了一個Django模型, 你要自己來修改資料庫來保證和模型同步
  
  
一 建立MSSQL資料庫 bookdb
   建立project
   django-admin.py startproject mysite
   
二 配置settings.py的DATABASES配置
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
            'NAME': 'bookdb',                      # Or path to database file if using sqlite3.
            'USER': 'root',                      # Not used with sqlite3.
            'PASSWORD': '423423',                  # Not used with sqlite3.
            'HOST': 'localhost',                      # Set to empty string for localhost. Not used with sqlite3.
            'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
        }
    }
    一旦在輸入了那些設定並儲存之後應當測試一下你的配置。 我們可以在`` depot`` 專案目錄下執行`
    python manage.py shell 
    來進行測試。(是以正確Django配置啟用Python互動直譯器的一種方法。 這個方法在這裡是很有必要的,
    因為Django需要知道載入 哪個配置檔案來獲取資料庫連線資訊。)
    輸入下面這些命令來測試你的資料庫配置:
    >>> from django.db import connection
    >>> cursor = connection.cursor()
三 建立模型


1 在專案中建立一個app
    系統對app有一個約定: 如果你使用了Django的資料庫層(模型),你 必須建立一個Django app。
    模型必須存放在apps中。 因此,為了開始建造 我們的模型,我們必須建立一個新的app    
    python manage.py startapp books
    
2 建立module.py:


   1)首先要注意的事是每個資料模型都是 django.db.models.Model 的子
   類。它的父類 Model 包含了所有必要的和資料庫互動的方法,並提供了一個簡潔漂亮的定義資料庫欄位的語
   法。
   
   2)每個模型相當於單個資料庫表,每個屬性也是這個表中的一個欄位。 屬性名就是欄位名,它的型別(例如
     CharField )相當於資料庫的欄位型別 (例如 varchar )
     
   3)“每個資料庫表對應一個類”這條規則的例外情況是多對多關係。 在我們的範例模型中, Book 有一個
    多對多欄位 叫做 authors 。 該欄位表明一本書籍有一個或多個作者,但 Book 資料庫表卻並沒有 authors 字
    段。 相反,Django建立了一個額外的表(多對多連線表)來處理書籍和作者之間的對映關係
    
  ###自定義表屬性相關
  4)我們並沒有顯式地為這些模型定義任何主鍵。 除非你單獨指明,否則Django會自動為每
   個模型生成一個自增長的整數主鍵欄位每個Django模型都要求有單獨的主鍵。id
   
  5)預設的欄位名與屬性名相同,可以通過欄位的db_column屬性自定義,如
      where = models.CharField(max_length=400, db_column='place')
  6)除了ID欄位,其他欄位預設不建立索引,可以通過欄位的db_index屬性自定義,如
     publish_date= models.DateField(db_index=True)
  7)自定義ID
     如果不指定ID,預設建立一個型別為IntegerField的id欄位,可以通過欄位的primary_key屬性指定自己的ID欄位。如:
     key = models.IntegerField(primary_key=True)
  8)自定義表名 
    預設的表名是appName_modelName,在Model類的Meta中可以通過db_table屬性改變預設的表名。
        
  9) 自定義表空間
    對於索引欄位,預設在settings的DEFAULT_INDEX_TABLESPACE設定的表空間中建立索引,可以通過欄位的db_tablespace屬性指定,如
         publish_date= models.DateField(db_index=True,db_tablespace='another_tbs')
    對於Model類,通過Meta中的db_tablespace屬性指定特定的表空間。


  10)自定義關聯關係
       1)外來鍵預設關聯到對方的主鍵欄位,可以通過外來鍵的to_field指定關聯到的欄位,如
              to_model = models.ForeighKey(ToModel,to_field='key')
       2)對於many-to-many關聯,Django會建立一個關聯表,預設表名是兩個表名通過下劃線連線起來。
        可以通過ManyToManyField的db_table指定關聯表的表名。  
       3)對於many-to-many關聯,如果不想使用Django建立的關聯表,可以通過ManyToManyField的through屬性指定到一個已存在的Model類。 
    
3 模型安裝(生成資料庫表)
 完成這些程式碼之後,現在讓我們來在資料庫中建立這些表。 要完成該項工作,
 1)第一步是在 Django 專案中 啟用這些模型。 將 books app 新增到配置檔案的已安裝應用列表中即可完成此步驟。
 
 2)驗證模型的有效性:validate 命令檢查你的模型的語法和邏輯是否正確。
  python manage.py validate
  
 3)執行下面的命令來生成 CREATE TABLE 語句
    python manage.py sqlall books
    
    sqlall 命令並沒有在資料庫中真正建立資料表,只是把SQL語句段列印出來,這樣你可以看到Django究竟會做
   些什麼。 如果你想這麼做的話,你可以把那些SQL語句複製到你的資料庫客戶端執行,或者通過Unix管道直接
   進行操作(例如,`` python manager.py sqlall books | psql mydb`` )。
   
4)Django提供了一種更為簡易的提交SQL語句至資料庫的方法: `` syncdb`` 命令
    python manage.py syncdb
    
    執行這個命令後,將看到類似以下的內容:
    Creating table books_publisher
    Creating table books_author
    Creating table books_book
    Installing index for books.Book model
    syncdb 命令是同步你的模型到資料庫的一個簡單方法。 它會根據 INSTALLED_APPS 裡設定的app來檢查資料庫, 
    如果表不存在,它就會建立它。 需要注意的是, syncdb 並 不能將模型的修改或刪除同步到資料庫;
    如果你修改或刪除了一個模型,並想把它提交到資料庫,syncdb並不會做出任何處理。
   
    如果你再次執行 python manage.py syncdb ,什麼也沒發生,因為你沒有新增新的表,執行python manage.py syncdb總是安全的,因為它不會重複執行SQL語句。
    如果你有興趣,花點時間用你的SQL客戶端登入進資料庫伺服器看看剛才Django建立的資料表。 你可以手動啟
    動命令列客戶端(例如,執行PostgreSQL的`` psql`` 命令),也可以執行 `` python manage.py dbshell`` ,
    這個命令將依據`` DATABASE_SERVER`` 的裡設定自動檢測使用哪種命令列客戶端。
    
5)基本資料訪問
    python manage.py shell 並輸入
    下面的內容試試看:
    >>> from books.models import Publisher
    >>> p1 = Publisher(name='Apress', address='2855 Telegraph Avenue',
    ...     city='Berkeley', state_province='CA', country='U.S.A.',
    ...     website='http://www.apress.com/')
    >>> p1.save()
    >>> p2 = Publisher(name="O'Reilly", address='10 Fawcett St.',
    ...     city='Cambridge', state_province='MA', country='U.S.A.',
    ...     website='http://www.oreilly.com/')
    >>> p2.save()
    >>> publisher_list = Publisher.objects.all()
    >>> publisher_list
    [<Publisher: Publisher object>, <Publisher: Publisher object>]
    
    這短短几行程式碼幹了不少的事。 這裡簡單的說一下:
    1)首先,匯入Publisher模型類, 通過這個類我們可以與包含 出版社 的資料表進行互動。
    2)接著,建立一個`` Publisher`` 類的例項並設定了欄位`` name, address`` 等的值。
    3)呼叫該物件的 save() 方法,將物件儲存到資料庫中。 Django 會在後臺執行一條 INSERT 語句。
    4)最後,使用`` Publisher.objects`` 屬性從資料庫取出出版商的資訊,這個屬性可以認為是包含出版商的記
    錄集。 這個屬性有許多方法, 這裡先介紹呼叫`` Publisher.objects.all()`` 方法獲取資料庫中`` Publisher``
    類的所有物件。這個操作的幕後,Django執行了一條SQL `` SELECT`` 語句。
    
    這裡有一個值得注意的地方,在這個例子可能並未清晰地展示。 當你使用Django modle API建立物件時
    Django並未將物件儲存至資料庫內,除非你呼叫`` save()`` 方法:
    
   如果需要一步完成物件的建立與儲存至資料庫,就使用`` objects.create()`` 方法


6)新增模組的字串表現
為Publisher 物件新增一個方法 __unicode__() 。 __unicode__() 方法告
訴Python如何將物件以unicode的方式顯示出來。 
為以上三個模型新增__unicode__()方法後,就可以看到效
果了:


 為了讓我們的修改生效,先退出Python Shell,然後再次執行 python manage.py shell 進入。(這是保證程式碼
修改生效的最簡單方法。)現在`` Publisher``物件列表容易理解多了。
>>> from books.models import Publisher
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
[<Publisher: Apress>, <Publisher: O'Reilly>]


請確保你的每一個模型裡都包含 __unicode__() 方法,這不只是為了互動時方便,也是因為 Django會在其他
一些地方用 __unicode__() 來顯示物件。
最後, __unicode__() 也是一個很好的例子來演示我們怎麼新增 行為 到模型裡。 Django的模型不只是為物件
定義了資料庫表的結構,還定義了物件的行為。 __unicode__() 就是一個例子來演示模型知道怎麼顯示它們自
己。


四 模型操作(一般在view.py進行)
1 插入資料
    先使用一些關鍵引數建立物件例項,如下:
    >>> p = Publisher(name='Apress',
    ...         address='2855 Telegraph Ave.',
    ...         city='Berkeley',
    ...         state_province='CA',
    ...         country='U.S.A.',
                website='http://www.apress.com/')
    >>> p.save()  
    


            
    因為 Publisher 模型有一個自動增加的主鍵 id ,所以第
    並把它賦值給這個物件例項:
    >>> p.id
    52    # this will differ based on your own data     


2 查詢資料
    1)filter:獲取列表
    你可以傳遞多個引數到 filter() 來縮小選取範圍:    
    >>> Publisher.objects.filter(country="U.S.A.", state_province="CA")
    [<Publisher: Apress>]


    在 name 和 contains 之間有雙下劃線。和Python一樣,Django也使用雙下劃線來表明會進行一些魔術般的
    操作。這裡,contains部分會被Django翻譯成LIKE語句:
    >>> Publisher.objects.filter(name__contains="press")
    [<Publisher: Apress>]


    其他的一些查詢型別有:icontains(大小寫無關的LIKE),startswith和endswith, 還有range(SQLBETWEEN查
    詢)。
    
    2)get:獲取單個物件
    上面的例子中`` filter()`` 函式返回一個記錄集,這個記錄集是一個列表。 相對列表來說,有些時候我們更需要
    獲取單個的物件, `` get()`` 方法就是在此時使用的:(一般是主鍵作為查詢)
    >>> Publisher.objects.get(name="Apress")
    <Publisher: Apress>
    這樣,就返回了單個物件,而不是列表(更準確的說,QuerySet)。 所以,如果結果是多個物件,會導致丟擲
    異常:
    
    3)資料排序
    如果需要以多個欄位為標準進行排序(第二個欄位會在第一個欄位的值相同的情況下被使用到),使用多個參
    數就可以了,如下:
    >>> Publisher.objects.order_by("state_province", "address")
     [<Publisher: Apress>, <Publisher: O'Reilly>]
    我們還可以指定逆向排序,在前面加一個減號 ‐ 字首:
    >>> Publisher.objects.order_by("-name")
    [<Publisher: O'Reilly>, <Publisher: Apress>]
    
    預設排序
    
    class Publisher(models.Model):
    name = models.CharField(max_length=30)
    address = models.CharField(max_length=50)
    city = models.CharField(max_length=60)
    state_province = models.CharField(max_length=30)
    country = models.CharField(max_length=50)
    website = models.URLField()
    def __unicode__(self):
        return self.name
    class Meta:
        ordering = ['name']
    現在,讓我們來接觸一個新的概念。 class Meta,內嵌於 Publisher 這個類的定義中(如果 class Publisher
    是頂格的,那麼 class Meta 在它之下要縮排4個空格--按 Python 的傳統 )。你可以在任意一個 模型 類中
    使用 Meta 類,來設定一些與特定模型相關的選項。 在 附錄B 中有 Meta 中所有可選項的完整參考,現在,我
    關注 ordering 這個選項就夠了。 如果你設定了這個選項,那麼除非你檢索時特意額外地使用了 order_by(),
    否則,當你使用 Django 的資料庫 API 去檢索時,Publisher物件的相關返回值預設地都會按 name 欄位排序。
    
    4)我們已經知道如何對資料進行過濾和排序。 當然,通常我們需要同時進行過濾和排序查詢的操作。 因此,你可
    以簡單地寫成這種“鏈式”的形式:
    >>> Publisher.objects.filter(country="U.S.A.").order_by("-name")
    [<Publisher: O'Reilly>, <Publisher: Apress>]
    
    5)限制返回的資料 
    另一個常用的需求就是取出固定數目的記錄。 想象一下你有成千上萬的出版商在你的資料庫裡, 但是你只想顯
    示第一個。 你可以使用標準的Python列表裁剪語句:
    >>> Publisher.objects.order_by('name')[0]
    <Publisher: Apress>
    
    類似的,你可以用Python的range-slicing語法來取
    >>> Publisher.objects.order_by('name')[0:2]
    
3 更新資料
    我們可以看到Django的save()方法更新了不僅僅是name列的值,還有更新了所有的列。 若
    name以外的列有可能會被其他的程式所改動的情況下,只更改name列顯然是更加明智的。 更改某一指定的
    列,我們可以呼叫結果集(QuerySet)物件的update()方法: 示例如下:
    >>> Publisher.objects.filter(id=4).update(name='Apress Publishing')
    
    與之等同的SQL語句變得更高效,並且不會引起競態條件。
    UPDATE books_publisher
    SET name = 'Apress Publishing'
    WHERE id = 52;
    update()方法對於任何結果集(QuerySet)均有效,這意味著你可以同時更新多條記錄。 以下示例演示如何
    將所有Publisher的country欄位值由’U.S.A’更改為’USA’:
    >>> Publisher.objects.all().update(country='USA')
    2
    update()方法會返回一個整型數值,表示受影響的記錄條數。 在上面的例子中,這個值是2。
4 刪除資料


    >>> Publisher.objects.filter(name='Apress Publishing').delete()
    >>> Publisher.objects.filter(country='USA').delete()
    >>> Publisher.objects.all().delete()
    >>> Publisher.objects.all()
    []
    刪除資料時要謹慎! 為了預防誤刪除掉某一個表內的所有資料,Django要求在刪除表內所有資料時顯示使用
    all()。
    
    
    
    
    
    
 五 資料模型高階進階
 from django.db import models
class Publisher(models.Model):
    name = models.CharField(max_length=30)
    address = models.CharField(max_length=50)
    city = models.CharField(max_length=60)
    state_province = models.CharField(max_length=30)
    country = models.CharField(max_length=50)
    website = models.URLField()
    def __unicode__(self):
        return self.name
class Author(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=40)
    email = models.EmailField()
    def __unicode__(self):
        return u'%s %s' % (self.first_name, self.last_name)
class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher)
    publication_date = models.DateField()
    def __unicode__(self):
        return self.title
        
1 訪問外來鍵(Foreign Key)值
    當你獲取一個ForeignKey 欄位時,你會得到相關的資料模型物件。 例如:
    >>> b = Book.objects.get(id=1)
    >>> b.publisher
    <Publisher: Apress Publishing>
    >>> b.publisher.website
    u'http://www.apress.com/'
    
    對於用`` ForeignKey`` 來定義的關係來說,在關係的另一端也能反向的追溯回來,
    。通過一個`` publisher`` 物件,直接獲取 books ,用 publisher.book_set.all() ,如
    
    >>> p = Publisher.objects.get(name='O\'Reilly')
    >>> p.book_set.all()
    [<Book: The Django Book>, <Book: Dive Into Python>, ...]
    
    實際上,book_set 只是一個 QuerySet(參考第5章的介紹),所以它可以像QuerySet一樣,能實現資料過濾和分
    切,例如:
    >>> p = Publisher.objects.get(name='Apress Publishing')
    >>> p.book_set.filter(name__icontains='django')
    [<Book: The Django Book>, <Book: Pro Django>]
    屬性名稱book_set是由模型名稱的小寫(如book)加_set組成的。 
    
2  訪問多對多值(Many-to-Many Values)
    多對多和外來鍵工作方式相同,只不過我們處理的是QuerySet而不是模型例項。 
    >>> b = Book.objects.get(id=1)
    >>> b.authors.all()
    [<Author: Apress Holovaty>] 
    >>> b.authors.filter(first_name='Apress')
    [<Author: Adrian Holovaty>]
    >>> b.authors.filter(first_name='Adam')
    []
    反向查詢也可以。 要檢視一個作者的所有書籍,使用author.book_set ,就如這樣:
    >>> a = Author.objects.get(first_name='Apress', last_name='Holovaty')
    >>> a.book_set.all()
    [<Book: The Django Book>]
    這裡,就像使用 ForeignKey欄位一樣,屬性名book_set是在資料模型(model)名後追加_set。


                             
3 更改資料庫模式(Database Schema)
    在我們在第5章介紹 syncdb 這個命令時, 我們注意到 syncdb僅僅建立資料庫裡還沒有的表,它 並不 對你資料模
    型的修改進行同步,也不處理資料模型的刪除。 如果你新增或修改資料模型裡的欄位,或是刪除了一個資料模型,
    你需要手動在資料庫裡進行相應的修改。


    當處理模型修改的時候,將Django的資料庫層的工作流程銘記於心是很重要的。
    1)如果模型包含一個未曾在資料庫裡建立的欄位,Django會報出錯資訊。 當你第一次用Django的資料庫
      API請求表中不存在的欄位時會導致錯誤(就是說,它會在執行時出錯,而不是編譯時)。
    2) Django不關心資料庫表中是否存在未在模型中定義的列。
    3)Django不關心資料庫中是否存在未被模型表示的表格。
    
    改變模型的模式架構意味著需要按照順序更改Python程式碼和資料庫。
    
    A 新增欄位
    
        當要向一個產品設定表(或者說是model)新增一個欄位的時候,要使用的技巧是利用Django不關心表裡是否包
        含model裡所沒有的列的特性。 策略就是現在資料庫里加入欄位,然後同步Django的模型以包含新欄位。
        
        首先,進入開發環境(也就是說,不是在釋出環境裡):
        1.  在你的模型裡新增欄位。
        2.  執行 manage.py sqlall [yourapp] 來測試模型新的 CREATE TABLE 語句。 注意為新欄位的列定義。
        3.  開啟你的資料庫的互動命令介面(比如, psql 或mysql , 或者可以使用 manage.py dbshell )。 執行
        ALTER TABLE 語句來新增新列。
        4.  使用Python的manage.py shell,通過匯入模型和選中表單(例如, MyModel.objects.all()[:5] )來驗證
        新的欄位是否被正確的新增 ,如果一切順利,所有的語句都不會報錯。
        然後在你的產品伺服器上再實施一遍這些步驟。
        1.  啟動資料庫的互動介面。
        2.  執行在開發環境步驟中,第三步的ALTER TABLE語句。
        3.  將新的欄位加入到模型中。 如果你使用了某種版本控制工具,並且在第一步中,已經提交了你在開發環境
        上的修改,現在,可以在生產環境中更新你的程式碼了(例如,如果你使用Subversion,執行svn update。
        4.  重新啟動Web server,使修改生效。
        
            讓我們實踐下,比如新增一個num_pages欄位到第五章中Book模型。首先,我們會把開發環境中的模型改成
            如下形式:
            class Book(models.Model):
                title = models.CharField(max_length=100)
                authors = models.ManyToManyField(Author)
                publisher = models.ForeignKey(Publisher)
                publication_date = models.DateField()
                num_pages = models.IntegerField(blank=True, null=True)
                def __unicode__(self):
                    return self.title
            (注意 閱讀第六章的“設定可選欄位”以及本章下面的“新增非空列”小節以瞭解我們在這裡添
            加blank=True和null=True的原因。)
            然後,我們執行命令manage.py sqlall books 來檢視CREATE TABLE語句。 語句的具體內容取決與你所使用
            的資料庫, 大概是這個樣子:
            CREATE TABLE "books_book" (
                "id" serial NOT NULL PRIMARY KEY,
                "title" varchar(100) NOT NULL,
                "publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id"),
                "publication_date" date NOT NULL,
                "num_pages" integer NULL
            );
            新加的欄位被這樣表示:
            "num_pages" integer NULL
            接下來,我們要在開發環境上執行資料庫客戶端,如果是PostgreSQL,執行 psql,,然後,我執行如下語句。
            ALTER TABLE books_book ADD COLUMN num_pages integer NULL;
            新增 NULL 欄位
            
           
            >>> from mysite.books.models import Book
            >>> Book.objects.all()[:5]
            如果沒有異常發生,我們將切換到生產伺服器,然後在生產環境的資料庫中執行命令ALTER TABLE 然後我們更新
            生產環境中的模型,最後重啟web伺服器。
    B 刪除欄位
        刪除欄位,然後重新啟動你的web伺服器。
        用以下命令從資料庫中刪除欄位:
        ALTER TABLE books_book DROP COLUMN num_pages;


    C  刪除多對多關聯欄位
        從你的模型中刪除ManyToManyField,然後重啟web伺服器。
        用下面的命令從資料庫刪除關聯表:
        DROP TABLE books_book_authors;
    
    D  刪除模型
   
        從檔案中刪除你想要刪除的模型,然後重啟web 伺服器models.py
        然後用以下命令從資料庫中刪除表:
        DROP TABLE books_book;
        當你需要從資料庫中刪除任何有依賴的表時要注意(也就是任何與表books_book有外來鍵的表 )。




4 Managers
    在語句Book.objects.all()中,objects是一個特殊的屬性,需要通過它查詢資料庫。在第5章,我們只是簡要地說這是模組的manager 。
    模組manager是一個物件,Django模組通過它進行資料庫查詢。 
    每個Django模組至少有一個manager,你可以建立自定義manager以定製資料庫訪問。
    下面是你建立自定義manager的兩個原因: 增加額外的manager方法,和/或修改manager返回的初始QuerySet。


    1)增加額外的Manager方法
        增加額外的manager方法是為模組新增表級功能的首選辦法。 (至於行級功能,也就是隻作用於模型物件例項
        的函式,一會兒將在本章後面解釋。)
        例如,我們為Book模型定義了一個title_count()方法,它需要一個關鍵字,返回包含這個關鍵字的書的數量。


        # models.py
        from django.db import models
        # ... Author and Publisher models here ...
        class BookManager(models.Manager):
            def title_count(self, keyword):
                return self.filter(title__icontains=keyword).count()
        class Book(models.Model):
            title = models.CharField(max_length=100)
            authors = models.ManyToManyField(Author)
            publisher = models.ForeignKey(Publisher)
            publication_date = models.DateField()
            num_pages = models.IntegerField(blank=True, null=True)
            objects = BookManager()
            def __unicode__(self):
                return self.title
                
        有了這個manager,我們現在可以這樣做:
        >>> Book.objects.title_count('Django')
        4


        下面是編碼該注意的一些地方:
        A 我們建立了一個BookManager類,它繼承了django.db.models.Manager。這個類只有一個title_count()方法,用來做統計。 
        注意,這個方法使用了self.filter(),此處self指manager本身。


        B 我們把BookManager()賦值給模型的objects屬性。 它將取代模型的預設manager(objects)如果我們
        沒有特別定義,它將會被自動建立。 我們把它命名為objects,這是為了與自動建立的manager保持一致。


        C 為什麼我們要新增一個title_count()方法呢?是為了將經常使用的查詢進行封裝,這樣我們就不必重複編碼了。
        
    2)修改初始Manager QuerySets
        
        manager的基本QuerySet返回系統中的所有物件。 例如,`` Book.objects.all()`` 返回資料庫book中的所有書本。
        我們可以通過覆蓋Manager.get_query_set()方法來重寫manager的基本QuerySet。 get_query_set()按照你的要求返回一個QuerySet。
        例如,下面的模型有兩個manager。一個返回所有對像,另一個只返回作者標題The Django Book的書。
        
        class BookManager(models.Manager):
            def title_count(self, keyword):
                return self.filter(title__icontains=keyword).count()
                
        class DahlBookManager(models.Manager):
            def get_query_set(self):
                return super(DahlBookManager, self).get_query_set().filter(title='The Django Book')
                
        class Book(models.Model):
            title = models.CharField(max_length=100)
            authors = models.ManyToManyField(Author)
            publisher = models.ForeignKey(Publisher)
            publication_date = models.DateField()
            num_pages = models.IntegerField(blank=True, null=True)
            objects = BookManager()
            dahl_objects = DahlBookManager()
            def __unicode__(self):
                return self.title
            
        在這個示例模型中,Book.objects.all()返回了資料庫中的所有書本,而Book.dahl_objects.all()只返回了一本. 
        注意我們明確地將objects設定成manager的例項,因為如果我們不這麼做,那麼唯一可用的manager就將是dah1_objects。
        當然,由於get_query_set()返回的是一個QuerySet物件,所以我們可以使用filter(),exclude()和其他一切
        QuerySet的方法。 像這些語法都是正確的:


        Book.dahl_objects.all()
        Book.dahl_objects.filter(title='The Django Book')
        Book.dahl_objects.count()


        這個例子也指出了其他有趣的技術: 在同一個模型中使用多個manager。
        只要你願意,你可以為你的模型新增多個manager()例項。 
        這是一個為模型新增通用濾器的簡單方法。
        class MaleManager(models.Manager):
            def get_query_set(self):
                return super(MaleManager, self).get_query_set().filter(sex='M')
        class FemaleManager(models.Manager):
            def get_query_set(self):
                return super(FemaleManager, self).get_query_set().filter(sex='F')
        class Person(models.Model):
            first_name = models.CharField(max_length=50)
            last_name = models.CharField(max_length=50)
            sex = models.CharField(max_length=1, choices=(('M', 'Male'), ('F', 'Female')))
            people = models.Manager()
            men = MaleManager()
            women = FemaleManager()
        這個例子允許你執行`` Person.men.all()`` ,`` Person.women.all()`` ,`` Person.people.all()`` 查詢,生成你
        想要的結果。
        
        如果你使用自定義的Manager物件,請注意,Django遇到的第一個Manager(以它在模型中被定義的位置為準)
        會有一個特殊狀態。 Django將會把第一個Manager 定義為預設Manager ,Django的許多部分(但是不包括
        admin應用)將會明確地為模型使用這個manager。
        結論是,你應該小心地選擇你的預設manager。因為覆蓋get_query_set() 了,你可能接受到一個無用的返回對像,你必須避免這種情況。




5 模型方法


    為了給你的對像新增一個行級功能,那就定義一個自定義方法。 
    有鑑於manager經常被用來用一些整表操作(table-wide),模型方法應該只對特殊模型例項起作用。
    這是一項在模型的一個地方集中業務邏輯的技術。
    
    from django.contrib.localflavor.us.models import USStateField
    from django.db import models
    class Person(models.Model):
        first_name = models.CharField(max_length=50)
        last_name = models.CharField(max_length=50)
        birth_date = models.DateField()
        address = models.CharField(max_length=100)
        city = models.CharField(max_length=50)
        state = USStateField() # Yes, this is U.S.‐centric...
        def baby_boomer_status(self):
            "Returns the person's baby‐boomer status."
            import datetime
            if datetime.date(1945, 8, 1) <= self.birth_date <= datetime.date(1964, 12, 31):
                return "Baby boomer"
            if self.birth_date < datetime.date(1945, 8, 1):
            return "Post‐boomer"
        def is_midwestern(self):
            "Returns True if this person is from the Midwest."
            return self.state in ('IL', 'WI', 'MI', 'IN', 'OH', 'IA', 'MO')
        def _get_full_name(self):
            "Returns the person's full name."
            return u'%s %s' % (self.first_name, self.last_name)
        full_name = property(_get_full_name)
        
    例子中的最後一個方法是一個property。 想了解更多關於屬性的資訊請訪
    問http://www.python.org/download/releases/2.2/descrintro/#property
    
    這是用法的例項:
    >>> p = Person.objects.get(first_name='Barack', last_name='Obama')
    >>> p.birth_date
    datetime.date(1961, 8, 4)
    >>> p.baby_boomer_status()
    'Baby boomer'
    >>> p.is_midwestern()
    True
    >>> p.full_name  # Note this isn't a method ‐‐ it's treated as an attribute
    u'Barack Obama'




6 執行原始SQL查詢


    有時候你會發現Django資料庫API帶給你的也只有這麼多,那你可以為你的資料庫寫一些自定義SQL查詢。 
    你可以通過匯入django.db.connection對像來輕鬆實現,它代表當前資料庫連線。 
    要使用它,需要通過connection.cursor()得到一個遊標對像。 然後,使用cursor.execute(sql, [params])來執行SQL語句,使
    用cursor.fetchone()或者cursor.fetchall()來返回記錄集。
  
    connection和cursor幾乎實現了標準Python DB-API,
    你可以訪問` http://www.python.org/peps/pep-0249.html <http://www.python.org/peps/pep-0249.html>`__來獲取更多資訊。 
    如果你對Python DB-API不熟悉,請注意在cursor.execute() 的SQL語句中使用`` “%s”`` ,而不要在SQL內直接新增引數。 如果你使
    用這項技術,資料庫基礎庫將會自動新增引號,同時在必要的情況下轉意你的引數。
    
    不要把你的檢視程式碼和django.db.connection語句混雜在一起,
    把它們放在自定義模型或者自定義manager法中是個不錯的主意。 比如,上面的例子可以被整合成一個自定義manager方法,就像這樣:
    from django.db import connection, models
    def get_first_name(self, last_name):
        cursor = connection.cursor()
        cursor.execute("""
            SELECT DISTINCT first_name
            FROM books_author
            WHERE last_name = %s""", [last_name])
        return [cursor.fetchone()]
        
        
    然後這樣使用:
    >>> Author.objects.get_first_name('Holovaty') 
    [(u'Apress',)]
    
7 與遺留資料庫整合
    Django的資料庫層從Python程式碼生成SQL schemas—但是對於遺留資料庫,你已經擁有SQL schemas. 
    這種情況,你需要為已經存在的資料表建立model. 為此,Django自帶了一個可以通過讀取您的資料表結構來生成
    model的工具. 該輔助工具稱為inspectdb,你可以通過執行manage.py inspectdb來呼叫它.


    使用 inspectdb
    python manage.py inspectdb
    inspectdb工具自省你配置檔案指向的資料庫,針對每一個表生成一個Django模型,然後將這些Python模型的
    程式碼顯示在系統的標準輸出裡面。
    下面是一個從頭開始的針對一個典型的遺留資料庫的整合過程。 兩個前提條件是安裝了Django和一個傳統數
    據庫。


    1 通過執行django-admin.py startproject mysite (這裡 mysite 是你的專案的名字)建立一個Django專案。
    
    2 編輯專案中的配置檔案, mysite/settings.py ,告訴Django你的資料庫連線引數和資料庫名。 
    
    3 通過執行 python mysite/manage.py startapp myapp (這裡 myapp 是你的應用的名字)建立一個Django應
    用。 這裡我們使用myapp 做為應用名。
    
    4 執行命令 python mysite/manage.py inspectdb。這將檢查DATABASE_NAME 資料庫中所有的表並列印出為每
    張表生成的模型類。 看一看輸出結果以瞭解inspectdb能做些什麼。
    
    5 將標準shell的輸出重定向,儲存輸出到你的應用的 models.py 檔案裡:
    python mysite/manage.py inspectdb > mysite/myapp/models.py
    
    6 編輯 mysite/myapp/models.py 檔案以清理生成的 models 並且做一些必要的自定義。 
    
    清理生成的Models
    
        如你可能會預料到的,資料庫自省不是完美的,你需要對產生的模型程式碼做些許清理。 這裡提醒一點關於處理生成 models 的要點:
        
        1) 資料庫的每一個表都會被轉化為一個model類 (也就是說,資料庫的表和model 類之間是一對一的對映)。
        這意味著你需要為多對多連線的表,重構其models 為 ManyToManyField 的物件。


        2)所生成的每一個model中的每個欄位都擁有自己的屬性,包括id主鍵欄位。 但是,請注意,如果某個model
        沒有主鍵的話,那麼Django會自動為其增加一個id主鍵欄位。 這樣一來,你也許希望移除這樣的程式碼行。
        id = models.IntegerField(primary_key=True)
        這樣做並不是僅僅因為這些行是冗餘的,而且如果當你的應用需要向這些表中增加新記錄時,這些行會導
        致某些問題。
        
        3)每一個欄位型別,如CharField、DateField, 是通過查詢資料庫列型別如VARCHAR,DATE來確定的。如果
        inspectdb無法把某個資料庫欄位對映到model欄位上,它會使用TextField欄位進行代替,並且會在所生成
        model欄位後面加入Python註釋“該欄位型別是猜的”。 對這要當心,如果必要的話,更改欄位型別。
        
        4)如果你的資料庫中的某個欄位在Django中找不到合適的對應物,你可以放心的略過它。
        Django模型層不要求必須匯入你資料庫表中的每個列。
        
        5)如果資料庫中某個列的名字是Python的保留字(比如pass、class或者for等),inspectdb會在每個屬性名
        後附加上_field,並將db_column屬性設定為真實的欄位名(也就是pass,class或者for等)。
        例如,某張表中包含一個INT型別的列,其列名為for,那麼所生成的model將會包含如下所示的一個欄位:
        for_field = models.IntegerField(db_column='for')
        inspectdb 會在該欄位後加注 ‘欄位重新命名,因為它是一個Python保留字’ 。
        
        6)如果資料庫中某張表引用了其他表(正如大多數資料庫系統所做的那樣),你需要適當的修改所生成model
        的順序,以使得這種引用能夠正確對映。 
        例如,model Book擁有一個針對於model Author的外來鍵,那麼後
        者應該先於前者被定義。如果你想建立一個指向尚未定義的model的關係,那麼可以使用包含model名的字
        符串,而不是model物件本身。
        
        7)對於PostgreSQL,MySQL和SQLite資料庫系統,inspectdb能夠自動檢測出主鍵關係。 也就是說,它會在合
        適的位置插入primary_key=True。
        而對於其他資料庫系統,你必須為每一個model中至少一個欄位插入這樣的語句,
        因為Django的model要求必須擁有一個primary_key=True的欄位。
        
        8)外來鍵檢測僅對PostgreSQL,還有MySQL表中的某些特定型別生效。
        至於其他資料庫,外來鍵欄位將在假定其為INT列的情況下被自動生成為IntegerField。
        
    

    附錄:利用MYSQL資料庫的實驗結果

Microsoft Windows XP [版本 5.1.2600]
(C) 版權所有 1985-2001 Microsoft Corp.


E:\ChromeDown\depot>E:\ChromeDown\depot>cd E:\CODE_SVN\pysrc


E:\CODE_SVN\pysrc>django-admin.py startproject mysite
E:\CODE_SVN\pysrc\mysite>python manage.py shell
Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.db import connection
>>> cursor = connection.cursor()
>>> exit()


E:\CODE_SVN\pysrc\mysite>python manage.py startapp books
E:\CODE_SVN\pysrc\mysite>python manage.py validate
0 errors found
E:\CODE_SVN\pysrc\mysite>python manage.py sqlall books
BEGIN;
CREATE TABLE `books_publisher` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `name` varchar(30) NOT NULL,
    `address` varchar(50) NOT NULL,
    `city` varchar(60) NOT NULL,
    `state_province` varchar(30) NOT NULL,
    `country` varchar(50) NOT NULL,
    `website` varchar(200) NOT NULL
)
;
CREATE TABLE `books_author` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `first_name` varchar(30) NOT NULL,
    `last_name` varchar(40) NOT NULL,
    `email` varchar(75) NOT NULL
)
;
CREATE TABLE `books_book_authors` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `book_id` integer NOT NULL,
    `author_id` integer NOT NULL,
    UNIQUE (`book_id`, `author_id`)
)
;
ALTER TABLE `books_book_authors` ADD CONSTRAINT `author_id_refs_id_9e7e386` FOREIGN KEY (`author_id`) REFERENCES `books_author` (`id`);
CREATE TABLE `books_book` (
    `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
    `title` varchar(100) NOT NULL,
    `publisher_id` integer NOT NULL,
    `publication_date` date NOT NULL
)
;
ALTER TABLE `books_book` ADD CONSTRAINT `publisher_id_refs_id_3a4d8b45` FOREIGN KEY (`publisher_id`) REFERENCES `books_publisher` (`id`);
ALTER TABLE `books_book_authors` ADD CONSTRAINT `book_id_refs_id_30430d9e` FOREIGN KEY (`book_id`) REFERENCES `books_book` (`id`);
CREATE INDEX `books_book_22dd9c39` ON `books_book` (`publisher_id`);
COMMIT;


E:\CODE_SVN\pysrc\mysite>python manage.py  syncdb
Creating tables ...
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_user_permissions
Creating table auth_user_groups
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table books_publisher
Creating table books_author
Creating table books_book_authors
Creating table books_book


You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): no
Installing custom SQL ...
Installing indexes ...
No fixtures found.






E:\CODE_SVN\pysrc\mysite>python manage.py  shell
Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from books.models import Publisher
>>> p1 = Publisher(name='Apress', address='2855 Telegraph Avenue',city='Berkeley', state_province='CA', country='U.S.A.',website='http://www.apress.com/')
>>> p1.save()
>>> p2 = Publisher(name="O'Reilly", address='10 Fawcett St.',
... city='Cambridge', state_province='MA', country='U.S.A.',
... website='http://www.oreilly.com/')
>>> >>> p2.save()
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
[<Publisher: Publisher object>, <Publisher: Publisher object>]
>>> exit()










E:\CODE_SVN\pysrc\mysite>python manage.py  shell
Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from books.models import Publisher
>>> publisher_list = Publisher.objects.all()
>>> publisher_list
[<Publisher: Apress>, <Publisher: O'Reilly>]


>>> Publisher.objects.filter(country="U.S.A.", state_province="CA")
[<Publisher: Apress>]


>>> Publisher.objects.get(name="Apress")
<Publisher: Apress>


>>> Publisher.objects.order_by("state_province", "address")
[<Publisher: Apress>, <Publisher: O'Reilly>]


>>> Publisher.objects.order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress>]


>>> Publisher.objects.filter(country="U.S.A.").order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress>]


>>> Publisher.objects.order_by('name')[0:2]
[<Publisher: Apress>, <Publisher: O'Reilly>]


>>> Publisher.objects.order_by('name')[0]
<Publisher: Apress>




>>> Publisher.objects.filter(id=4).update(name='Apress Publishing')
1L
>>> Publisher.objects.all().update(country='USA')
2L


>>> Publisher.objects.filter(name='Apress Publishing').delete()
>>> Publisher.objects.all()
[<Publisher: O'Reilly>]












>>> from books.models import Publisher
>>> from books.models import Author
>>> p=Author(first_name='Apress', last_name='Holovaty',email='Apress@sina.com')
>>> p.save()
>>> p.id
1L
>>> p1 = Author(first_name='Jacob', last_name='Kaplan-Moss',email='Jacob@sina.com')
>>> p1.save()
>>> p1.id
2L
>>> Author.objects.all()
[<Author: Apress Holovaty>, <Author: Jacob Kaplan-Moss>]


>>> from books.models import Book
>>> p1=Publisher.objects.get(id=2) 
>>> a1=Author.objects.get(first_name='Apress')  
>>> b1=Book(title='The Django Book',publisher=p1,publication_date='2010-5-5')  
>>> b1.save()  
>>> b1.authors.add(a1)  
>>> b1.save()


>>> p1=Publisher.objects.get(id=2) 
>>> a1=Author.objects.get(first_name='Jacob')   
>>> b1=Book(title='Dive into Python',publisher=p1,publication_date='2010-5-5')  
>>> b1.save()  
>>> b1.authors.add(a1)  
>>> b1.save()  


>>> b = Book.objects.get(id=1)
>>> b.publisher
<Publisher: O'Reilly>


>>> p = Publisher.objects.get(name='O\'Reilly')
>>> p.book_set.all()
[<Book: The Django Book>, <Book: Dive into Python>]


>>> b = Book.objects.get(id=1)
>>> b.authors.all()
[<Author: Apress Holovaty>]


>>> b.authors.filter(first_name='Adrian')
[]
>>> b.authors.filter(first_name='Apress')
[<Author: Apress Holovaty>]


>>> a = Author.objects.get(first_name='Apress', last_name='Holovaty')
>>> a.book_set.all()
[<Book: The Django Book>]
>>> exit()






E:\CODE_SVN\pysrc\mysite>python manage.py shell
Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from books.models import Author
>>> Author.objects.get_first_name('Apress') 
[None]
>>> Author.objects.get_first_name('Holovaty') 
[(u'Apress',)]
>>> 

    
    
    

相關文章