ORM 應用詳解

  • 2019 年 10 月 8 日
  • 筆記

目錄

ORM 概念

對象關係映射(Object Relational Mapping,簡稱ORM)模式是一種為了解決面向對象與關係數據庫存在的互不匹配的現象的技術。 簡單的說,ORM是通過使用描述對象和數據庫之間映射的元數據,將程序中的對象自動持久化到關係數據庫中。 ORM在業務邏輯層和數據庫層之間充當了橋樑的作用。

ORM 由來

讓我們從O/R開始。字母O起源於"對象"(Object),而R則來自於"關係"(Relational)。 幾乎所有的軟件開發過程中都會涉及到對象和關係數據庫。在用戶層面和業務邏輯層面,我們是面向對象的。 當對象的信息發生變化的時候,我們就需要把對象的信息保存在關係數據庫中。 按照之前的方式來進行開發就會出現程序員會在自己的業務邏輯代碼中夾雜很多SQL語句用來增加、讀取、修改、刪除相關數據, 而這些代碼通常都是極其相似或者重複的

ORM 優勢

ORM解決的主要問題是對象和關係的映射。它通常將一個類和一張表一一對應,類的每個實例對應表中的一條記錄, 類的每個屬性對應表中的每個字段。 ORM提供了對數據庫的映射,不用直接編寫SQL代碼,只需操作對象就能對數據庫操作數據。 讓軟件開發人員專註於業務邏輯的處理,提高了開發效率。

ORM 劣勢

ORM的缺點是會在一定程度上犧牲程序的執行效率。 ORM的操作是有限的,也就是ORM定義好的操作是可以完成的,一些複雜的查詢操作是完成不了。 ORM用多了SQL語句就不會寫了,關係數據庫相關技能退化…

ORM 總結

ORM只是一種工具,工具確實能解決一些重複,簡單的勞動。這是不可否認的。 但我們不能指望某個工具能一勞永逸地解決所有問題,一些特殊問題還是需要特殊處理的。 但是在整個軟件開發過程中需要特殊處理的情況應該都是很少的,否則所謂的工具也就失去了它存在的意義。

ORM 與 DB 的對應關係

ORM 面向對象和關係型數據庫的一種映射,通過操作對象的方式操作數據庫數據,不支持對庫的操作,只能操作表 對應關係: 類 –> 表 對象 –> 數據行 屬性 –> 字段

Model 模塊 在Django中model是你數據的單一、明確的信息來源。它包含了你存儲的數據的重要字段和行為。通常, 一個模型(model)映射到一個數據庫表。 基本情況: 每個模型都是一個Python類,它是django.db.models.Model的子類。 模型的每個屬性都代表一個數據庫字段。 綜上所述,Django為您提供了一個自動生成的數據庫訪問API,詳詢官方文檔https://docs.djangoproject.com/en/1.11/topics/db/queries/

ORM 操作

# 獲取表中所有的數據  ret = models.User.objects.all()  # QuerySet 對象列表  【對象】  for i in ret:      print(i.xxx,i.sss)  #xxx,sss 填寫要獲取的字段    # 獲取一個對象(有且唯一)  obj = models.User.objects.get(username='gandan')   # 獲取不到或者獲取到多個對象會報錯  print(obj.xxx,obj.sss)  ##xxx,sss 填寫要獲取的字段    # 獲取滿足條件的對象  ret = models.User.objects.filter(username='gandan1',password='dasb')  # QuerySet 對象列表 #獲取不到返回空列表  print(ret[0].xxx,ret[0].sss) #可以通過索引與循環取值

ORM 簡單 增刪改查 操作

#創建記錄方式1  student_obj = models.Student(          name='gandan',          age=18,      )      student_obj.save()    #創建記錄方式2  new_obj = models.Student.objects.create(name='tandan',age=17) #也可以寫成 **{'name':'xx'}      print(new_obj)  #Student object --  model對象      print(new_obj.name)  #點屬性,可以獲取對應字段的數據      #創建記錄方式3 批量創建  objs_list = []  for i in range(100,3000000):      obj = models.Student(          name='goudan',          age = 16,      )      objs_list.append(obj)  models.Student.objects.bulk_create(objs_list)      #創建方法4 update_or_create 有就更新,沒有就創建      models.Student.objects.update_or_create(          name='胖妞',          defaults={              'age':18,          }      )    #添加日期數據      import datetime      current_date = datetime.datetime.now()      # print(current_date) #2019-07-19 12:19:26.385654      # 兩種方式      # models.Brithday.objects.create(name='鋼蛋',date=current_date)      # models.Brithday.objects.create(name='鐵蛋',date='2000-12-08')
#刪除  delete  queryset 和model對象都可以調用      models.Student.objects.get(id=3).delete()  #model對象來調用的delete方法      models.Student.objects.filter(name='胖妞').delete()      models.Student.objects.all().delete()      #刪除所有
更新 update方法 model對象不能調用更新方法 報錯信息'Student' object has no attribute 'update'      只能queryset調用,如果      models.Student.objects.get(name='gandan').update(age=38)      models.Student.objects.filter(name='tiandan').update(age=38)
  • 簡單查
#查詢所有的數據  .all方法 返回的是queryset集合      all_objs = models.Student.objects.all()      #<QuerySet [<Student: Student object>, <Student: Student object>, <Student: Student object>]> -- 類似於列表  --  queryset集合      #條件查詢  .filter方法,返回的也是queryset集合,查詢不到內容,不會 報錯,返回一個<QuerySet []>空的queryset      objs = models.Student.objects.filter(id=2)  #找id為2的那條記錄      print(objs) #<QuerySet [<Student: xiaozhuang>]>      objs = models.Student.objects.filter(name='dazhaung')      print(objs) #<QuerySet [<Student: dazhaung>]>    #條件查詢 get方法,返回的是model對象,而且get方法有且必須只有1個結果      obj = models.Student.objects.get(id=3)  #找id為3的那條記錄      print(obj)  #xiaozhuang2
#增  obj = models.Publisher.objects.create(name='xxx')    obj = models.Publisher(name='xxx')# 存在在內存中的對象  obj.save() # 提交到數據庫中  新增  """  book_obj = Book.objects.create(name='xxx',pub=出版社對象)  book_obj = Book.objects.create(name='xxx',pub_id=出版社對象的id)  book_obj = Book(name='xxx',pub=出版社對象)  book_obj.save() #注意區別 pub與pub_id  """"  #刪  obj_list = models.Publisher.objects.filter(pk=pk)  obj_list.delete()    # Book.objects.filter(pk=1).delete()    obj = models.Publisher.objects.get(pk=pk)  obj.delete()    #改  obj = models.Publisher.objects.get(pk=1)  obj.name = publisher_name  #只是在內存中操作  obj.save()  # 保存數據到數據庫中    #查  models.Publisher.objects.all()#查詢所有的數據 queryset 對象列表  models.Publisher.objects.get(name='xxxx')#對象,獲取不到或者獲取到多個就報錯  models.Publisher.objects.filter(name='xxxx')#獲取滿足條件的所有的對象   queryset  對象列表   

ORM 常用字段

  • AutoField 主鍵
自增的整形字段,必填參數primary_key=True,則成為數據庫的主鍵。無該字段時,django自動創建。    一個model不能有兩個AutoField字段。  #如  pid = models.AutoField(primary_key=True)
  • ForeignKey 外鍵
    consultant = models.ForeignKey('UserProfile', verbose_name="銷售", blank=True, null=True, on_delete=models.CASCADE)  #參數1關聯表,參數2顯示中文 on_delete=models.CASCADE 級聯刪除
  • IntegerField 整數
一個整數類型。數值的範圍是 -2147483648 ~ 2147483647。  #如  age = models.IntegerField()
  • CharField 字符串
字符類型,必須提供max_length參數。max_length表示字符的長度。  #小提示 電話號建議使用CharField存儲 節省資源  #如  name = models.CharField(max_length=32) 
  • BooleanField 布爾值
BooleanField(True/False) #數據庫存為True--> 1 False--> 0
  • TextField 文本
TextField()#比較長的字符串
  • DateTimeField 日期時間
  • auto_now_add=True
# 新增數據的時候會自動保存當前的時間  birth = models.DateTimeField(auto_now_add=True)    #默認是 UTC時間 一般不跨時區的應用,可以不使用時區,即在settings.py設置   USE_TZ = False
  • auto_now=True
 # 新增、修改當前數據的時候會自動保存當前的時間  birth = models.DateTimeField(auto_now=True)
  • DecimalField 十進制的小數
  • max_digits 小數總長度
  • decimal_places 小數位長度
max_digits       小數總長度   5  decimal_places   小數位長度   2  #那麼整數位長度就為3

ORM 查看數據庫與ORM字段對應關係

ORM 其他字段

  • 字段類型,詳情可點擊查詢官網
    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中提供驗證機制          - e_mail = models.EmailField(max_length=255, unique=True)        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)          - 二進制類型

自定義字段

  • 有些字段 django沒有提供,就需要我們自定來完成
  • 自定義一個char類型字段:
class MyCharField(models.Field):#自定義的char類型的字段類        def __init__(self, max_length, *args, **kwargs):          self.max_length = max_length          super(MyCharField, self).__init__(max_length= max_length,*args, **kwargs)        def db_type(self, connection):#限定生成數據庫表的字段類型為char,長度為max_length指定的值          return 'char(%s)'%self.max_length
  • 使用自定義char類型字段:
class Class(models.Model):        id = models.AutoField(primary_key=True)      title = models.CharField(max_length=25)      cname = MyCharField(max_length=25)# 使用自定義的char類型的字段
  • 自定義一個二進制字段,以及Django字段與數據庫字段類型的對應關係。
class UnsignedIntegerField(models.IntegerField):      def db_type(self, connection):          return 'integer UNSIGNED'    # PS: 返回值為字段在數據庫中的屬性。  # Django字段與數據庫字段類型對應關係如下:      '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)',

字段參數

  • 字段參數,詳情可點擊查看官網
    null                數據庫中字段是否可以為空  #注意 數據庫的 null 和''空字符是兩種情況      db_column           數據庫中字段的列名      default             數據庫中字段的默認值      primary_key         數據庫中字段是否為主鍵      db_index            數據庫中字段是否可以建立索引      unique              數據庫中字段是否可以建立唯一索引      unique_for_date     數據庫中字段【日期】部分是否可以建立唯一索引      unique_for_month    數據庫中字段【月】部分是否可以建立唯一索引      unique_for_year     數據庫中字段【年】部分是否可以建立唯一索引        verbose_name        Admin中顯示的字段名稱      blank               Admin中是否允許用戶輸入為空      editable            Admin中是否可以編輯      help_text           Admin中該字段的提示信息      choices             Admin中顯示選擇框的內容,用不變動的數據放在內存中從而避免跨表操作                          如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)        error_messages      自定義錯誤信息(字典類型),從而定製想要顯示的錯誤信息;                          字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date                          如:{'null': "不能為空.", 'invalid': '格式錯誤'}        validators          自定義錯誤驗證(列表類型),從而定製想要的驗證規則                        from django.core.validators import RegexValidator                         from django.core.validators import EmailValidator,URLValidator,DecimalValidator,MaxLengthValidator,  MinLengthValidator,MaxValueValidator,MinValueValidator                          如:                              test = models.CharField(                                  max_length=32,                                  error_messages={                                      'c1': '優先錯信息1',                                      'c2': '優先錯信息2',                                      'c3': '優先錯信息3',                                  },                                  validators=[                                      RegexValidator(regex='root_d+', message='錯誤了', code='c1'),                                      RegexValidator(regex='root_112233d+', message='又錯誤了', code='c2'),                                      EmailValidator(message='又錯誤了', code='c3'), ]                              )字段參數

Model Meta參數

  • 這個不是很常用,如果你有特殊需要可以使用。詳情點擊查看官網
class UserInfo(models.Model):      nid = models.AutoField(primary_key=True)      username = models.CharField(max_length=32)        class Meta:          # 數據庫中生成的表名稱 默認 app名稱 + 下劃線 + 類名          db_table = "table_name"            # admin中顯示的表名稱          verbose_name = '個人信息'            # verbose_name加s          verbose_name_plural = '所有用戶信息'            # 聯合索引          index_together = [              ("pub_date", "deadline"),   # 應為兩個存在的字段          ]            # 聯合唯一索引          unique_together = (("driver", "restaurant"),)   # 應為兩個存在的字段

常用13中查詢(必會)

<1> all():#查詢所有結果 ——》 QuerySet 對象列表 想要的到方便查看的結果,可以使用__str___方法加在ORM的表類後邊 如:      """      def __str__(self):          return '{} - {}'.format(self.pk, self.name)      """      ret = models.Person.objects.all()      <2> get(**kwargs):#獲取滿足條件的一個數據 ——》 對象  獲取不到或者多個都報錯      ret = models.Person.objects.get(pk=1)    <3> filter(**kwargs):#獲取滿足條件的所有數據   ——》 QuerySet   對象列表 沒有返回None      ret = models.Person.objects.filter(pk=1)    <4> exclude(**kwargs):#獲取不滿足條件的所有數據   ——》 QuerySet   對象列表      ret = models.Person.objects.exclude(pk=1)    <5> values(*field):#返回一個ValueQuerySet——一個特殊的QuerySet,運行後得到的並不是一系列model的實例化對象,  而是一個可迭代的字典序列,拿到對象指定的字段和字段的值    QuerySet  [ {} ,{} ]      ret = models.Person.objects.values('pid','name')  # values()  拿到對象所有的字段和字段的值    QuerySet  [ {} ,{} ]    <6> values_list():#它與values()非常相似,它返回的是一個元組序列,values()返回的是一個字典序列,拿到對象所有的字段的值  QuerySet  [ () ,() ] 用索引取值      values_list(字段)#拿到對象指定的字段的值      QuerySet  [ {} ,{} ]      ret = models.Person.objects.values_list('name','pid')    <7> order_by(字段):#對查詢結果排序默認升序 123      order_by(-字段):#字段前邊加減號排序方式改為降序321      order_by(字段1,-字段2):#先按照字段1升序排序,再按照字段2降序排序,如果字段1值有相等的,將按照字段2排序大的在前面      ret = models.Person.objects.all().order_by('age','-pid')    <8> reverse():#(反向排序 只能對已經排序的QuerySet進行反轉)      ret = models.Person.objects.all().order_by('pk').reverse()    <9> distinct():#(去重  完全相同的內容才能去重)從返回結果中剔除重複紀錄(如果你查詢跨越多個表,可能在計算QuerySet時得到重複的結果。  此時可以使用distinct(),注意只有在PostgreSQL中支持按字段去重。)      ret = models.Person.objects.values('age').distinct()    <10> count():#(計數)返回數據庫中匹配查詢(QuerySet)的對象數量。      ret = models.Person.objects.all().count()      #len(models.Person.objects.all())效果一樣    <11> first():#(取第一元素   沒有元素 None)返回第一條記錄      ret = models.Person.objects.filter(pk=1).values().first()    <12> last():#(取最後一元素   沒有元素 None)返回最後一條記錄      ret = models.Person.objects.filter(pk=1).values().last()    <13> exists():#(查詢的數據是否存在)如果QuerySet包含數據,就返回True,否則返回False      ret = models.Person.objects.filter(pk=1000).exists()
#返回QuerySet對象的方法有  all()    filter()    exclude()    order_by()    reverse()    distinct()    #特殊的QuerySet  values()       返回一個可迭代的字典序列    values_list() 返回一個可迭代的元祖序列    #返回具體對象的  get()    first()    last()    #返回布爾值的方法有:  exists()    #返回數字的方法有  count()

單表查詢的雙下劃線應用

import os    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm_practice.settings")  import django    django.setup()    from app01 import models    ret = models.Person.objects.filter(pk__gt=1)   # gt  greater than   大於  ret = models.Person.objects.filter(pk__lt=3)   # lt  less than   小於  ret = models.Person.objects.filter(pk__gte=1)   # gte  greater than   equal    大於等於  ret = models.Person.objects.filter(pk__lte=3)   # lte  less than  equal  小於等於  ret = models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 獲取id大於1 且 小於10的值      ret = models.Person.objects.filter(pk__range=[2,3])   # range  範圍  ret = models.Person.objects.filter(pk__in=[1,3,10,100])   # in  成員判斷  ret = models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in    ret = models.Person.objects.filter(name__contains='A')#name包含A的  ret = models.Person.objects.filter(name__icontains='A')   # 忽略大小寫    ret = models.Person.objects.filter(name__startswith='a')# 以什麼開頭  ret = models.Person.objects.filter(name__istartswith='A')#以什麼開頭不區分大小寫      ret = models.Person.objects.filter(name__endswith='a')#以什麼結尾  ret = models.Person.objects.filter(name__iendswith='I')#不區分大小寫    ret  = models.Person.objects.filter(birth__year='2019')#查字段年份  ret  = models.Person.objects.filter(birth__contains='2018-06-24')#查月份只能查年在查月  查日同理    ret  = models.Person.objects.filter(phone__isnull=False)#查看phone字段值不為null的(空字符不等於null哦)

ForeignKey外鍵的操作

  • 正向查找
  • 對象查找(跨表)
  • 示例:
#語法:  #對象.關聯字段.字段  book_obj = models.Book.objects.first()  # 第一本書對象  print(book_obj.publisher)  # 得到這本書關聯的出版社對象  print(book_obj.publisher.name)  # 得到出版社對象的名稱
  • 字段查找(跨表)
  • 示例:
#語法:  #關聯字段__字段  print(models.Book.objects.values_list("publisher__name"))
  • 反向操作
  • 對象查找
  • 示例:
#語法:  #obj.表名_set    publisher_obj = models.Publisher.objects.first()  # 找到第一個出版社對象  books = publisher_obj.book_set.all()  # 找到第一個出版社出版的所有書  titles = books.values_list("title")  # 找到第一個出版社出版的所有書的書名
  • 字段查找
  • 示例:
#語法:  #表名__字段  titles = models.Publisher.objects.values_list("book__title")
  • related_name 反向查給小寫表名起別名
class Publisher(models.Model):      name = models.CharField(max_length=32, verbose_name="名稱")        def __str__(self):          return self.name      class Book(models.Model):      title = models.CharField(max_length=32)      pub = models.ForeignKey(Publisher, related_name='books',related_query_name='xxx',on_delete=models.CASCADE)        def __str__(self):          return self.title    基於對象查詢    正向    book_obj.pub    ——》  所關聯的對象    book_obj.pub_id    ——》  所關聯的對象id    反向    沒有指定related_name    pub_obj.book_set     ——》  關係管理對象    (類名小寫_set)    pub_obj.book_set.all()   ——》  所關聯的所有對象    指定related_name='books'    pub_obj.books   ——》  關係管理對象    (類名小寫_set)    pub_obj.books.all()   ——》  所關聯的所有對象        基於字段查詢    models.Book.objects.filter(pub__name='xxxxx')        沒有指定related_name    models.Publisher.objects.filter(book__title='xxxxx')    指定related_name=『books』    models.Publisher.objects.filter(books__title='xxxxx')    指定related_query_name='book『    models.Publisher.objects.filter(book__title='xxxxx')

多對多

"關聯管理器"是在一對多或者多對多的關聯上下文中使用的管理器。  它存在於下面兩種情況:      1 外鍵關係的反向查詢      2 多對多關聯關係  簡單來說就是當 點後面的對象 可能存在多個的時候就可以使用以下的方法。
  • 方法
  • all()
# all()  所關聯的所有的對象    # print(mjj.books.all())  # set  設置多對多的關係    [id,id]    [ 對象,對象 ]  # mjj.books.set([1,2])  # mjj.books.set(models.Book.objects.filter(pk__in=[1,2,3]))
  • create()
#創建一個新的對象,保存對象,並將它添加到關聯對象集之中,返回新創建的對象。  import datetime  models.Author.objects.first().book_set.create(title="python從入門到放棄", publish_date=datetime.date.today())    # create()  # obj = mjj.books.create(title='葵花寶典',pub_id=1)  # print(obj)  # book__obj = models.Book.objects.get(pk=1)  # obj = book__obj.authors.create(name='taibai')
  • add()
  • 把指定的model對象添加到關聯對象集中。
#添加對象  author_objs = models.Author.objects.filter(id__lt=3)  models.Book.objects.first().authors.add(*author_objs)    # add  添加多對多的關係   (id,id)   (對象,對象)  # mjj.books.add(4,5)  # mjj.books.add(*models.Book.objects.filter(pk__in=[4,5]))
  • 添加id
models.Book.objects.first().authors.add(*[1, 2])
  • set()
#更新model對象的關聯對象。  book_obj = models.Book.objects.first()  book_obj.authors.set([2, 3])
  • remove()
#從關聯對象集中移除執行的model對象  book_obj = models.Book.objects.first()  book_obj.authors.remove(3)    # remove 刪除多對多的關係  (id,id)   (對象,對象)  # mjj.books.remove(4,5)  # mjj.books.remove(*models.Book.objects.filter(pk__in=[4,5]))
  • clear()
#從關聯對象集中移除一切對象。  book_obj = models.Book.objects.first()  book_obj.authors.clear()    # clear()   清除所有的多對多關係  # mjj.books.clear()
  • 注意
    • 對於ForeignKey對象,clear()和remove()方法僅在null=True時存在
  • 舉個例子:
  • ForeignKey字段沒設置null=True時
class Book(models.Model):      title = models.CharField(max_length=32)      publisher = models.ForeignKey(to=Publisher)
  • 沒有clear()和remove()方法
>>> models.Publisher.objects.first().book_set.clear()  Traceback (most recent call last):    File "<input>", line 1, in <module>  AttributeError: 'RelatedManager' object has no attribute 'clear'
  • 當ForeignKey字段設置null=True時
class Book(models.Model):      name = models.CharField(max_length=32)      publisher = models.ForeignKey(to=Class, null=True)
  • 此時就有clear()和remove()方法:
models.Publisher.objects.first().book_set.clear()
  • 注意:
對於所有類型的關聯字段,add()、create()、remove()和clear(),set()都會馬上更新數據庫。換句話說,在關聯的任何一端,  都不需要再調用save()方法。

聚合(aggregate)查詢和分組(annotate)查詢

annotate看上去和aggregate很像,反正英語盲的人基本很難區分這兩個孿生仔。    實際上,annotate和aggregate用法基本一樣,只是返回結果不同。    annotate返回一個QuerySet數據查詢對象;    aggregate返回一個dict字典。
  • 聚合 aggregate
aggregate()是QuerySet 的一個終止子句,意思是說,它返回一個包含一些鍵值對的字典。    鍵的名稱是聚合值的標識符,值是計算出來的聚合值。鍵的名稱是按照字段和聚合函數的名稱自動生成出來的。    用到的內置函數:    from django.db.models import Avg, Sum, Max, Min, Count  示例:    >>> from django.db.models import Avg, Sum, Max, Min, Count  >>> models.Book.objects.all().aggregate(Avg("price"))  {'price__avg': 13.233333}  如果你想要為聚合值指定一個名稱,可以向聚合子句提供它。    >>> models.Book.objects.aggregate(average_price=Avg('price'))  {'average_price': 13.233333}  如果你希望生成不止一個聚合,你可以向aggregate()子句中添加另一個參數。所以,如果你也想知道所有圖書價格的最大值和最小值,  可以這樣查詢:    >>> models.Book.objects.all().aggregate(Avg("price"), Max("price"), Min("price"))  {'price__avg': 13.233333, 'price__max': Decimal('19.90'), 'price__min': Decimal('9.90')}    總結,aggregate是一種統計拓展方法。aggregate得到結果是一個字典,而且它只能針對一個字段統計計算。
  • 分組 annotate
  • 假設現在有一張公司職員表 employee

id

name

age

salary

province

dept

1

張三

23

2000

河北

財務

2

李四

34

4000

山東

人事

3

王五

45

6000

北京

人事

4

趙六

56

8000

南京

教學部

#我們使用原生SQL語句,按照部門分組求平均工資:  select dept,AVG(salary) from employee group by dept;  ORM查詢:    from django.db.models import Avg  Employee.objects.values("dept").annotate(avg=Avg("salary").values("dept", "avg")
  • 連表查詢的分組
  • employee

id

name

age

salary

province

dept_id

1

張三

23

2000

河北

1

2

李四

34

4000

山東

2

3

王五

45

6000

北京

2

4

趙六

56

8000

南京

3

  • dept

id

name

1

財務部

2

人事部

3

教學部

4

行政部

SQL查詢:    select dept.name,AVG(salary) from employee inner join dept on (employee.dept_id=dept.id) group by dept_id;  ORM查詢:    from django.db.models import Avg  models.Dept.objects.annotate(avg=Avg("employee__salary")).values("name", "avg")

F查詢 和 Q查詢

  • F 查詢 (針對自己單表中字段的比較和處理)
在上面所有的例子中,我們構造的過濾器都只是將字段值與某個常量做比較。如果我們要對兩個字段的值做比較,那該怎麼做呢?    Django 提供 F() 來做這樣的比較。F() 的實例可以在查詢中引用字段,來比較同一個 model 實例中兩個不同字段的值。    示例1:    查詢評論數大於收藏數的書籍    from django.db.models import F  models.Book.objects.filter(commnet_num__gt=F('keep_num'))  Django 支持 F() 對象之間以及 F() 對象和常數之間的加減乘除和取模的操作。    models.Book.objects.filter(commnet_num__lt=F('keep_num')*2)  修改操作也可以使用F函數,比如將每一本書的價格提高30元    models.Book.objects.all().update(price=F("price")+30)  引申:    如果要修改char字段咋辦?    如:把所有書名後面加上(第一版)    >>> from django.db.models.functions import Concat  >>> from django.db.models import Value  >>> models.Book.objects.all().update(title=Concat(F("title"), Value("("), Value("第一版"), Value(")")))
  • Q 查詢
  • |(或) &(與) ~(非)
filter() 等方法中的關鍵字參數查詢都是一起進行「AND」 的。 如果你需要執行更複雜的查詢(例如OR語句),你可以使用Q對象。    示例1:    查詢作者名是小仙女或小魔女的    models.Book.objects.filter(Q(authors__name="小仙女")|Q(authors__name="小魔女"))  你可以組合& 和|  操作符以及使用括號進行分組來編寫任意複雜的Q 對象。同時,Q 對象可以使用~ 操作符取反,這允許組合正常的查詢和取反(NOT) 查詢。    示例:查詢作者名字是小仙女並且不是2018年出版的書的書名。    >>> models.Book.objects.filter(Q(author__name="小仙女") & ~Q(publish_date__year=2018)).values_list("title")  <QuerySet [('番茄物語',)]>  查詢函數可以混合使用Q 對象和關鍵字參數。所有提供給查詢函數的參數(關鍵字參數或Q 對象)都將"AND」在一起。但是,如果出現Q 對象,  它必須位於所有關鍵字參數的前面。    例如:查詢出版年份是2017或2018,書名中帶物語的所有書。    >>> models.Book.objects.filter(Q(publish_date__year=2018) | Q(publish_date__year=2017), title__icontains="物語")  <QuerySet [<Book: 番茄物語>, <Book: 香蕉物語>, <Book: 橘子物語>]>

事務

  • 事務的四大特性 原子性 持久行 一致性 隔離性
  • 局部開啟事務
#是數據庫運行的最基本單位,原子型操作,保證操作的完整性,如:實際生活中銀行轉賬,先從轉賬者賬戶扣除200元,再往收款賬戶存入200元,實際上就是數據庫的操作,  事務就是保證這個操作的完整性,比如轉賬中間出現了問題,事務會把數據回滾到操作之前的狀態.    #事務正確書寫格式  from django.db import transaction    try:      with transaction.atomic():          # 進行一系列的ORM操作          models.Publisher.objects.create(name='xxxxx')          models.Publisher.objects.create(name='xxx22')  except Exception as e :      print(e)  #注意要是第一條數據已經寫入成功,在寫入第二條出現錯誤,事務會把第一條寫入信息刪除,並拋出異常提示,數據庫是有記錄的,id主鍵數會加1,但是沒有數據    
  • 錯誤寫法導致事務無效
  • 全局開啟事務
"""  在Web應用中,常用的事務處理方式是將每個請求都包裹在一個事務中。這個功能使用起來非常簡單,  你只需要將它的配置項ATOMIC_REQUESTS設置為True。    它是這樣工作的:當有請求過來時,Django會在調用視圖方法前開啟一個事務。如果請求卻正確處理並正確返回了結果,  Django就會提交該事務。否則,Django會回滾該事務。  """
DATABASES = {      'default': {          'ENGINE': 'django.db.backends.mysql',          'NAME': 'mxshop',          'HOST': '127.0.0.1',          'PORT': '3306',          'USER': 'root',          'PASSWORD': '123',          'OPTIONS': {              "init_command": "SET default_storage_engine='INNODB'",         #'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", #配置開啟嚴格sql模式              }          "ATOMIC_REQUESTS": True, #全局開啟事務,綁定的是http請求響應整個過程          "AUTOCOMMIT":False, #全局取消自動提交,慎用      },    'other':{      'ENGINE': 'django.db.backends.mysql',              ......    } #還可以配置其他數據庫  }    #上面這種方式是統一個http請求對應的所有sql都放在一個事務中執行(要麼所有都成功,要麼所有都失敗)。  是全局性的配置, 如果要對某個http請求放水(然後自定義事務),可以用non_atomic_requests修飾器,那麼他就不受事務的管控了
from django.db import transaction    @transaction.non_atomic_requests  def my_view(request):      do_stuff()    @transaction.non_atomic_requests(using='other')  def my_other_view(request):      do_stuff_on_the_other_database()
"""  但是Django 文檔中說,不推薦這麼做。因為如果將事務跟 HTTP 請求綁定到一起的時,  然而view 是依賴於應用程序對數據庫的查詢語句效率和數據庫當前的鎖競爭情況。當流量上來的時候,性能會有影響,知道一下就行了    所以推薦用下面這種方式,通過 transaction.atomic 來更加明確的控制事務。atomic允許我們在執行代碼塊時,在數據庫層面提供原子性保證。   如果代碼塊成功完成, 相應的變化會被提交到數據庫進行commit;如果執行期間遇到異常,則會將該段代碼所涉及的所有更改回滾。  """
  • 注意
"""  還要注意:如果你配置了全局的事務,它和局部事務可能會產生衝突,你可能會發現你局部的事務完成之後,  如果你的函數裏面其他的sql除了問題,也就是沒在這個上下文管理器的局部事務包裹範圍內的函數裏面的其他的sql出現了問題,  你的局部事務也是提交不上的,因為全局會回滾這個請求和響應所涉及到的所有的sql,所以還是建議以後的項目盡量不要配置全局的事務,  通過局部事務來搞定,當然了,看你們的業務場景。  """

QuerySet方法大全

##################################################################  # PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #  ##################################################################    def all(self)      # 獲取所有的數據對象    def filter(self, *args, **kwargs)      # 條件查詢      # 條件可以是:參數,字典,Q    def exclude(self, *args, **kwargs)      # 條件查詢      # 條件可以是:參數,字典,Q    def select_related(self, *fields)      性能相關:表之間進行join連表操作,一次性獲取關聯的數據。        總結:      1. select_related主要針一對一和多對一關係進行優化。      2. select_related使用SQL的JOIN語句進行優化,通過減少SQL查詢的次數來進行優化、提高性能。    def prefetch_related(self, *lookups)      性能相關:多表連表操作時速度會慢,使用其執行多次SQL查詢在Python代碼中實現連表操作。        總結:      1. 對於多對多字段(ManyToManyField)和一對多字段,可以使用prefetch_related()來進行優化。      2. prefetch_related()的優化方式是分別查詢每個表,然後用Python處理他們之間的關係。    def annotate(self, *args, **kwargs)      # 用於實現聚合group by查詢        from django.db.models import Count, Avg, Max, Min, Sum        v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))      # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id        v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)      # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1        v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)      # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1    def distinct(self, *field_names)      # 用於distinct去重      models.UserInfo.objects.values('nid').distinct()      # select distinct nid from userinfo        註:只有在PostgreSQL中才能使用distinct進行去重    def order_by(self, *field_names)      # 用於排序      models.UserInfo.objects.all().order_by('-id','age')    def extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)      # 構造額外的查詢條件或者映射,如:子查詢        Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))      Entry.objects.extra(where=['headline=%s'], params=['Lennon'])      Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])      Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])     def reverse(self):      # 倒序      models.UserInfo.objects.all().order_by('-nid').reverse()      # 註:如果存在order_by,reverse則是倒序,如果多個排序則一一倒序       def defer(self, *fields):      models.UserInfo.objects.defer('username','id')      或      models.UserInfo.objects.filter(...).defer('username','id')      #映射中排除某列數據     def only(self, *fields):      #僅取某個表中的數據       models.UserInfo.objects.only('username','id')       或       models.UserInfo.objects.filter(...).only('username','id')     def using(self, alias):       指定使用的數據庫,參數為別名(setting中的設置)      ##################################################  # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #  ##################################################    def raw(self, raw_query, params=None, translations=None, using=None):      # 執行原生SQL      models.UserInfo.objects.raw('select * from userinfo')        # 如果SQL是其他表時,必須將名字設置為當前UserInfo對象的主鍵列名      models.UserInfo.objects.raw('select id as nid from 其他表')        # 為原生SQL設置參數      models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])        # 將獲取的到列名轉換為指定列名      name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}      Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)        # 指定數據庫      models.UserInfo.objects.raw('select * from userinfo', using="default")        ################### 原生SQL ###################      from django.db import connection, connections      cursor = connection.cursor()  # cursor = connections['default'].cursor()      cursor.execute("""SELECT * from auth_user where id = %s""", [1])      row = cursor.fetchone() # fetchall()/fetchmany(..)      def values(self, *fields):      # 獲取每行數據為字典格式    def values_list(self, *fields, **kwargs):      # 獲取每行數據為元祖    def dates(self, field_name, kind, order='ASC'):      # 根據時間進行某一部分進行去重查找並截取指定內容      # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)      # order只能是:"ASC"  "DESC"      # 並獲取轉換後的時間          - year : 年-01-01          - month: 年-月-01          - day  : 年-月-日        models.DatePlus.objects.dates('ctime','day','DESC')    def datetimes(self, field_name, kind, order='ASC', tzinfo=None):      # 根據時間進行某一部分進行去重查找並截取指定內容,將時間轉換為指定時區時間      # kind只能是 "year", "month", "day", "hour", "minute", "second"      # order只能是:"ASC"  "DESC"      # tzinfo時區對象      models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)      models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))        """      pip3 install pytz      import pytz      pytz.all_timezones      pytz.timezone(『Asia/Shanghai』)      """    def none(self):      # 空QuerySet對象      ####################################  # METHODS THAT DO DATABASE QUERIES #  ####################################    def aggregate(self, *args, **kwargs):     # 聚合函數,獲取字典類型聚合結果     from django.db.models import Count, Avg, Max, Min, Sum     result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))     ===> {'k': 3, 'n': 4}    def count(self):     # 獲取個數    def get(self, *args, **kwargs):     # 獲取單個對象    def create(self, **kwargs):     # 創建對象    def bulk_create(self, objs, batch_size=None):      # 批量插入      # batch_size表示一次插入的個數      objs = [          models.DDD(name='r11'),          models.DDD(name='r22')      ]      models.DDD.objects.bulk_create(objs, 10)    def get_or_create(self, defaults=None, **kwargs):      # 如果存在,則獲取,否則,創建      # defaults 指定創建時,其他字段的值      obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})    def update_or_create(self, defaults=None, **kwargs):      # 如果存在,則更新,否則,創建      # defaults 指定創建時或更新時的其他字段      obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})    def first(self):     # 獲取第一個    def last(self):     # 獲取最後一個    def in_bulk(self, id_list=None):     # 根據主鍵ID進行查找     id_list = [11,21,31]     models.DDD.objects.in_bulk(id_list)    def delete(self):     # 刪除    def update(self, **kwargs):      # 更新    def exists(self):     # 是否有結果

Django ORM執行原生SQL(了解)

# extra  # 在QuerySet的基礎上繼續執行子語句  # extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)    # select和select_params是一組,where和params是一組,tables用來設置from哪個表  # Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))  # Entry.objects.extra(where=['headline=%s'], params=['Lennon'])  # Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])  # Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])    舉個例子:  models.UserInfo.objects.extra(                      select={'newid':'select count(1) from app01_usertype where id>%s'},                      select_params=[1,],                      where = ['age>%s'],                      params=[18,],                      order_by=['-age'],                      tables=['app01_usertype']                  )                  """                  select                      app01_userinfo.id,                      (select count(1) from app01_usertype where id>1) as newid                  from app01_userinfo,app01_usertype                  where                      app01_userinfo.age > 18                  order by                      app01_userinfo.age desc                  """      # 執行原生SQL  # 更高靈活度的方式執行原生SQL語句  # from django.db import connection, connections  # cursor = connection.cursor()  # cursor = connections['default'].cursor()  # cursor.execute("""SELECT * from auth_user where id = %s""", [1])  # row = cursor.fetchone()

Django終端打印SQL語句

  • 在Django項目的settings.py文件中,在最後複製粘貼如下代碼:
LOGGING = {      'version': 1,      'disable_existing_loggers': False,      'handlers': {          'console':{              'level':'DEBUG',              'class':'logging.StreamHandler',          },      },      'loggers': {          'django.db.backends': {              'handlers': ['console'],              'propagate': True,              'level':'DEBUG',          },      }  }  #即為你的Django項目配置上一個名為django.db.backends的logger實例即可查看翻譯後的SQL語句。 

在Python腳本中調用Django環境

  • 第一種方法
  • 第二種方法 創建py文件複製下面代碼
import os    #if __name__ == '__main__':      #os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")  os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")  import django  django.setup()    from app01 import models    books = models.Book.objects.all()  print(books)  #後邊寫可被執行的ORM或原生sql語句

django控制台寫入數據(交互式無法保存記錄)

作 者:郭楷豐