Django book2 模型 學習筆記

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='[email protected]')
>>> p.save()
>>> p.id
1L
>>> p1 = Author(first_name='Jacob', last_name='Kaplan-Moss',email='[email protected]')
>>> 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',)]
>>> 

    
    
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章