理解django的多對多ManyToManyField


轉自:http://luozhaoyu.iteye.com/blog/1510635

對於第一次碰到django這樣類activerecord的ORM,初學者可能比較疑惑的是ManyToManyField這個字段。老鳥可以繞開,這里拿djangobook沒有說明的地方來仔細解釋下。 
Python代碼   收藏代碼
  1. from django.db import models  
  2.   
  3. class Publisher(models.Model):  
  4.     name = models.CharField(max_length=30)  
  5.     address = models.CharField(max_length=50)  
  6.     city = models.CharField(max_length=60)  
  7.     state_province = models.CharField(max_length=30)  
  8.     country = models.CharField(max_length=50)  
  9.     website = models.URLField()  
  10.   
  11. class Author(models.Model):  
  12.     first_name = models.CharField(max_length=30)  
  13.     last_name = models.CharField(max_length=40)  
  14.     email = models.EmailField()  
  15.   
  16. class Book(models.Model):  
  17.     title = models.CharField(max_length=100)  
  18.     authors = models.ManyToManyField(Author)  
  19.     publisher = models.ForeignKey(Publisher)  
  20.     publication_date = models.DateField()  

有出版商,作者,和書。一本書有多個作者,只有一個出版商。 
作者和出版商好理解,各一個表就是了。書應該作為幾個表呢?1個和2個都可以。如果你主要是以出版商和作者為對象操作,可以把書看成一個紐帶而已,書這個表里存放着出版商和作者的關系。又因為一行存不下所有作者的id(假設沒有壓縮),所以book表里面會有很多book會重復。所以book表的名字改成author_publisher搞不好還更妥當。 
如果你要認真的把書也看成一個表(不想看到重復的書名),那么就需要把書和作者的關系又單獨提出來。這里是個多對多的關系所以用ManyToManyField,如果一對多呢?就用ForeignKey。 
我們用
Java代碼   收藏代碼
  1. python manage.py sql books  
查看生成的表結構 
Sql代碼   收藏代碼
  1. BEGIN;  
  2. CREATE TABLE "books_publisher" (  
  3.     "id" serial NOT NULL PRIMARY KEY,  
  4.     "name" varchar(30) NOT NULL,  
  5.     "address" varchar(50) NOT NULL,  
  6.     "city" varchar(60) NOT NULL,  
  7.     "state_province" varchar(30) NOT NULL,  
  8.     "country" varchar(50) NOT NULL,  
  9.     "website" varchar(200) NOT NULL  
  10. )  
  11. ;  
  12. CREATE TABLE "books_author" (  
  13.     "id" serial NOT NULL PRIMARY KEY,  
  14.     "first_name" varchar(30) NOT NULL,  
  15.     "last_name" varchar(40) NOT NULL,  
  16.     "email" varchar(75) NOT NULL  
  17. )  
  18. ;  
  19. CREATE TABLE "books_book" (  
  20.     "id" serial NOT NULL PRIMARY KEY,  
  21.     "title" varchar(100) NOT NULL,  
  22.     "publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id") DEFERRABLE INITIALLY DEFERRED,  
  23.     "publication_date" date NOT NULL  
  24. )  
  25. ;  
  26. CREATE TABLE "books_book_authors" (  
  27.     "id" serial NOT NULL PRIMARY KEY,  
  28.     "book_id" integer NOT NULL REFERENCES "books_book" ("id") DEFERRABLE INITIALLY DEFERRED,  
  29.     "author_id" integer NOT NULL REFERENCES "books_author" ("id") DEFERRABLE INITIALLY DEFERRED,  
  30.     UNIQUE ("book_id", "author_id")  
  31. )  
  32. ;  
  33. CREATE INDEX "books_book_publisher_id" ON "books_book" ("publisher_id");  
  34. COMMIT;  

結果確實是生成了四個表。 
其中book_authors表是關聯表,不能直接插入數據,實際上也不存在叫做BookAuthors的對象。所以要插入這里面數據,建立起book和author的聯系時,必須取出book實例,並給book賦值 
Python代碼   收藏代碼
  1. #首先是創建一個book,book創建之后才能添加聯系表,這是顯然的  
  2. book = Book()  
  3. book.save()  
  4. #添加三個作者,傳如一個列表  
  5. book.authors = Author.objects.all()[0:3]  
  6. #或者添加一個作者,傳入一個實例  
  7. book.authors.add(Author.objects.all()[0])  
  8. #最后是save  
  9. book.save()  

那么,使用ManyToManyField的好處是不是就是省去了創建一個簡單聯系表(如果不滿足於這么簡單的表,也可一通過through參數來指明存在的表)?使用它我們還可以做到通過把一張表中某鍵值在另一張表中全部映射的對象找出來。比如把某書的所有作者,或者某作者的所有書找出來。 
Python代碼   收藏代碼
  1. book.authors.all()  
  2. author.book_set.all()  

可是如果用最土的三張表的方法:一個publisher,一個author,一個publisher_author,在PublisherAuthor模型不指定ManyToManyField而只用ForeignKey也可以這么方便么?(找出映射的對象) 
猜想是可以的……可以查出publisher或author對應的PublisherAuthor對象……就相當與只執行了 
Sql代碼   收藏代碼
  1. select publisher_author.* from publisher_author, author where publisher_author.author_id = author.id  

我們還得拿着這些author的id才能找出真正的這些author。 
而之前的ManyToManyField做了什么呢? 
Sql代碼   收藏代碼
  1. select * from publisher where publisher.id in (select publisher_id from publisher_author, author where publisher_author.author_id = author.id)   

嗯……多對多關系只是幫我們多做了一步嵌套子查詢,並包裝成publisher集而已,更方便,但未必更高效。 

注:以上SQL是偽的,未經過查驗,根據結果反推,ORM至少是做了這些工作的,只會更多,不會更少。如果有機會我會再查查它到底執行了什么,如果知道結果的朋友也請告訴我吧XD


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM