Django數據表關係映射

在關係型數據庫中,通常不會把所有數據都放在同一張表中,這樣做會額外佔用內存空間,
在關係列數據庫中通常用表關聯來解決數據庫。
常用的表關聯方式有三種:

  • 一對一映射
    如: 一個身份證對應一個人
  • 一對多映射
    如: 一個班級可以有多個學生
  • 多對多映射
    如: 一個學生可以報多個課程,一個課程可以有多個學生學習

一對一映射

  • 語法OneToOneField

    class A(model.Model):
        ...
    
    class B(model.Model):
        屬性 = models.OneToOneField(A)
    
  • 用法示例
    創建男人和妻子類

    # file : xxxxxxxx/models.py
    from django.db import models
       
    class Man(models.Model):
        '''男人模型類'''
        name = models.CharField('姓名', max_length=50)
    
    class Wife(models.Model):
        '''妻子模型類'''
        name = models.CharField("姓名", max_length=50)
        husband = models. (Man)  # 增加一對一屬性
    
  • 創始一對一的數據記錄

    from . import models
    man1 = models.Man.objects.create(name='王先生')
    wife1 = models.Wife.objects.create(name='王夫人', husband=man1)  # 關聯王老師
    man2 = models.Man.objects.create(name='隔壁老王')  # 一對一可以沒有數據對應的數據 
    
  • 一對一數據的相互獲取
    Wife 對象中,通過 husband 屬性找到對應的Man對象
    Author對象中,通過wife 屬性找到對應的Wife對象

  • 正向查詢
    直接通過關聯屬性查詢即可

    # 通過 wife 找 Man
    from . import models
    wife = models.Wife.objects.get(name='王夫人')
    print(wife.name, '的老公是', wife.husband.name)
    
  • 反向查詢
    通過反向關聯屬性查詢
    反向關聯屬性爲實例對象.引用類名(小寫),如男人的反向引用爲男人對象.wife
    當反向引用不存在時,則會觸發異常

    # 通過 man.wife 關聯屬性 找 wife,如果沒有對應的wife則觸發異常
    man1 = models.Man.objects.get(name='王先生')
    print(man1.name, '的妻子是', man1.wife.name)
    man2 = models.Man.objects.get(name='隔壁老王')
    try:
        print(man2.name, '的妻子是', man2.wife.name)
    except:
        print(man2.name, '還沒有妻子')
    
  • 作用:
    主要是解決常用數據與不常用數據的存儲問題,
    把經常加載的一個數據放在主表中,不常用數據放在另一個副表中。
    這樣在訪問主表數據時不需要加載副表中的數據以提高訪問速度提高效率和節省內存空間,
    如經常把書的內容和書名建成兩張表,因爲在網站上經常訪問書名等信息,但不需要得到書的內容。

一對多映射

  • 用法語法ForeignKey
    當一個A類對象可以關聯多個B類對象時

    class A(model.Model):
        ...
    
    class B(model.Model):
        a = models.ForeignKey(A)
    
  • 外鍵類ForeignKey
    構造函數:ForeignKey(to, on_delete, **options)
    參數

    • to 關聯的主類
    • on_delete
      models.CASCADE 級聯刪除。 Django模擬SQL約束ON DELETE CASCADE的行爲,並刪除包含ForeignKey的對象。
      models.PROTECT 拋出ProtectedError 以阻止被引用對象的刪除;
      SET_NULL 設置ForeignKey值爲null;需要指定該列null=True
      SET_DEFAULTForeignKey設置爲其默認值;必須設置ForeignKey的默認值
      其它參請參考文檔 https://docs.djangoproject.com/en/1.11/ref/models/fields/#foreignkey
    • **options 可以是常用的字段選項如:nullunique
  • 示例
    有二個出版社對應五本書的情況.
    清華大學出版社 有書(C++,Java,Python)
    北京大學出版社 有書(西遊記,水滸)

    • 定義一對多類

      # file: one2many/models.py
      from django.db import models
      class Publisher(models.Model):
          '''出版社'''
          name = models.CharField('名稱', max_length=50, unique=True)
      
      class Book(models.Model):
          title = models.CharField('書名', max_length=50)
          publisher = models.ForeignKey(Publisher, null=True)
      
      
    • 創建一對多的對象

      # file: xxxxx/views.py
      from . import models
      pub1 = models.Publisher.objects.create(name='清華大學出版社')
      models.Book.objects.creat e(title='C++', publisher=pub1)
      models.Book.objects.create(title='Java', publisher=pub1)
      models.Book.objects.create(title='Python', publisher=pub1) 
      
      pub2 = models.Publisher.objects.create(name='北京大學出版社')
      models.Book.objects.create(title='西遊記', publisher=pub2)
      models.Book.objects.create(title='水滸', publisher=pub2)
      
    • 查詢:
      通過多查一通過 publisher 屬性查詢即可

      # 通過一本書找到對應的出版社
      abook = models.Book.objects.get(id=1)
      print(abook.title, '的出版社是:', abook.publisher.name)
      

      通過一查多通過book_set(),管理器對象

      # 通過出版社查詢對應的書
      pub1 = models.Publisher.objects.get(name='清華大學出版社')
      books = pub1.book_set.all()  # 通過book_set 獲取pub1對應的多個Book數據對象
      # books = models.Book.objects.filter(publisher=pub1)  # 也可以採用此方式獲取
      print("清華大學出版社的書有:")
      for book in books:
          print(book.title)
      

多對多映射

多對多表達對象之間多對多複雜關係,如: 每個人都有不同的學校(小學,初中,高中,…),每個學校都有不同的學生…

  • 語法ManyToManyField

  • 示例
    一個作者可以出版多本圖書
    一本圖書可以被多名作者同時編寫

    class Author(models.Model):
        ...
    
    class Book(models.Model):
        ...
        authors = models.ManyToManyField(Author)
    
  • 數據查詢
    通過 Book 查詢對應的所有的 Authors

    book.authors.all() -> 獲取 book 對應的所有的author的信息
    book.authors.filter(age__gt=80) -> 獲取book對應的作者中年齡大於80歲的作者的信息
    

    通過 Author 查詢對應的所有的Books
    Django會生成一個關聯屬性 book_set 用於表示對對應的book的查詢對象相關操作

    author.book_set.all()
    author.book_set.filter()
    author.book_set.create(...)  # 創建新書並聯作用author
    author.book_set.add(book)   # 添加已有的書爲當前作者author
    author.book_set.clear()  # 刪除author所有並聯的書
    
  • 示例:
    多對多模型

    class Author(models.Model):
        '''作家模型類'''
        name = models.CharField('作家', max_length=50)
        def __str__(self):
            return self.name
    class Book(models.Model):
        title = models.CharField('書名', max_length=50)
        author = models.ManyToManyField(Author)
        def __str__(self):
            return self.title
    

    多對多視圖操作

    from django.http import HttpResponse
    
    from . import models
    
    def many2many_init(request):
        # 創建兩人個作者
        author1 = models.Author.objects.create(name='呂澤')
        author2 = models.Author.objects.create(name='王老師')
    
        # 呂擇和王老師同時寫了一本Python
        book11 = author1.book_set.create(title="Python")
        author2.book_set.add(book11)  #
    
        # 王老師還寫了兩本書
        book21 = author2.book_set.create(title="C")  # 創建一本新書"C"  
        book22 = author2.book_set.create(title="C++")  # 創建一本新書"C++"
    
        return HttpResponse("初始化成功")
    
    def show_many2many(request):
        authors = models.Author.objects.all()
        for auth in authors:
            print("作者:", auth.name, '發出版了', auth.book_set.count(), '本書: ')
            for book in books:
                print('    ', book.title)
        print("----顯示書和作者的關係----")
        books = models.Book.objects.all()
        for book in books:
            auths = book.author.all()
            print(book.title, '的作者是:', '、'.join([str(x.name) for x in auths]))
        return HttpResponse("顯示成功,請查看服務器端控制檯終端")
    

實際開發中,對於多對多的關係,在數據庫設計層面,一般都會使用中間表來處理。
中間表中以外鍵的方式(一對多)關聯兩個多方。
同樣的,在Django中也可以使用中間模型類來處理。

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