Web框架之Django_05 模型層瞭解(單表查詢、多表查詢、聚合查詢、分組查詢)

摘要:

  • 單表查詢

  • 多表查詢

  • 聚合查詢

  • 分組查詢

1、Django ORM 經常使用字段和參數:

  1. 經常使用字段:
    #AutoField
    int自增列,必須填入參數primary_key = True,當model中若是沒有自增列,則會自動建立一個列名爲id的列
    #IntegerField
    一個整數類型,範圍在--2147483648 to 2147483647。(通常不用它來存手機號(位數也不夠),直接用字符串存)
    #CharField
    字符類型,必須提供max_length參數,max_length表示字符長度
    Tips:Django的CharField對應的MySQL數據庫中的varchar類型,沒有設置對應char類型的字段,可是Django容許咱們自定義新的字段,下面我來自定義對應於數據庫的char類型

    #應用上面自定義的char類型
    class Class(models.Model):
        id=models.AutoField(primary_key=True)
        title=models.CharField(max_length=32)
        class_name=RealCharField(max_length=16)
        gender_choice=((1,''),(2,''),(3,'保密'))
        gender=models.SmallIntegerField(choices=gender_choice,default=3)
    須要注意的是:自定義字段在實際項目應用中可能會常常用到,因此這裏須要留意。
    #DateField
    日期字段,日期格式 YYYY-MM-MD,例如:2019-6-12,至關於Python中的datetime.date()實例。
    #DateTimeField 
    日期格式字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],至關於Python中的datetime.datetime()實例。
  2. 字段合集:
    AutoField(Field)
        - int自增列,必須填入參數 primary_key=True
    
    BigAutoField(AutoField)
        - bigint自增列,必須填入參數 primary_key=True
    
        注:當model中若是沒有自增列,則自動會建立一個列名爲id的列
        from django.db import models
    
        class UserInfo(models.Model):
            # 自動建立一個列名爲id的且爲自增的整數列
            username = models.CharField(max_length=32)
    
        class Group(models.Model):
            # 自定義自增列
            nid = models.AutoField(primary_key=True)
            name = models.CharField(max_length=32)
    
    SmallIntegerField(IntegerField):
        - 小整數 -32768 ~ 32767
    
    PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正小整數 0 ~ 32767
    IntegerField(Field)
        - 整數列(有符號的) -2147483648 ~ 2147483647
    
    PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正整數 0 ~ 2147483647
    
    BigIntegerField(IntegerField):
        - 長整型(有符號的) -9223372036854775808 ~ 9223372036854775807
    
    BooleanField(Field)
        - 布爾值類型
    
    NullBooleanField(Field):
        - 能夠爲空的布爾值
    
    CharField(Field)
        - 字符類型
        - 必須提供max_length參數, max_length表示字符長度
    
    TextField(Field)
        - 文本類型
    
    EmailField(CharField):
        - 字符串類型,Django Admin以及ModelForm中提供驗證機制
    
    IPAddressField(Field)
        - 字符串類型,Django Admin以及ModelForm中提供驗證 IPV4 機制
    
    GenericIPAddressField(Field)
        - 字符串類型,Django Admin以及ModelForm中提供驗證 Ipv4和Ipv6
        - 參數:
            protocol,用於指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
            unpack_ipv4, 若是指定爲True,則輸入::ffff:192.0.2.1時候,可解析爲192.0.2.1,開啓此功能,須要protocol="both"
    
    URLField(CharField)
        - 字符串類型,Django Admin以及ModelForm中提供驗證 URL
    
    SlugField(CharField)
        - 字符串類型,Django Admin以及ModelForm中提供驗證支持 字母、數字、下劃線、鏈接符(減號)
    
    CommaSeparatedIntegerField(CharField)
        - 字符串類型,格式必須爲逗號分割的數字
    
    UUIDField(Field)
        - 字符串類型,Django Admin以及ModelForm中提供對UUID格式的驗證
    
    FilePathField(Field)
        - 字符串,Django Admin以及ModelForm中提供讀取文件夾下文件的功能
        - 參數:
                path,                      文件夾路徑
                match=None,                正則匹配
                recursive=False,           遞歸下面的文件夾
                allow_files=True,          容許文件
                allow_folders=False,       容許文件夾
    
    FileField(Field)
        - 字符串,路徑保存在數據庫,文件上傳到指定目錄
        - 參數:
            upload_to = ""      上傳文件的保存路徑
            storage = None      存儲組件,默認django.core.files.storage.FileSystemStorage
    
    ImageField(FileField)
        - 字符串,路徑保存在數據庫,文件上傳到指定目錄
        - 參數:
            upload_to = ""      上傳文件的保存路徑
            storage = None      存儲組件,默認django.core.files.storage.FileSystemStorage
            width_field=None,   上傳圖片的高度保存的數據庫字段名(字符串)
            height_field=None   上傳圖片的寬度保存的數據庫字段名(字符串)
    
    DateTimeField(DateField)
        - 日期+時間格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
    
    DateField(DateTimeCheckMixin, Field)
        - 日期格式      YYYY-MM-DD
    
    TimeField(DateTimeCheckMixin, Field)
        - 時間格式      HH:MM[:ss[.uuuuuu]]
    
    DurationField(Field)
        - 長整數,時間間隔,數據庫中按照bigint存儲,ORM中獲取的值爲datetime.timedelta類型
    
    FloatField(Field)
        - 浮點型
    
    DecimalField(Field)
        - 10進制小數
        - 參數:
            max_digits,小數總長度
            decimal_places,小數位長度
    
    BinaryField(Field)
        - 二進制類型
    字段合集
    對應關係:
        'AutoField': 'integer AUTO_INCREMENT',
        'BigAutoField': 'bigint AUTO_INCREMENT',
        'BinaryField': 'longblob',
        'BooleanField': 'bool',
        'CharField': 'varchar(%(max_length)s)',
        'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
        'DateField': 'date',
        'DateTimeField': 'datetime',
        'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
        'DurationField': 'bigint',
        'FileField': 'varchar(%(max_length)s)',
        'FilePathField': 'varchar(%(max_length)s)',
        'FloatField': 'double precision',
        'IntegerField': 'integer',
        'BigIntegerField': 'bigint',
        'IPAddressField': 'char(15)',
        'GenericIPAddressField': 'char(39)',
        'NullBooleanField': 'bool',
        'OneToOneField': 'integer',
        'PositiveIntegerField': 'integer UNSIGNED',
        'PositiveSmallIntegerField': 'smallint UNSIGNED',
        'SlugField': 'varchar(%(max_length)s)',
        'SmallIntegerField': 'smallint',
        'TextField': 'longtext',
        'TimeField': 'time',
        'UUIDField': 'char(32)',
    ORM字段與MySQL字段對應關係
  3. 字段參數:
    #null :
    用於表示某個字段能夠爲空
    #unique :
    若是設置爲unique = True 則該字段在此表中必須是惟一的
    # db_index :
    若是db_index = True 則表明着爲此字段設置索引
    # default :
    爲該字段設置默認值
  4. DateField和DateTimeField:
    #
    auto_now_add
    配置auto_now_add = True,建立數據記錄的時候會把當前時間添加到數據庫,後續操做數據不自動更新
    #auto_now
    配置上auto_now = True,每次更新數據記錄的時候會更新該字段
  5. 關係字段:

    #①ForeignKey
    外鍵類型在ORM中用表示外鍵關聯關係,通常吧ForeignKey字段設置在‘一段多’中的多的一方,ForeignKey能夠和其它表作關聯關係同時也能夠和自身作關聯關係。
    #字段參數:
    ##to   
    設置要關聯的表
    ##to_field   
    設置要關聯的表的字段
    ##on_delete   
    當刪除關聯表中的數據時,當前表與其關聯的行的行爲
    models.CASCADE (Django2.x版本必需要設置,1.x版本默認就設置了)
    刪除關聯數據,與之關聯也刪除
    ##db_constraint   
    是否在數據庫中建立外鍵約束,默認爲True
    models.DO_NOTHING
    刪除關聯數據,引起錯誤IntegrityError
    
    
    models.PROTECT
    刪除關聯數據,引起錯誤ProtectedError
    
    
    models.SET_NULL
    刪除關聯數據,與之關聯的值設置爲null(前提FK字段須要設置爲可空)
    
    
    models.SET_DEFAULT
    刪除關聯數據,與之關聯的值設置爲默認值(前提FK字段須要設置默認值)
    
    
    models.SET
    
    刪除關聯數據,
    a. 與之關聯的值設置爲指定值,設置:models.SET(值)
    b. 與之關聯的值設置爲可執行對象的返回值,設置:models.SET(可執行對象)
    其他字段參數

    示例:python

    def func():
        return 10
    
    class MyModel(models.Model):
        user = models.ForeignKey(
            to="User",
            to_field="id",  # 不寫默認關聯對方關聯表的primary_key
            on_delete=models.SET(func)
        )

    #②OneToOneField
    一對一字段
    一般一對一字段用來擴展已有字段。(通俗的說就是一我的的全部信息不是放在一張表裏面的,簡單的信息一張表,隱私的信息另外一張表,之間經過一對一外鍵關聯)mysql

    #字段參數:
    ##to    設置要關聯的表
    ##to_field    
    設置要關聯的表的字段
    ##on_delete    
    當刪除關聯表中的數據時,當前與其關聯的行的行爲(參考ForeignKey)
    #③ManyToManyField
    多對多字段

    #字段參數:
    ##to    設置要關聯的表
    ##to_field    
    設置要關聯的表的字段
    ##on_delete    
    當刪除關聯表中的數據時,當前與其關聯的行的行爲(參考ForeignKey)
    分別以圖書管理系統的Book、Publish、Author三張表直接的關係來舉例
    
    from django.db import models
    
    
    class Book(models.Model):
        title = models.CharField(max_length=32)
        price = models.DecimalField(max_digits=6, decimal_places=1)
        publish = models.ForeignKey(to='Publish')
        author = models.ManyToManyField(to='Author')
    
    
    class Publish(models.Model):
        name = models.CharField(max_length=32)
        email = models.CharField(max_length=32)
    
    
    class Author(models.Model):
        name = models.CharField(max_length=32)
        age = models.IntegerField(default=18)
    例子

 2、單表查詢

  • 準備
    爲了更方便的測試表查詢的結果,咱們須要先配置一下參數,能夠是咱們直接在Django頁面中運行咱們的測試表查詢代碼,快速顯示結果,因此須要作以下操做:
    項目根目錄建立一個test.py測試文件>>>>在項目的manage.py文件中複製一段代碼>>>>將複製的代碼粘貼到test.py文件中,而後再加入幾段代碼,這樣就能夠在test文件中進行數據庫表數據操做了,固然,鏈接數據庫的過程不能少,表的建立也是。
    manage.py文件複製的代碼:
    import os
    if __name__ == "__main__":
        os.environ.setdefault("DJANGO_SETTINGS_MODULE", "created_by_pycharm.settings")
     
    
    添加的代碼:(在main下面)
        import django
        django.setup()
        from app01 import models    # 注意這句話必須在這裏,不能放最上面

    這裏須要建一張表,用於單表查詢:(models文件內)
    from django.db import models
    
    # Create your models here.
    # 單表查詢示例表
    class User(models.Model):
        name = models.CharField(max_length=32)
        age = models.IntegerField()
        register_time = models.DateField(auto_now_add=True)
    項目settings裏面將MySQL數據庫連入:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'orm_test',
            'HOST': '127.0.0.1',
            'PORT': 3306,
            'USER': 'root',
            'PASSWORD': '123',
        }
    }
    
    
    同時不要忘了在項目程序文件夾中init文件添加代碼:
    import pymysql
    pymysql.install_as_MySQLdb()
    這樣才表明數據庫已經和Django項目對接完畢
    固然新建完要生效,須要進行數據庫遷移(同步)操做
    python3 manage.py makemigrations
    python3 manage.py migrate
  • 新增表數據
    # 基於create建立
        models.User.objects.create(name='sgt', age=18)
    
        # 基於對象的綁定方法建立
        user_obj = models.User(name='王大錘', age=12)
        user_obj.save()
  • 修改表數據
    # 基於QuerySet
        models.User.objects.filter(name='ttt').update(age=100)
        # 基於對象
        user_obj = models.User.objects.filter(name='sss').first()
        user_obj.age = 99
        user_obj.save()
  • 刪除表數據
    # 基於QuerySet
        models.User.objects.filter(name='ggg').delete()
        # 基於對象
        user_obj = models.User.objects.filter(name='ttt').first()
        user_obj.delete()
    總結一下單表操做的一些方法:

    <1> all():  查詢全部的結果,返回QuerySet對象們的列表
    git

    res = models.User.objects.all()
    print(res)
    <QuerySet [<User: User object>, <User: User object>, <User: User object>, <User: User object>]>

    <2> filter(**kwargs): 經過括號中的條件查詢到匹配的對象,返回QuerySet對象們的列表sql

    res = models.User.objects.filter(name='sgt')
    print(res)
    <QuerySet [<User: User object>, <User: User object>, <User: User object>]>
    
    這裏提一下:filter篩選條件是能夠有多個的,多個條件是與關係,必須所有知足纔會獲得查詢結果

    <3> get(**kwargs):  返回與所給篩選條件匹配的object對象,且返回結果只有一個若是符合篩選條件的對象超過一個或者沒有都會拋出錯誤。
    數據庫

    res1 = models.User.objects.get(name='sgt')
    # app01.models.MultipleObjectsReturned: get() returned more than one User -- it returned 3!
    res2 = models.User.objects.get(name='孫悟空')
    # app01.models.DoesNotExist: User matching query does not exist.
    res3 = models.User.objects.get(name='王大錘')  # User object

    <4> exclude(**kwargs):  返回篩選條件不匹配的QuerySet對象們的列表(取反篩選)
    django

    res1 = models.User.objects.exclude(name='sgt')
    <QuerySet [<User: User object>, <User: User object>, <User: User object>]>

    <5> order_by(*field):  對查詢結果經過某一字段進行排序(默認升序),返回QuerySet對象們的列表json

    res1 = models.User.objects.filter(name='sgt').order_by('age')
    # 排序默認是升序,若是這裏的age前加上一個負號‘-’,怎會進行降序排序。
    # res1 = models.User.objects.filter(name='sgt').order_by('-age')
    print(res1)
    # <QuerySet [<User: User object>, <User: User object>, <User: User object>]>
    # 爲了讓結果更清晰,遍歷下獲得排好序的QuerySet對象們列表,以其age顯示順序
    users = []
    for user_obj in res1:
        users.append(user_obj.age)
    print(users)   # [18, 22, 32]

    <6> reverse():  對查詢的排序結果進行反向排列,記住,不是反向排序,而是反向排列,返回QuerySet對象們的列表
    app

    res1 = models.User.objects.filter(name='sgt').order_by('age').reverse()
    users = []
    for user_obj in res1:
        users.append(user_obj.age)
    print(users)   # 結果反過來排列了:[32, 22, 18]

    <7> count():  QuerySet方法,返回查詢到的QuerySet對象的數量,該方法只能對返回的查詢結果爲QuerySet對象使用,若是返回的對象是一個普通的對象,那麼結果跟res3同樣ide

    res1 = models.User.objects.filter(name='sgt').count()
    print(res1)  # 3
    
    res2 = models.User.objects.filter(name='hahaha').count()
    print(res2)  # 0
    
    res3 = models.User.objects.get(name='王大錘').count()
    # 拋出錯誤:AttributeError: 'User' object has no attribute 'count'

    <8> first():  返回查詢結果的第一個對象,返回一個普通對象(User object)函數

    res = models.User.objects.filter(name='sgt').first()
    print(res)   # User object

    <9> last(): 返回查詢結果的最後一個對象,返回一個普通對象(User object)

    res = models.User.objects.filter(name='sgt').last()
    print(res)  # User object

    <10> exists(): QuerySet方法, 若是QuerySet包含數據,就返回True,不然返回False

    res = models.User.objects.filter(name='sgt').exists()
    print(res)  # True
    res = models.User.objects.filter(name='sgg').exists()
    print(res)  # False

    <11> values(*field): 返回一個特殊的QuerySet對象,對象裏面是一個列表套字典(model的實例化對象,而是一個可迭代的字典序列)

    res = models.User.objects.filter(name='sgt').values('age')
    print(res)
    print(res.order_by('age'))
    print(res.order_by('age').reverse())
    print(res.count())
    print(res[0])
    print(res.first(), res.last())
    
    <QuerySet [{'age': 18}, {'age': 32}, {'age': 22}]>  # values方法返回結果
    <QuerySet [{'age': 18}, {'age': 32}, {'age': 22}]>  # 經過age來反向排列,無效,說明該方法只能用在order_by以後,才能進行反向排列
    <QuerySet [{'age': 18}, {'age': 22}, {'age': 32}]>  # order_by
    <QuerySet [{'age': 32}, {'age': 22}, {'age': 18}]>  # order_by後再reverse
    3   # 使用count方法
    {'age': 18}  # 能夠用索引取出對應索引的字典
    {'age': 18} {'age': 22}   # 使用first和last方法

    <12> values_list(*field): 它與values()很是類似,它返回的是一個元組序列的QuerySet對象

    res = models.User.objects.filter(name='sgt').values_list('age')
    print(res)
    print(res.order_by('age'))
    print(res.order_by('age').reverse())
    print(res.count())
    print(res[0])
    print(res.first(), res.last())
    
    <QuerySet [(18,), (32,), (22,)]>
    <QuerySet [(18,), (22,), (32,)]>
    <QuerySet [(32,), (22,), (18,)]>
    3
    (18,)
    (18,) (22,)
    
    結果和values方法差很少,只是把字典序列變成了元祖序列

    <13> distinct(): 從返回結果中剔除重複紀錄,注意必須是全部數據都相同才知足去重條件,可是一個表中的每一條數據是不會徹底重複的,由於有primary_key的存在,就不會徹底相同,因此此方法的主要是在上面的values和values_list方法的基礎上用的話會實現一些咱們想要的結果。返回特殊的QuerySet對象

    res1 = models.User.objects.filter(name='sgt')
    res2 = models.User.objects.filter(name='sgt').distinct()
    print(res1)
    print(res2)
    # <QuerySet [<User: User object>, <User: User object>, <User: User object>, <User: User object>]>
    # <QuerySet [<User: User object>, <User: User object>, <User: User object>, <User: User object>]>
    # 結果數量不變,說明沒有去重的項目,主要緣由:id沒有相同的,確定沒法去重
    # 咱們用values方法的基礎上使用distinct方法:
    res1 = models.User.objects.filter(name='sgt').values('age')
    res2 = res1.distinct()
    print(res1)
    print(res2)
    
    # <QuerySet [{'age': 18}, {'age': 32}, {'age': 22}, {'age': 22}]>
    # <QuerySet [{'age': 18}, {'age': 32}, {'age': 22}]>
    # 發現這樣去重的意義就出來了
  • 只要是QuerySet對象均可以無線的點其中的方法,點下去...
  • 基於雙下劃線的單表查詢:

    大於(__gt)、小於(__lt)、大於等於(__gte)、小於等於(__lte)

    # 將結果再使用values方法,更直接顯示
    res1 = models.User.objects.filter(age__gt=12).values('name', 'age')
    print(res1)
    # <QuerySet [{'name': '王大錘', 'age': 18}, {'name': '王大炮', 'age': 66}, {'name': '黃飛鴻', 'age': 88}]>
    res2 = models.User.objects.filter(age__lt=12).values('name', 'age')
    print(res2)
    # <QuerySet [{'name': '米小圈', 'age': 8}, {'name': '鐵頭', 'age': 9}]>
    res3 = models.User.objects.filter(age__gte=12).values('name', 'age')
    print(res3)
    # <QuerySet [{'name': '王大錘', 'age': 18}, {'name': '佩奇', 'age': 12}, {'name': '王大炮', 'age': 66}, {'name': '黃飛鴻', 'age': 88}]>
    res4 = models.User.objects.filter(age__lte=12).values('name', 'age')
    print(res4)
    # <QuerySet [{'name': '米小圈', 'age': 8}, {'name': '鐵頭', 'age': 9}, {'name': '佩奇', 'age': 12}]>

    包含(區分大小寫__contains|不區分大小寫__icontains),以xx開頭(__startswith),以xx結尾(__endswith)

    res1 = models.User.objects.filter(name__contains='i').values('name', 'age')
    print(res1)
    # <QuerySet [{'name': 'igon', 'age': 55}, {'name': 'igoN', 'age': 66}]>
    res2 = models.User.objects.filter(name__contains='N').values('name', 'age')
    print(res2)
    # <QuerySet [{'name': 'igoN', 'age': 66}]>
    res0 = models.User.objects.filter(name__icontains='j').values('name', 'age')
    print(res0)
    # <QuerySet [{'name': 'json', 'age': 11}, {'name': 'Jason', 'age': 16}]>
    res3 = models.User.objects.filter(name__startswith='j').values('name', 'age')
    print(res3)
    # <QuerySet [{'name': 'json', 'age': 11}]>
    res4 = models.User.objects.filter(name__endswith='t').values('name', 'age')
    print(res4)
    # <QuerySet [{'name': 'sgt', 'age': 18}]>

     時間爲2018年的記錄:(__year=2017)

    res = models.User.objects.filter(register_time__year=2018).values('name', 'age', 'register_time')
    print(res)
    # <QuerySet [{'name': 'json', 'age': 11, 'register_time': datetime.date(2018, 6, 21)}]>

     在指定容器內__in=[a,b,c],在指定範圍內__range=[m,n],首尾都包括在內

    res1 = models.User.objects.filter(age__in=[66, 88]).values('name', 'age')
    print(res1)
    # <QuerySet [{'name': '王大炮', 'age': 66}, {'name': '黃飛鴻', 'age': 88}, {'name': 'igoN', 'age': 66}]>
    res2 = models.User.objects.filter(age__range=[9, 12]).values('name', 'age')
    print(res2)
    # <QuerySet [{'name': '鐵頭', 'age': 9}, {'name': '佩奇', 'age': 12}, {'name': 'json', 'age': 11}]>

 3、多表查詢

  • 表與表之間的關係

    #一對一(OneToOneField):一對一字段不管建在哪張關係表裏面均可以,可是推薦建在查詢頻率比較高的那張表裏面
    #一對多(ForeignKey):一對多字段建在多的那一方
    #多對多(ManyToManyField):多對多字段不管建在哪張關係表裏面均可以,可是推薦建在查詢頻率比較高的那張表裏面
    多對多操做:
        添加add(),
        修改set(),
        移除remove() 注意不能接收可迭代對象,
        清空clear()  無需傳參

    如何判斷表與表之間究竟是什麼關係
    換位思考
        A能不能有多個B
        B能不能有多個A
  • 多表的增刪改查:

    from django.db import models
    
    # 多表查詢示例表
    class Book(models.Model):
        title = models.CharField(max_length=32)
        price = models.DecimalField(max_digits=8, decimal_places=2)
        publish_date = models.DateField(auto_now_add=True)
        # 外鍵關係
        # 一對多:(外鍵)
        publish = models.ForeignKey(to='Publish')
        # 多對多:
        author = models.ManyToManyField(to='Author')   # 這是一個虛擬字段, 信號字段
    
    
    class Publish(models.Model):
        name = models.CharField(max_length=32)
        addr = models.CharField(max_length=32)
        email = models.EmailField()  # 對應的就是varchar類型
    
    
    class Author(models.Model):
        name = models.CharField(max_length=32)
        age = models.IntegerField()
        # 一對一關係
        authordetail = models.OneToOneField(to='AuthorDetail')
    
    class AuthorDetail(models.Model):
        phone = models.CharField(max_length=32)
        addr = models.CharField(max_length=32)
    ======增刪改:一對多======
    Book與Publish 一對多關係
    新增:
    # 一對多新增:
    # 因爲book表和Publish表示多對一的關係,因此咱們須要先創建Publish相關數據才能進行book記錄新增
    models.Publish.objects.create(name='東方出版社', addr='北京', email='123@163.com')
    models.Publish.objects.create(name='西方出版社', addr='內蒙', email='456@163.com')
    models.Publish.objects.create(name='南方出版社', addr='武漢', email='789@163.com')
    models.Publish.objects.create(name='北方出版社', addr='上海', email='546@163.com')
    # 新增book記錄(直接添加寫id)
    models.Book.objects.create(title='西遊記', price=66, publish_id=1)
    models.Book.objects.create(title='紅樓夢', price=266, publish_id=2)
    models.Book.objects.create(title='西廂記', price=166, publish_id=3)
    models.Book.objects.create(title='金屏梅', price=366, publish_id=4)
    #  一對多新增,傳輸數據對象
    publish_obj = models.Publish.objects.filter(pk=1).first()
    models.Book.objects.create(title='三國志', price=48, publish=publish_obj)
    修改:
    # 一對多修改
    
    # QuerySet方法修改
    ①
    models.Book.objects.filter(pk=1).update(publish_id=2)
    ②
    publish_obj = models.Publish.objects.filter(pk=4).first()
    models.Book.objects.filter(pk=1).update(publish=publish_obj)
    
    # 對象方法修改
    ①
    book_obj = models.Book.objects.filter(pk=1).first()
    book_obj.publish_id = 3  # 點表中真實存在的字段名
    book_obj.save()
    ②
    book_obj = models.Book.objects.filter(pk=1).first()
    publish_obj = models.Publish.objects.filter(pk=1).first()
    book_obj.publish = publish_obj
    book_obj.save()
    刪除:(默認狀況下)
    # 多對一刪除
    ①QuerySet方法
    # 當刪除'多'的表數據,則那張'一'的表無影響
    models.Book.objects.filter(pk=1).delete()
    
    # 當刪除'一'的表數據,則那張'多'的表對應的記錄會刪除
    models.Publish.objects.filter(pk=2).delete()
    
    ②對象方法:
    book_obj = models.Book.objects.filter(pk=2).first()
    book_obj.delete()
    ======增刪改:多對多======
    book與author表之間是多對多關係
    新增
    # 多對多關係表的新增
    # 過程:先新增數據,後綁定關係
    # 先新增author數據和book數據(假設做者信息已經存在對應Detail表)
    models.Author.objects.create(name='蕭鼎', age=66, authordetail_id=1)
    models.Author.objects.create(name='路遙', age=56, authordetail_id=2)
    models.Author.objects.create(name='三毛', age=77, authordetail_id=3)
    models.Author.objects.create(name='金庸', age=95, authordetail_id=4)
    book_obj = models.Book.objects.create(title='畫江湖', price=99, publish_id=1)
    book_obj.author.add(2, 3)
    
    固然新增也支持傳對象的方式
    author_obj1 = models.Author.objects.filter(pk=1).first()
    author_obj2 = models.Author.objects.filter(pk=2).first()
    book_obj.author.add(author_obj1, author_obj2)

    修改

    # 修改book和author記錄及其關係:set()方法,必須傳可迭代對象
    book_obj = models.Book.objects.filter(pk=3).first()
    # 修改方法和新增相似,也是修改自身數據後再修改關係
    # 能夠傳數字和對象,而且也支持傳多個
    # 傳id
    book_obj.author.set((1,))   # 傳一個
    book_obj.author.set((1,2,3))  # 傳多個
    # 傳對象
    author_list = models.Author.objects.all()
    book_obj = models.Book.objects.filter(pk=3).first()
    book_obj.author.set(author_list)
    刪除:默認狀況下刪除一方表記錄,連帶的會刪除其綁定關係,可是另外一邊記錄無影響,僅僅是綁定關係沒有了
    # 多對多刪除
    # QuerySet方法
    models.Book.objects.filter(pk=4).delete()
    # 刪除id爲4的書,同時會自動刪除此書與做者之間的關係,此時綁定關係會自動刪除
    
    # 對象方法
    book_obj = models.Book.objects.filter(pk=5).first()
    book_obj.author.remove(2)  # 先刪除關係不會刪除book記錄數據
    
    book_obj = models.Book.objects.filter(pk=6).first()
    book_obj.delete()  # 刪除book對象,會自動觸發刪除綁定關係
    
    # 假設一本書有多個做者
    book_obj = models.Book.objects.filter(pk=1).first()
    author_list = models.Author.objects.all()
    book_obj.author.remove(*author_list)   # 注意這裏的QuerySet須要打散才行

     ======增刪改:一對一======
    刪除

    # 一對一,author表建立外鍵綁定關係到authordetail表,刪除建立外鍵的那張author表的記錄,另外一方無影響
    models.Author.objects.filter(pk=1).delete()
    # 一對一,刪除非外鍵的那張authordetail表記錄,則綁定關係以及對應的author表記錄都會隨之刪除
    models.AuthorDetail.objects.filter(pk=2).delete()
    新增:
    # 一對一新增:建立外鍵的表author中新增記錄,對應的外鍵必須未用佔且存在,由於是一對一關係,不然報錯
    models.Author.objects.create(name='王錘', age=88, authordetail_id=1)
    # 一對一新增,authordetail表中新增記錄,author表無影響.
    models.AuthorDetail.objects.create(phone='123', addr='毛里求斯')
    修改:
    # 一對一修改,
    models.Author.objects.filter(pk=8).update(name='湯匙')
    models.AuthorDetail.objects.filter(pk=4).update(addr='伊拉克')
    
    # 一對一修改 關聯外鍵值:
    # 外鍵值必須未佔用,且存在,否則報錯
    models.Author.objects.filter(pk=8).update(authordetail_id=5)

    ======清空clear()======

    book_obj = models.Book.objects.filter(pk=3).first()
    book_obj.authors.clear()
    # 清空當前書籍對應的全部做者的綁定關係
  • 正向查詢:經過一張表a查詢另外一張表b,若是a與b之間的關係外鍵是建在a中,那麼咱們從a中能經過這個外鍵字段查詢到b表中的對應數據的過程就叫作正向查詢
    反向查詢:上面的正向查詢,若是從b表去找a表的數據,外鍵仍是建在a表中,這時候b表中就沒有與a表關聯的字段,此時b查到a就須要用另外的手段來實現,這種就叫反向查詢
    正向、反向查詢方法:
    原則:正向查詢按字段,反向查詢按表名小寫

    # 一對一

    Author表和AuthorDetail表是一對一關係:(外鍵在Author表中)

    ## 正向查詢:從Author查到AuthorDetail:
    res = models.Author.objects.filter(pk=3).first().authordetail.addr
    print(res)    # 冥王星
    
    ## 反向查詢:從AuthorDetail 查到Author
    res1 = models.AuthorDetail.objects.filter(pk=3).first().author.name
    print(res1)  # 三毛

    # 一對多

    # Book到Publish
    res = models.Book.objects.filter(pk=1).first().publish.name
    print(res)  # 東方出版社
    
    # Publish到Book
    res1 = models.Publish.objects.filter(pk=2).first().book_set.all()
    print(res1)  # <QuerySet [<Book: Book object>, <Book: Book object>]>
    res2 = models.Publish.objects.filter(pk=2).first().book_set.all().values('title')
    print(res2)  # <QuerySet [{'title': '紅樓夢'}, {'title': '西遊記'}]>

    # 多對多

    # 正向查詢
    res1 = models.Book.objects.filter(pk=2).first().author.all()
    print(res1)  # <QuerySet [<Author: Author object>, <Author: Author object>]>
    res2 = models.Book.objects.filter(pk=2).first().author.all().values('name')
    print(res2)  # <QuerySet [{'name': '金庸'}, {'name': '湯匙'}]>
    # 反向查詢
    res3  = models.Author.objects.filter(pk=3).first().book_set.all()
    print(res3)  # <QuerySet [<Book: Book object>, <Book: Book object>]>
    res4  = models.Author.objects.filter(pk=3).first().book_set.all().values('title')
    print(res4)  # <QuerySet [{'title': '西廂記'}, {'title': '紅樓夢'}]>

    規律:一對多、多對多 反向查詢的時候要加_set.all()
              一對一 反向查詢不用加

  • 基於雙下劃線的多表查詢

  • 用例子來講明:
    ① 查詢書籍爲紅樓夢的出版社名:
    res = models.Book.objects.filter(title='紅樓夢').values('publish__name')
    print(res)  # <QuerySet [{'publish__name': '南方出版社'}]>

     ② 查詢書籍爲西遊記的做者的姓名:

    res = models.Book.objects.filter(title='西遊記').values('author__name')
    print(res)  # <QuerySet [{'author__name': '三毛'}, {'author__name': '唐唐'}]>

    ③ 查詢南方出版社第一版的書名:

    res = models.Publish.objects.filter(name='南方出版社').values('book__title')
    print(res)  # <QuerySet [{'book__title': '紅樓夢'}, {'book__title': '西遊記'}]>

    ④ 查詢電話號碼是911的做者寫的書名:

    res = models.AuthorDetail.objects.filter(phone='911').values('author__book__title')
    print(res)  # <QuerySet [{'author__book__title': '三國志'}, {'author__book__title': '西廂記'}]>

    ⑤ 查詢做者名字爲金庸寫的書的書名:

    res = models.Author.objects.filter(name='金庸').values('book__title')
    print(res)  # <QuerySet [{'book__title': '三國志'}, {'book__title': '西廂記'}]>

     ⑥ 查詢書籍爲西遊記的做者的電話號碼:

    res = models.Book.objects.filter(title='西遊記').values('author__name', 'author__authordetail__phone')
    print(res)
    # <QuerySet [{'author__name': '三毛', 'author__authordetail__phone': '119'}, 
    # {'author__name': '唐唐', 'author__authordetail__phone': '110'}]>

    ⑦ 經過做者細節表 查詢做者叫金庸的電話號碼

    res = models.AuthorDetail.objects.filter(author__name='金庸').values('phone')
    print(res)  # <QuerySet [{'phone': '911'}]>

     ⑧ 從書這張表 查詢出版社爲南方出版社的全部圖書的名字和價格

    res = models.Book.objects.filter(publish__name='南方出版社').values('title', 'price')
    print(res)  # <QuerySet [{'title': '紅樓夢', 'price': Decimal('67.00')}, {'title': '西遊記', 'price': Decimal('87.00')}]>

    ⑨ 查詢東方出版社第一版的書的價格大於45的書

    res = models.Publish.objects.filter(name='東方出版社', book__price__gt=45).values('book__title', 'book__price')
    print(res)
    # <QuerySet [{'book__title': '三國志', 'book__price': Decimal('48.00')}]>

 4、聚合查詢和分組查詢

  • 聚合查詢
    aggregate(),它是QuerySet的一個終止子句,意思就是,會返回一個包含一些鍵值對的字典 
    鍵的名稱是聚合值的標識符,值是計算出來的聚合值。鍵的名稱是按照字段和聚合函數的名稱自動生成出來的
    用到的內置函數:
    from django.db.models import Avg, Sum, Max, Min, Count

     示例:

    from django.db.models import Avg,Sum,Max,Min,Count、
    查詢全部書籍的平均價格,最大價格,書的數量
    res = models.Book.objects.all().aggregate(Avg('price'), Max('price'), Count('title'))
    print(res)  # {'price__avg': 56.6, 'price__max': Decimal('87.00'), 'title__count': 5}

    能夠給聚合值指定一個名稱,用於使用該聚合值

    若是你想要爲聚合值指定一個名稱,能夠向聚合子句提供它。
    
    >>> models.Book.objects.aggregate(average_price=Avg('price'))
    {'average_price': 13.233333}
  • 分組查詢
    示例:
    統計每個出版社的出版的書平均價格
    res = models.Publish.objects.annotate(avg_price=Avg('book__price')).values('name', 'avg_price')
    print(res)
    # <QuerySet [{'name': '東方出版社', 'avg_price': 46.0}, {'name': '南方出版社', 'avg_price': 77.0}, {'name': '北方出版社', 'avg_price': 37.0}]>


    統計出每一個出版社買的最便宜的書的價格

    # <QuerySet [{'name': '東方出版社', 'avg_price': 46.0}, {'name': '南方出版社', 'avg_price': 77.0}, {'name': '北方出版社', 'avg_price': 37.0}]>
    res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price' )
    print(res)
    # <QuerySet [{'name': '東方出版社', 'min_price': Decimal('44.00')}, {'name': '南方出版社', 'min_price': Decimal('67.00')}, {'name': '北方出版社', 'min_price': Decimal('37.00')}]>

    統計不止一個做者的圖書

    res = models.Book.objects.annotate(author_num=Count('author')).filter(author_num__gt=1).values('title')
    print(res)
     # <QuerySet [{'title': '西廂記'}]>
相關文章
相關標籤/搜索