DJango 多表操作:一對一、一對多、多對多的增刪改,基於對象/雙下劃線的跨表查詢

掌握DJango model數據表相關操作

分析思路,創建數據表

對於表操作,表之間的關聯關係,必須理解他們之間的關係,對於編程很重要。可以看看映射關係、外鍵和relationship查詢 ,至少明白外鍵相關基本知識。

(一)多表查詢
    一對一:models.OneToOneField(to_field='id',to='Authordatil')
    一對多:(外鍵設置唯一性)
        models.ForeignKey(to='Publish',to_field='id')

    多對多:自動生成第三張表
        models.ManyToManyField(to='Author')


(二)創建表模型
     作者與詳情  ( 一對一 )
     書與作者( 多對多 )
     出版社與書(一對多 )

     出版社:id name  add  email
     書: id  name  price  publish

     作者: id  name  sex  authordatil
     詳情: id  photo  address

models.py 文件,創建數據模型

from django.db import models
class Publish(models.Model):
    id=models.AutoField(primary_key=True)
    name=models.CharField(max_length=100)
    address=models.CharField(max_length=64)
    email=models.EmailField()
    def __str__(self):
        return '%s,%s,%s'%(self.name,self.address,self.email)

class Author(models.Model):
    id=models.AutoField(primary_key=True)
    name=models.CharField(max_length=32)
    choices=((0,'女'),(1,'男'))
    sex=models.SmallIntegerField(choices,default=0) #數字整型
    #作者與詳情:(一對一)   to='AuthorDetail'  加引號,這個表能找到就可以,不用引號,類必須在上面定義
    authordatil=models.OneToOneField(to_field='id',to='Authordatil',on_delete=models.CASCADE)
    def __str__(self):
        return self.name

class Authordatil(models.Model):
    id=models.AutoField(primary_key=True)
    photo=models.CharField(max_length=124)
    address=models.CharField(max_length=64)

class Book(models.Model):
    id=models.AutoField(primary_key=True)
    name=models.CharField(max_length=32)
    price=models.DecimalField(max_digits=5,decimal_places=2)
    #出版社與書(一對多關係)外檢設置唯一性,關聯的表,關聯的字段
    publish=models.ForeignKey(to='Publish',to_field='id',on_delete=models.CASCADE)
    #書與作者關係是多對多關係
    authors=models.ManyToManyField(to='Author')
  • id 字段是自動添加的

  • 對於外鍵字段,Django 會在字段名上添加"_id" 來創建數據庫中的列名

  • 外鍵字段 ForeignKey 有一個 null=True 的設置(它允許外鍵接受空值 NULL),你可以賦給它空值 None 。

標題添加/刪除/修改表記錄

1、一對多添加記錄

  • 通過 _id 來添加記錄

直接添加Book,views中定義一個方法,簡單測試:

    models.Book.objects.create(name='紅樓夢', price=20.0, publish_id=1)
    return HttpResponse('成功插入一條Book記錄!')

看看控制檯,會報錯了
在這裏插入圖片描述
'Cannot add or update a child row: a foreign key constraint fails 初步判定主鍵約束錯誤,也就是說publish_id 對應的Publish不存在。那就先添加幾個Publish吧
views中定義一個方法:

def addPublish(request):
    models.Publish.objects.create(name='北京出版社', address='北京', email='[email protected]')
    models.Publish.objects.create(name='安徽出版社', address='安徽', email='[email protected]')
    models.Publish.objects.create(name='人教版出版社', address='北京', email='[email protected]')
    li=[]
    for i in range(5):
        li.append(models.Publish(name='新華出版社%s' % i, address='北京', email='[email protected]'))  # 批量生成對象
    models.Publish.objects.bulk_create(li)
    return HttpResponse('成功插入Publish記錄!')

查看數據庫信息:
在這裏插入圖片描述
出版社Publish數據有了,現在繼續測試添加Book功能了。

def addBookById(request):
    models.Book.objects.create(name='紅樓夢', price=20.0, publish_id=101)
    return HttpResponse('成功插入一條Book記錄!')

查看數據庫數據,可以看到數據已經成功插入了。
在這裏插入圖片描述

簡單理解剛纔的問題:主鍵約束了之後,插入book前提是已經有了外鍵的數據,publish_id,也就是有一個Publish記錄,這樣纔可以插入Book記錄,且publish_id正好是Publish記錄中對應的一條記錄。

  • 通過對象添加記錄
def addBookByPublish(request):
    publish = models.Publish.objects.filter(pk=101).first()
    models.Book.objects.create(name='紅樓夢', price=34.5, publish=publish)
    return HttpResponse('通過對象插入一條記錄!')

首先得到一個publish對象,然後作爲參數直接插入到Book表中,看看數據庫信息。

在這裏插入圖片描述

# pk 表示主鍵,一旦主鍵改變,還是會找到對應的主鍵來獲取對象,如果通過主鍵查詢道德數據爲null,那麼表示不存在這個記錄,也就無法正常插入Book了。

2、一對多刪除記錄

def delBook(request):
    models.Book.objects.filter(name='紅樓夢').first().delete()
    return HttpResponse('刪除記錄!')

一對多修改記錄

  • 通過queryset對象的update方法修改

  • 通過對象的屬性修改,調用對象的save()方法保存

  • 通過queryset對象的update方法修改

    方式一:通過 _id 修改
    
def upDateBook(request):
    models.Book.objects.filter(pk=4).update(name='西遊記', publish_id=102)
    return HttpResponse('通過ID修改!')
    

修改前數據表Book信息
在這裏插入圖片描述
修改後信息如下
在這裏插入圖片描述
方式二:通過 對象修改修改

def upDateBookByPublish(request):
    publish = models.Publish.objects.filter(pk=103).first()
    models.Book.objects.filter(pk=4).update(name='東遊記',price=100.0, publish=publish)
    return HttpResponse('通過對象修改!')

數據庫當前數據
在這裏插入圖片描述

  • 通過對象的屬性修改,調用對象的save()方法保存

方式一:通過 _id 修改

def upDateBookByIDBook(request):
    book = models.Book.objects.filter(pk=4).first()
    book.publish_id = 104
    book.price = 200.0
    book.save()
    return HttpResponse('upDateBookByIDBook!')

在這裏插入圖片描述
方式二:通過對象修改

def upDateBookBook(request):
    book = models.Book.objects.filter(pk=4).first()
    publish = models.Publish.objects.filter(pk=105).first()
    book.publish = publish
    book.save()
    return HttpResponse('upDateBookBook!')

在這裏插入圖片描述

3、多對多增刪改記錄

  • 多對多增加記錄

  • 多對多刪除記錄

  • 多對多清空記錄

  • 多對多修改記錄

  • 多對多增加記錄
    先準備測試用的數據

(1)通過對象添加記錄

def addAuthorDetail(request):
    models.Authordatil.objects.create(photo='www.baidu.com/author1.png', address='天上人間1包間')
    models.Authordatil.objects.create(photo='www.baidu.com/author2.png', address='天上人間2包間')
    models.Authordatil.objects.create(photo='www.baidu.com/author3.png', address='天上人間3包間')
    models.Authordatil.objects.create(photo='www.baidu.com/author4.png', address='天上人間4包間')
    return HttpResponse('添加AuthorDetail')

def addAuthor(request):
    models.Author.objects.create(name='tom', sex=0,authordatil_id=1)
    models.Author.objects.create(name='tony', sex=1,authordatil_id=2)
    models.Author.objects.create(name='jack', sex=1,authordatil_id=3)
    models.Author.objects.create(name='ant', sex=0,authordatil_id=4)
    return HttpResponse('添加addAuthor')

在這裏插入圖片描述
在這裏插入圖片描述
之前庫中有一本書東遊記
在這裏插入圖片描述

現在就要爲這邊《東遊記》這本書添加作者


def addAuthorByBook(request):
    # 爲紅樓夢這本書添加一個作者
    tom = models.Author.objects.filter(name='tom').first()
    book = models.Book.objects.filter(name='東遊記').first()
    tony = models.Author.objects.filter(name='tony').first()
    jack = models.Author.objects.filter(name='jack').first()
    book.authors.add(tom)
    # 一次添加兩個作者,中間用逗號隔開
    book.authors.add(tony, jack)
    return HttpResponse('給書添加作者')

看數據庫數據表: bookapp_book_authors
在這裏插入圖片描述
(2)通過 id 添加記錄

直接上代碼

def addAuthorByBookById(request):
    # 爲紅樓夢這本書添加一個作者
    book = models.Book.objects.filter(name='東遊記').first()
    # 一次添加兩個作者,中間用逗號隔開
    book.authors.add(4)
    return HttpResponse('addAuthorByBookById')

看看數據庫表
在這裏插入圖片描述
也就是說,得到了book之後,直接通過作者的id作爲參數,找到了作者,然後直接添加,內部django幫助封裝了一層其實。

  • 多對多刪除記錄
remove()    # 可以傳對象,可以傳id,可以傳多個

(1)通過對象刪除記錄

def delBookOfAuthor(request):
    # 刪除東遊記作者名字是tom的作者
    tom = models.Author.objects.filter(name='tom').first()
    book = models.Book.objects.filter(name='東遊記').first()
    book.authors.remove(tom)
    return HttpResponse('delBookOfAuthor')

在這裏插入圖片描述
可以看到,bookapp_book_authors表中的tom記錄已經刪掉了

(2)通過id刪除記錄

def delBookOfAuthorByID(request):
    # 刪除東遊記作者id是3、4的兩個作者
    book = models.Book.objects.filter(name='東遊記').first()
    book.authors.remove(3,4)
    return HttpResponse('delBookOfAuthorByID')

在這裏插入圖片描述

  • 多對多清空記錄
clear()    # 沒有參數

    # 刪除東遊記作者id是3、4的兩個作者
    book = models.Book.objects.filter(name='東遊記').first()
    book.authors.clear()
    return HttpResponse('delBookOfAllAuthor')

在這裏插入圖片描述

  • 多對多修改記錄
set()    # 先清空在增加,必須傳列表,列表裏面可以是對象,可以是id

(1)通過對象修改記錄

def updateBookAuthor(request):
    tom = models.Author.objects.filter(name='tom').first()
    book = models.Book.objects.filter(name='東遊記').first()
    book.authors.set([tom, ])
    return HttpResponse('updateBookAuthor')

在這裏插入圖片描述

(2)通過id修改記錄

def updateBookAuthorID(request):
    # 修改東遊記作者爲作者id是3、4的作者
    book = models.Book.objects.filter(name='東遊記').first()
    book.authors.set([3,4 ])
    return HttpResponse('updateBookAuthorID')

在這裏插入圖片描述

四、基於對象的跨表查詢–多次查詢、子查詢

正向查詢和反向查詢

正向查詢:關聯字段在從表,由從表查詢主表中的數據    -----> 按字段查詢
反向查詢:關聯字段在從表,由主表查詢從表中的數據    -----> 按表名小寫查詢

1、一對一基於對象的跨表查詢

正向:正向查詢按 字段
反向:反向查詢按 表名小寫
def findBookAndPublish(request):
    # 1.正向 查詢東遊記這本書的出版社郵箱
    book = Book.objects.filter(name='東遊記').first()
    # book.publish  就是出版社對象
    pulish = book.publish
    print(pulish.email)
    # 2.反向  查詢地址是北京的出版社出版的圖書
    publish = Publish.objects.filter(address='北京').first()
    # publish.book_set.all()  拿出所有的圖書
    books = publish.book_set.all()
    # 統計一下條數
    books = publish.book_set.all().count()
    print(books)
    return HttpResponse('findBookAndPublish')

結果:

itjavawfc@163.com
0

2、多對多基於對象的跨表查詢

正向:正向查詢按 字段.all()
反向:反向按 表名小寫_set.all()```

```python
def findBookAndPublishMoreToMore(request):
    # 1.查詢紅樓夢這本書所有的作者
    book = Book.objects.filter(name='東遊記').first()
    book.authors.all()  # 是所有的作者,是一個queryset對象,可以繼續點
    print(book.authors.all())
    # 2.查詢lqz寫的所有書
    lqz = Author.objects.filter(name='jack').first()
    books = lqz.book_set.all()
    print(books)
    return HttpResponse('findBookAndPublishMoreToMore')
<QuerySet [<Author: jack>, <Author: ant>]>
<QuerySet [<Book: Book object (4)>]>

五、基於雙下劃線的跨表查詢

1、一對一的基於雙下劃線的跨表查詢

正向:按 字段,跨表可以在filter,也可以在values中
反向:按 表名小寫,跨表可以在filter,也可以在values中
def findBookTyp1(request):
    # 1.查詢tom作者的照片   正向查詢  跨表的話,按字段
    # 以author表作爲基表
    ret = Author.objects.filter(name='tom').values('authordatil__photo')
    print(ret)

    # 2.以authordetail作爲基表 反向查詢,按表名小寫  跨表的話,用表名小寫
    ret = Authordatil.objects.filter(author__name='tom').values('photo')
    print(ret)
    return HttpResponse('findBookTyp1')

打印結果:

<QuerySet [{'authordatil__photo': 'www.baidu.com/author1.png'}]>
<QuerySet [{'photo': 'www.baidu.com/author1.png'}]>

2、多對多的基於雙下劃線的跨表查詢

正向:按 字段,跨表可以在filter,也可以在values中
反向:按 表名小寫,跨表可以在filter,也可以在values中

def findBookTyp2(request):
    # 查詢東遊記的所有作者名字
    # 1.以Book爲基表
    ret = Book.objects.filter(name='東遊記').values('authors__name')
    print(ret)

    # 2.以Author爲基表
    ret = Author.objects.filter(book__name='東遊記').values('name')
    print(ret)
    return HttpResponse('findBookTyp2')

<QuerySet [{'authors__name': 'jack'}, {'authors__name': 'ant'}]>
<QuerySet [{'name': 'jack'}, {'name': 'ant'}]>

3、連續跨表查詢
一直用雙下劃線 __ 獲取字段

def findBookTyp3(request):
    # 查詢東遊記這本書所有的作者的照片
    book = Book.objects.filter(name='東遊記').first()
    authors = book.authors.all()
    for author in authors:
        authordatil = author.authordatil
        print(authordatil.photo)

    # 方法二,使用連續跨表
    ret = Book.objects.filter(name='東遊記').values('authors__authordatil__photo')
    print(ret)
    return HttpResponse('findBookTyp3')

結果:

www.baidu.com/author3.png
www.baidu.com/author4.png
<QuerySet [{'authors__authordatil__photo': 'www.baidu.com/author3.png'}, {'authors__authordatil__photo': 'www.baidu.com/author4.png'}]>

當前文章用到的部分Code

發佈了354 篇原創文章 · 獲贊 119 · 訪問量 69萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章