1. 程式人生 > >django 模型層(2)

django 模型層(2)

Django 模型層(2)

多表操作---模型之間的關係 

1 一對一:作者----作者詳細資訊

2 一對多:書籍----出版社

3 多對多:書籍----作者

 

一  建立模型(主鍵(id)自動建立)

沒有任何關係的一張表的建立

class Emp(models.Model):
    name=models.CharField(max_length=32)
    age=models.IntegerField()
    salary=models.DecimalField(max_digits=8,decimal_places=2)
    dep
=models.CharField(max_length=32) province=models.CharField(max_length=32)
單獨一張表對應的模型類

1 一對一 OneToOneField

models.OneToOneField(to='AuthorDetail',null=True,on_delete=models.CASCADE)
引數to:表示與AuthorDetail繫結一對一關係
引數 null=True:表示允許為空;
引數on_delete:級聯刪除,表示當一對一關係的一方資料被刪除了,其對應關係也刪除了
class Author(models.Model):
        
''' ...其他欄位省去 ''' # 作者與authordetail建立一對一關係 authordetail=models.OneToOneField(to='AuthorDetail',null=True,on_delete=models.CASCADE)

 

2一對多 ForeignKey (在多的一方(Book)繫結)  

 基本書寫格式:
   ForeignKey(to='被關聯的表名’,null=True,on_delete=models.CASCADE)
class
Book(models.Model): ''' 。。。 ''' # 建立一對多的關係:一個出版社可以對應多本書 publish = models.ForeignKey(to='Publish', null=True, on_delete=models.CASCADE)

 

3 多對多 ManyToMany

   注意:

    1 自動建立關係表book_author;

 authors = models.ManyToManyField(to='Author')
class Book(models.Model):
    '''
      ...
    '''

    # 書籍與作者建立多對多的關係
    authors = models.ManyToManyField(to='Author')

 

from django.db import models

# Create your models here.

# 關係戶:書籍------作者  多對多
#        書籍------出版社  一對多
#        作者------作者詳細資訊  一對一


class Book(models.Model):
    '''
        建立書籍表
    '''
    title = models.CharField(max_length=32)
    pub_date = models.DateField()
    price = models.DecimalField(max_digits=5, decimal_places=2)

    # 建立一對多的關係:一個出版社可以對應多本書
    publish = models.ForeignKey(to='Publish', null=True, on_delete=models.CASCADE)

    # 書籍與作者建立多對多的關係
    authors = models.ManyToManyField(to='Author')
    def __str__(self):
        return self.title


class Publish(models.Model):
    '''
       創建出版社表
       '''
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()
    def __str__(self):
        return self.name

class Author(models.Model):
    '''
    建立作者表
    '''
    name=models.CharField(max_length=32)
    age=models.IntegerField()

    # 作者與authordetail建立一對一關係
    authordetail=models.OneToOneField(to='AuthorDetail',null=True,on_delete=models.CASCADE)
    def __str__(self):
        return self.name

class AuthorDetail(models.Model):
    '''
    建立作者詳細資訊表
    '''
    birthday=models.DateField()
    telephone=models.BigIntegerField()
    addr=models.CharField(max_length=64)
    def __str__(self):
        return self.addr
建立表模型類的完整程式碼

4 資料遷移

方法一

python manage.py makemigrations

python manage.py migrate
資料遷移程式碼

方法二

pycharm 中快捷啟動方式

 

 5 建立的表結果

二 新增記錄

單表新增資料的兩種基本方式

models.AuthorDetail(id=1,birthday="1995-12-12",telephone=110,addr="北京")
方式一(類的是例項化)
obj=models.AuthorDetail.objects.create(birthday="1995-12-12",telephone=110,addr="北京")  
方式二(模型類objects管理器下的create方法)

1 一對一

 

 # 新增一對一關係記錄方式一
    models.Author.objects.create(name='alex',age='8',authordetail_id=1)
    

 # 新增一對一關係記錄方式二
wusir_d = models.AuthorDetail.objects.create(birthday="1999-8-12", telephone=666, addr="上海")
wusir=models.Author.objects.create(name='wusir',age=18,authordetail=wusir_d) #接收記錄物件

2 一對多

 

# 繫結方式一 
python=models.Book.objects.create(title='python',pub_date='2015-10-8',price=120,publish_id=1)
# 繫結方式二 
strawberry=models.Publish.objects.create(name='草莓出版社',city='武漢',email='[email protected]')
go=models.Book.objects.create(title='go',pub_date='2014-12-9',price=150,publish=strawberry)

 3 多對多(如何給關係表新增資料)

正向:找關聯屬性 

 # 正向繫結用欄位  書籍找關係表通過Author屬性
   python=models.Book.objects.filter(title='python').first()
    go=models.Book.objects.filter(title='go').first()

    alex=models.Author.objects.filter(name='alex').first()
    egon=models.Author.objects.filter(name='egon').first()
    print(python,alex,egon)
    print(python.publish.city)
給python書籍繫結兩個作者資訊

 反向:表名小寫

    linux = models.Book.objects.filter(title="linux").first()
    R = models.Book.objects.filter(title="R").first()
    wusir = models.Author.objects.filter(name="wusir").first()

    wusir.book_set.add(linux,R)
給egon繫結兩本書

給關聯表新增記錄的方法

#以python書籍物件為例
python.authors.remove(alex)#移除作者資訊 alex

python.authors.clear()  #清空作者資訊

python.authors.add(作者物件1,作者物件2) #新增作者資訊

python.authors.add(1,2) #新增作者資訊

python.authors.set([ 1,]) #先清空後設置值
關聯表的方法

 

二 基於物件的跨表查詢 ********

方法總結
                正向查詢:按照關係欄位 
       物件1  -----------------------------------------> 物件2
             <----------------------------------------
                    反向查詢:按照表名小寫_set.all()
                    是否需要_set取決於結果的個數,
                    多個結果加_set.all()            

 1 一對一

 

 # 正向製作者--->作者詳細資訊
    #alex---->電話號碼
    # alex=models.Author.objects.filter(name='alex').first()
    # result=alex.authordetail.telephone
正向查詢
    # 反向查詢作者<----作者詳細資訊
    #tel=110的作者是誰
    # ad=models.AuthorDetail.objects.filter(telephone=110).first()
    # result=ad.author.name
反向查詢

2 一對多

 # 查詢linux這本書籍的出版社資訊地址(正向查詢)
    linux=models.Book.objects.filter(title='linux').first()
    result=linux.publish.city
正向查詢
    # 反向查詢(出版社--->書籍)
    # 查詢橘子出版社出版過的所有書籍
    publish=models.Publish.objects.filter(name='橘子出版社').first()
    print(publish)
    result=publish.book_set.all()
    print(result) #<QuerySet [<Book: java>]>
    for  i  in result:
        print(i.title)
反向查詢

3 多對多

 

 

正向查詢 反向查詢

 

三 基於雙下劃線的跨表查詢

模型類.objects.filter().values()

1 跨表操作在filter 和values使用__(雙下劃線)進行跨表操作;
2 正向查詢按關聯欄位,反向查詢按照表名小寫;

  1 一對一

    # 查詢alex的手機號
    
    # 正向查詢
    ret=Author.objects.filter(name="alex").values("authordetail__telephone")

    # 反向查詢
    ret=AuthorDetail.objects.filter(author__name="alex").values("telephone")

  2 一對多

# 練習:  查詢蘋果出版社出版過的所有書籍的名字與價格(一對多)

    # 正向查詢 按欄位:publish

    queryResult=Book.objects
            .filter(publish__name="蘋果出版社")
            .values_list("title","price")

    # 反向查詢 按表名:book

    queryResult=Publish.objects
              .filter(name="蘋果出版社")
              .values_list("book__title","book__price")

  3 多對多

# 練習: 查詢alex出過的所有書籍的名字(多對多)

    # 正向查詢 按欄位:authors:
    queryResult=Book.objects
            .filter(authors__name="yuan")
            .values_list("title")

    # 反向查詢 按表名:book
    queryResult=Author.objects
              .filter(name="yuan")
              .values_list("book__title","book__price")

  4 進階練習(連續跨表)

# 練習: 查詢人民出版社出版過的所有書籍的名字以及作者的姓名


    # 正向查詢
    queryResult=Book.objects
            .filter(publish__name="人民出版社")
            .values_list("title","authors__name")
    # 反向查詢
    queryResult=Publish.objects
              .filter(name="人民出版社")
              .values_list("book__title","book__authors__age","book__authors__name")


# 練習: 手機號以151開頭的作者出版過的所有書籍名稱以及出版社名稱


    # 方式1:
    queryResult=Book.objects
            .filter(authors__authorDetail__telephone__regex="151")
            .values_list("title","publish__name")
    # 方式2:    
    ret=Author.objects
              .filter(authordetail__telephone__startswith="151")
              .values("book__title","book__publish__name")
雙下劃線跨表跨表鞏固

 

四 聚合查詢於分組

emp-dep:

id  name age   salary   dep_id   id   name 
1   alex  12   2000       1      1    銷售部
2   egon  22   3000       2      2    人事部
3   wen   22   5000       2      2    人事部

class Emp(models.Model):
    name=models.CharField(max_length=32)
    age=models.IntegerField()
    salary=models.DecimalField(max_digits=8,decimal_places=2)
    dep=models.CharField(max_length=32)
    province=models.CharField(max_length=32)

  1 聚合 (aggregate) ---聚合函式 Sav, Max, Min, Avag

基本語法:
    模型類.objects.aggregate(聚合函式( 欄位 ))
結果:queryset型別,一個物件一個字典儲存資料
例項1:
# 計算所有圖書的平均價格
    from django.db.models import Avg
    Book.objects.all().aggregate(Avg('price')) #  {'price__avg': 34.35}
例項2:
    from django.db.models import Avg, Max, Min
    Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
#{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

  2 分組(annotate)

key:
    跨表分組查詢本質就是將關聯表join成一張表,再按單表的思路進行分組查詢。 
語法格式:
    (1)模型類.objects.all().value('分組條件').annotate('聚合函式')
                結果是queryset物件,其中元素是字典
              <QuerySet [{..},{...}]>
               
    
    (2)模型類.objects.all().annotate('聚合函式') #以主鍵分組
                結果是queryset物件,其中元素是物件 
                #<QuerySet [<Book: java>,<Book: java>]>
                    

  3 練習

(1)統計每個出版社的最便宜的書

publish_list=models.Publish.objects.annotate(min("book_price"))

for publish_obj in publish_list:
    print(publish_obj.name,publish_obj.MinPrice)
簡化程式碼:
queryResult= Publish.objects
            .annotate(MinPrice=Min("book__price"))
            .values_list("name","MinPrice")
print(queryResult)
SELECT "app01_publish"."name", MIN("app01_book"."price")  AS "MinPrice" FROM "app01_publish" 
LEFT  JOIN "app01_book" ON ("app01_publish"."nid" = "app01_book"."publish_id") 
GROUP BY "app01_publish"."nid", "app01_publish"."name", "app01_publish"."city", "app01_publish"."email" 
對應mysql語句

(2)統計每本書的作者個數

models.Book.objects.annotate(authorsNum=Count('authors'))#待會檢測

(3)統計每一本以py開頭的書籍的作者個數

models.Book.objects.filter(title_startswith='py')
            .annocate(num_authors=Count('authors')
            .values('title','c')

(4)統計不止一個作者的圖書

models.Book.objects
                   .annocate( num_authors=Count('author'))
                   .filter(num_authors__lg=1)

(5)根據一本圖書作者數量的多少對查詢集 QuerySet進行排序

models.Book.objects
           .annotate(num_authors=Count('authors')) 
          .order_by('num_authors')

(6)查詢各個作者出的書的總價格

models.author.objects
           .annotate(sum_price=sum('book__price')) 
           .values('name','sum_price')

 

五 F查詢與Q 查詢

  1 F查詢 ---- F('欄位名')

(1) 意義

1 在過濾條件中藉助F查詢,使得欄位之間可以作比較;
    2 在過濾條件中藉助F查詢,使得欄位之間可以做四則運算;

(2) 欄位之間的比較例項

# 查詢評論數大於收藏數的書籍

from django.db.models import F

models.objects.filter(commentNum__lg=F('keepNum'))

(3) 四則運算在過濾條件中的應用

# 查詢評論數大於收藏數2倍的書籍

from django.db.models import F
models.Book.objects.filter(commerntNum__lg=F('keepNum')*2)

#修改操作也可以使用F函式,比如將每一本書的價格提高30元:

models.Book.objects.update(price=F('price)+30)

  2 Q 查詢 ---- Q('欄位名')

(1)  意義

使得過濾條件的方式不再是單一的 & (與)運算

Q查詢中的與|或|非
與: &
或: |
非: ~

(2)查詢價格大於300或者名稱以p開頭的書籍 ( |  或運算)

queryset=models.Book.objects.filter(price__lg=300|Q(title__startswith='p')).values('title')

(3)查詢價格大於300或者不是2019年一月份的書籍

models.Book.objects.filter(title__lg=300|~Q(Q(pub_date_year=2016)&Q(pub_date_mouth=1)))