1. 程式人生 > >Django框架之ORM

Django框架之ORM

1,欄位和欄位的引數

1.1>ORM的概念:物件對映模型(Objects Relational Model)是一種為了解決面向物件和關係型資料庫存在的互不匹配的現象和技術,簡單的說,ORM是通過使用描述物件和資料庫之間對映的元資料,將程式中的物件自動持久化到關係資料庫中,ORM是連線業務邏輯和關係型資料庫的橋樑

1.2>ORM的由來

  在程式開發人員中,需要一邊寫業務邏輯部分的內容,有要一遍邊寫sql語句的增刪改查,這就造成程式開發人員的開發效率不夠高,錯誤漏洞會百出,sql語句特點是極其相似的或者重複的,這樣的背景下就衍生出來了ORM

1.3>ORM的優勢:

  ORE解決了物件和關係型資料庫的對映關係,類和表一一對應

  我們不用再去操作資料庫,直接操作類就好了,同過ORM把操作類轉化成操作資料庫

  提高了軟體開發人員的開發效率

1.4>ORM的劣勢

  在一定程度上降低了程式的執行效率

  ORM只能操作表,且在操作表的時候 也是有有限的操作,一些複雜的查詢依舊無法完成

  Mysql資料庫的操作技能的不到加強

1.5>總之:

  ORM是業務邏輯和資料庫之間的紐帶,他能完成一些簡單的,繁瑣的操作,一些特殊的問題還是無法完成,遇到特殊情況特殊處理

2,Django中的ORM

2.1>Django中ORM的配置

  1>手動建立一個數據庫

  2>在settings.py檔案中DATABASES 種設定 

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.mysql",  # 告訴Django用mysql資料庫
        "NAME": "db2",  # 指定資料庫的名字 
        "HOST": "127.0.0.1",  # 指定主機地址
        "PORT": 3306,  # 指定埠號
        "USER": "root",  # 指定用於型別
        "PASSWORD": "123456",  # 指定密碼
    }
}

  3>在和settings同級的__init__檔案中匯入pymysql

    import pymysql

    pymysql.install_as_MySQLdb()

  4>在新建的APP包中的models.py中建立類

  5>在Terminal中輸入變更命令

    python manage.py makemigrations  # 把models中的變更記錄到APP檔案下的migrations中,並且每次有變更就會新生成一個.py檔案,且每當新建立專案的時候,生成的py檔名是一樣的,它會預設是一個新的專案,如果使用了之前的資料庫和表,需要把migrations中的變更檔案copy過來

    python manage.py migrate  # 把變更的檔案轉化成mysql語句,去操作資料庫

  或者:在tool中找到run manage.py task

 

    輸入命令: makemigrations   

         migrate

  6>測試mysql資料庫

2.2>Model

  在Model中我們通過建立類來生成資料庫中的表

  類  <--------------------------->  表

  屬性    <--------------------------->  表中的欄位

  物件    <--------------------------->  資料行

2.3>常用欄位

  2.3.1>AutoField:自增的整形欄位,必填引數primary_key = True,則成為資料庫的主鍵,無該欄位是,Django會幫我們自動建立.注意,一個model中不能包含2個AutoField欄位

  2.3.2>IntegerField:表示是整數型別,數值範圍是+/-10(-2147483648~2147483647)

  2.3.3>CharField:對應資料庫中的varchar型別(不定長)需要指定最大的長度max_length引數

  2.3.4>DateField:日期型別,格式:YYYY-MM-DD,相當於python中的datetime.date(年:月:日)

      引數:auto_now:每次修改時,修改為當前時間,

        auto_now_add:新建立物件時自動添加當前日期時間,

        auto_now和auto_now_add是互斥的,不能同時設定,也可以不設定,手動的去修改

  2.3.5>DatetimeField:日期時間欄位,格式為YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ](年:月:日:時:分:秒)

 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)
        - 二進位制型別
View Code

 

 

在ORM中沒有char型別等,ORM值提供了有限的資料型別,有些型別需要我們自己定義

2.4>自定義一個char型別的欄位

  在APP下的model裡邊直接建立char類 ,並執行變更的命令

只要在models檔案中有變更就要執行變更命令,加入有1,2的選項就是新增欄位需要設定預設值

2.5>常見欄位的引數說明:

 1 欄位引數
 2 欄位引數,詳情可點選檢視官網。
 3 1
 4 2
 5 3
 6 4
 7 5
 8 6
 9 7
10 8
11 9
12 10
13 11
14 12
15 13
16 14
17 15
18 16
19 17
20 18
21 19
22 20
23 21
24 22
25 23
26 24
27 25
28 26
29 27
30 28
31 29
32 30
33 31
34 32
35 33
36 34
37 35
38 36
39 37
40 38
41 39
42 40
43     null                資料庫中欄位是否可以為空
44     db_column           資料庫中欄位的列名
45     default             資料庫中欄位的預設值
46     primary_key         資料庫中欄位是否為主鍵
47     db_index            資料庫中欄位是否可以建立索引
48     unique              資料庫中欄位是否可以建立唯一索引
49     unique_for_date     資料庫中欄位【日期】部分是否可以建立唯一索引
50     unique_for_month    資料庫中欄位【月】部分是否可以建立唯一索引
51     unique_for_year     資料庫中欄位【年】部分是否可以建立唯一索引
52  
53     verbose_name        Admin中顯示的欄位名稱
54     blank               Admin中是否允許使用者輸入為空
55     editable            Admin中是否可以編輯
56     help_text           Admin中該欄位的提示資訊
57     choices             Admin中顯示選擇框的內容,用不變動的資料放在記憶體中從而避免跨表操作
58                         如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)
59  
60     error_messages      自定義錯誤資訊(字典型別),從而定製想要顯示的錯誤資訊;
61                         字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
62                         如:{'null': "不能為空.", 'invalid': '格式錯誤'}
63  
64     validators          自定義錯誤驗證(列表型別),從而定製想要的驗證規則
65                         from django.core.validators import RegexValidator
66                         from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
67                         MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
68                         如:
69                             test = models.CharField(
70                                 max_length=32,
71                                 error_messages={
72                                     'c1': '優先錯資訊1',
73                                     'c2': '優先錯資訊2',
74                                     'c3': '優先錯資訊3',
75                                 },
76                                 validators=[
77                                     RegexValidator(regex='root_\d+', message='錯誤了', code='c1'),
78                                     RegexValidator(regex='root_112233\d+', message='又錯誤了', code='c2'),
79                                     EmailValidator(message='又錯誤了', code='c3'), ]
80                             )
81  
82 欄位引數
其它欄位的引數

2.6>表中Meta引數的說明

3,ORM查詢13種方法

import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day_67.settings")
    import django
    django.setup()
    from app01.models import Classroom, Teacher, Student, Person
    # for el in obj:
    #     print(el.name)
        # print(el.phone)
        # print(el.get_gender_display())
    # 1,all()查詢到該表的所有物件      --->物件列表
    # obj1 = Classroom.objects.all()
    # for el in obj:
    #     print(el.name)
    # 2,get  -->拿到的是一個物件
    # 查詢不到就會報錯,獲取多個也會報錯
    # obj2 = Classroom.objects.get(id=1)
    # print(obj2.name)
    # 3,filter,查詢所有滿足條件的物件   --->物件列表
    # obj3 = Classroom.objects.filter(name="python14脫產班")
    # for el in obj3:
    #     print(el.name)
    # 4,exclude 查詢出所有不滿足條件的物件    --->物件列表
    # obj5 = Classroom.objects.exclude(name="python14脫產班")
    # for el in obj5:
    #     print(el.id)
    # 5,value查詢出所有具體每一個物件的資料        ------->列表物件裡包含字典
    # obj6 = Classroom.objects.all().values()
    # for el in obj6:
    #     print(el)
    # 5.1value查詢出指定的物件的具體資訊         ----->物件列表裡包含字典
    # obj7 = Classroom.objects.all().values("id")
    # for el in obj7:
    #     print(el)
    # 6,value_list取具體的資料        ------>物件列表裡包含元組(只有value沒有key)
    # 沒有指定引數獲取的是鎖有欄位的資料
    # obj8 = Classroom.objects.all().values_list()
    # for el in obj8:
    #     print(el)
    # 指定引數,獲取指定欄位資料
    # obj9 = Classroom.objects.all().values_list("id","name")  # 獲取到的是元組
    # for el in obj9:
    #     print(el)
    # 7,order_by 排序,可以指定多個欄位
    # obj10 = Classroom.objects.all().order_by("-id")  # 先拿到所有物件,再排序
    # for el in obj10:
    #     print(el.id)
    # obj11 = Person.objects.all().order_by("age","-id")  # 在欄位前邊加符號就是倒序
    # for el in obj11:
    #     print(el.id)
    # obj12 = Classroom.objects.all().distinct("name")  # 也許列個物件完全一樣才能去重
    # for el in obj12:
    #     print(el)
    # 8,count()計數
    # obj13 = Classroom.objects.all().count()
    #     # print(obj13)
    #     # obj14 = Classroom.objects.filter(id=2).count()
    #     # print(obj14)
    # 9,first()當拿到的是一個 queryset物件的時候去first()是去第一個物件
    # obj15 = Classroom.objects.all()
    # print(obj15.first().name)
    # 10,first()當取到單一物件的時候就會報錯,必須獲得的是一個queryset
    # obj16 = Classroom.objects.get(id=3)
    # print(obj16.first())
    # 11,last()依舊
    # 12,exists()判斷是否返回布林值,只能判斷queryset物件列表
    obj17 = Classroom.objects.all().exists()
    print(obj17)
    '''
    返回物件列表的方法
    all()
    filter()
    exclude()
    order_by()
    reverse()
    distinct()
    values()  { }
    values_list()  ( )
    
    返回物件的方法
    get()
    first()
    last()
    create()
    
    返回布林值
    exists()
    
    返回數字
    coun()
    '''
ORM查詢的13種方法

 4,單表的查詢和雙下方法

import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day_67.settings")
    import django
    django.setup()
    from app01.models import Person, Classroom, Teacher, Student
    # obj1 = Person.objects.filter(id__gt=1)  # great than 大於
    # for el in obj1:
    #     print(el.name)
    # obj2 = Person.objects.filter(id__lt=2)  # less than 小於
    # for el in obj2:
    #     print(el.name)
    # obj3 = Person.objects.get(id__lt=2)  # 當用get的時候,返回儘可能縮小到一個物件才會生效
    # print(obj3.age,obj3.name)
    # obj4 = Person.objects.filter(id__gte=3)  # id值大於等於3的所有物件集合
    # for el in obj4:
    #     print(el.name)
    # obj5 = Person.objects.filter(id__lte=2)  # less than equal id值小於等於2
    # for el in obj5:
    #     print(el.name)
    # obj6 = Person.objects.filter(id__in=[3,5])  # id值等於3和5的物件集合
    # for el in obj6:
    #     print(el.id,el.name)
    # obj7 = Person.objects.filter(id__in=[3,10])  # 只包括id=3和id10
    # for el in obj7:
    #     print(el.id,el.name)
    # obj8 = Person.objects.filter(id__gte=1, id__lte=3)
    # for el in obj8:
    #     print(el.id,el.name)
    # obj9 = Person.objects.filter(id__range=[1,3])  # 範圍從1到3,左右都包括
    # for el in obj9:
    #     print(el.id,el.name)
    # obj10 = Person.objects.filter(name__contains="無")  # 中文也識別,包含contain 返回的是一個queryset
    # for el in obj10:
    #     print(el.id,el.name)
    # obj11 = Person.objects.filter(name__icontains="無")  # ignore contains 表示忽略大小寫
    #     # for el in obj11:
    #     #     print(el.id,el.name)
    # obj12 = Person.objects.filter(name__endswith="忌")  # 以什麼結尾 拿到的是queryset
    # for el in obj12:
    #     print(el.id,el.name)
    # obj13 = Person.objects.filter(name__startswith="張")  # 以什麼開始
    # print(obj13)
    # obj14 = Person.objects.filter(birth__year=2018)
    #     #     # print(obj14[0].id,obj14[0].name)
    obj15 = Person.objects.filter(birth__year=1997)
    obj16 = obj15.filter(birth__month=10)
    print(obj16,type(obj16))
    obj17 = Person.objects
    print(obj17,type(obj17))
    obj18 = Person.objects.filter(birth__month=1997-10)
    print(obj18,type(obj18))
    obj19 = Person.objects.filter(birth__year=1997,birth__month=10,birth__day=11)
    print(obj19,type(obj19))

5,外來鍵的查詢

 

import os

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day_67.settings")
    import django

    django.setup()
    from app01.models import Classroom, Student, Teacher

    # 基於物件的查詢(多對一)
    # student_obj = Student.objects.get(id=2)
    # print(student_obj.name)
    # print(student_obj.id)
    # print(student_obj.classroom)
    # print(student_obj.classroom_id)
    # 正想查詢
    # obj_id = Classroom.objects.get(name="python15脫產班").id
    # print(obj_id)  # 先找到班級的id,隨便拿一個有效的id值
    # obj1 = Student.objects.filter(classroom_id= obj_id)  # 匹配班級id用的
    # print(obj1[0].id,obj1[0].name)
    # 雙下方法可以使用values出來的是一個字典根據字典的key值去拿到值
    # obj2 = Student.objects.all().values("id","name","classroom_id","classroom__name")
    # print(obj2)
    # value_list拿到是一個元組用索引去取某一個值
    # obj4 = Student.objects.all().values_list("id", "name", "classroom__name")
    # for el in obj4:
    #     print(el[0],el[1],el[2])
    # for el in obj2:
    #     print(el["id"],el["name"],el["classroom__name"])
    # 可以用雙下方法來通過外來鍵直接查詢到關聯表的所有資訊
    # obj3 = Student.objects.filter(classroom__name="python14脫產班")
    # for el in obj3:
    #     print(el.name,el.id,el.classroom__name)

    # 反向查詢
    # obj5 = Classroom.objects.get(id=3)
    #     print(obj5)
    #     #     print(obj5.student_set.all())
    #     #     obj6 = obj5.student_set.all()  # student_set是在model中的外來鍵relate_name = "student_set"預設設定
    #     #     for el in obj6:
    #     #         print(el.name,el.id)
    # obj7 = Classroom.objects.filter(id=Student.objects.get(name="康琛").classroom_id)
    # for el in obj7:
    #     print(el.id,el.name)
    obj8 = Classroom.objects.get(id=1)  # 現獲取到班級id=4的物件
    # obj8.student_set.set(Student.objects.filter(id__in=[3,4]))  # 通過student_set方向查詢到id =3和id=4的物件,並修改外來鍵關聯為1
    # obj8.student_set.add(Student.objects.filter(id__in=[5,6]))  # 追加,
    # obj8.student_set.remove(Student.objects.filter(id__in=[5,6]))  # 需要在model中的該欄位設定為可以為空

小結:正向查詢的時候,通過外來鍵或的關聯表的相應物件,可以同過外來鍵查詢到關聯表的所有屬性

  反向查詢的時候就不能通過外來鍵查詢了,1>先要獲取到班級的物件,2>再使用student_set(relate_name= "students"時就換成students)這次可以獲取到學生的所有物件的到是一個queryset物件列表

6,多對多查詢

import os
if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day_67.settings")
    import django
    django.setup()
    from app01.models import Teacher, Classroom, Student
    # 獲取到教師的id=3的物件
    obj1 =  Teacher.objects.get(id=3)
    print(obj1.name)

    # obj1.students.add(*Student.objects.all())
    # obj1.students.add(1,2,3)  # add是有資料表中有了資料就不加了,沒有才會增加
    # 當新增有重複的資料.重複的不會再增加,原先沒有的才會增加
    # obj1.students.set([1,2,3,4])  # 也是給多對多表新增學生,當資料存在的時候,也就不會再設定了,沒有就會設定
    # obj1.students.remove(*Student.objects.all())  # 移除教師物件id=3的所有資料
    # obj1.students.clear()  # 把獲取到教室id=3物件的所有資料刪除
    obj1.students.create(name="雪雪",classroom_id=7)    
    # 這個可以指定已經存在的資料,也可以不存在的資料,不論存在與否,都會在第三張表新增資料,且學生表也會增加資料
    # classroom_id也會新增,只有,最開始的班級表不會新增資料

小結:多對多表的修改只是在第三張表的操作,遵循增刪改查

  增:add()有就會覆蓋(id值是變化的),沒有就會增加

   create(引數)建立該物件的對應關係資料,關聯id可以存在,也可以不存在,當不存在的時候,在學生表也會生成相應的資料,只有班級表不會增加資料

  刪:remove(可以指定引數),刪除第三張表的所有物件,原表並沒有發生變化

   clear()括號不加引數引數,清空該物件與關聯的所有資料

  改:set(),會把獲取到的物件的原來的值設定成set裡邊的一一對應關係 

  查:.all()查詢所有該對應關係的資料