Django -- ORM

什麼是orm?python

MVC框架中重要的一部分就是ORM,實現了數據模型與數據庫的解耦,即數據模型不須要依賴於特定的數據庫,經過簡單的配置就能夠輕鬆更換數據庫。mysql

ORM是對象關係映射的簡稱,主要任務是:git

  • 根據對象的類型生成表結構
  • 將對象、列表操做,轉換成SQL語句(或其餘數據庫的語句)
  • 將SQL查詢到的結果轉換爲對象、列表
  • 類名對應  ------>  數據庫中的表名
  • 類屬性對應  ----->  數據庫裏的字段
  • 類實例對應  ------->  數據庫表裏的一行數

 ORM的優點:正則表達式

  1. ORM使得咱們的通用數據庫交互變得簡單易行,並且徹底不用考慮該死的SQL語句。快速開發,由此而來。
  2. 能夠避免一些新手程序猿寫sql語句帶來的性能問題。
  3. 若是數據庫遷移,只須要更換Django的數據庫引擎便可

ORM的缺點:

  1. 性能有所犧牲,不過如今的各類ORM框架都在嘗試各類方法,好比緩存,延遲加載登來減輕這個問題。效果很顯著。
  2. 對於個別複雜查詢,ORM仍然力不從心,爲了解決這個問題,ORM通常也支持寫raw sql。
  3. 經過QuerySet的query屬性查詢對應操做的sql語句

數據庫的配置sql

1.默認支持數據庫

Django默認支持sqlite,mysql, oracle,postgresql數據庫。默認使用sqlite的數據庫,默認自帶sqlite的數據庫驅動 , 引擎名稱:django.db.backends.sqlite3django

2.mysql驅動程序

  • MySQLdb(mysql python)【python2中的驅動】
  • mysqlclient
  • MySQL
  • PyMySQL(純python的mysql驅動程序)【python3中的驅動】

3.建立數據庫

配置前必須先建立數據庫緩存

create database Django_books charset utf8;  #建立支持中文的數據庫

 4.更改項目中settings.py的數據庫設置oracle

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'Django_Books',         #你的數據庫名稱
        'USER': 'root',          #你的數據庫用戶名
        'PASSWORD': '123456',     #你的數據庫密碼
        'HOST': 'localhost',       #你的數據庫主機,留空默認爲localhost
        'PORT': '3306',          #你的數據庫端口
    }
}

 5.配置驅動

Django默認導入的驅動是MySQLdb,MySQLdb對python3的支持有很大問題,因此要換成PyMySQL:在項目名文件夾下的__init__.py中:app

import pymysql
pymysql.install_as_MySQLdb()    # 告訴Django使用pymysql驅動程序

 擴展:查看ORM操做執行的原生sql語句,在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',
        },
    }
}

 表(模型)的建立

示例:做者、書籍和出版社之間的關係

from django.db import models
 
class Book(models.Model):
    name = models.CharField(max_length=20)
    price = models.IntegerField()
    pub_data = models.DateField()
    author = models.ManyToManyField("Author")
    publish = models.ForeignKey("Publish",on_delete=models.CASCADE)
 
class Author(models.Model):
    name = models.CharField(max_length=32)
    sex = models.BooleanField(max_length=1,choices=((0,"男"),(1,"女"),))
    phone = models.CharField(max_length=11)
 
class Publish(models.Model):
    name = models.CharField(max_length=30)
    address = models.CharField("地址",max_length=150)

1.每一個數據模型(類)都是django.db.models.Model的子類,它的父類Model包含了全部必要的和數據庫交互的方法。

2.每一個類就至關於單個數據表,屬性名就是字段名

字段類型介紹

AutoField  自增列 = int(11)  若是沒有的話,默認會生成一個名稱爲 id 的列,若是要顯示的自定義一個自增列,必須將給列設置爲主鍵 primary_key=True。
CharField  字符串字段           必須有 max_length 參數
BooleanField  布爾類型=tinyint(1)   不能爲空,Blank=True
ComaSeparatedIntegerField  用逗號分割的數字=varchar   繼承CharField,因此必須 max_lenght 參數
DateField  日期類型 date   對於參數,auto_now = True 則每次更新都會更新這個時間;auto_now_add 則只是第一次建立添加,以後的更新再也不改變。
DateTimeField  日期類型 datetime        同DateField的參數
Decimal  十進制小數類型 = decimal     必須指定整數位max_digits和小數位decimal_places
EmailField  字符串類型(正則表達式郵箱) =varchar    對字符串進行正則表達式
FloatField  浮點類型 = double
IntegerField     整形
BigIntegerField  長整形
  不一樣的整型類型integer_field_ranges = {
    'SmallIntegerField': (-32768, 32767),
    'IntegerField': (-2147483648, 2147483647),
    'BigIntegerField': (-9223372036854775808, 9223372036854775807),
    'PositiveSmallIntegerField': (0, 32767),
    'PositiveIntegerField': (0, 2147483647),
  }
IPAddressField  字符串類型(ip4正則表達式)(已棄用,用1三、)
GenericIPAddressField  字符串類型(ip4和ip6是可選的)參數protocol能夠是:both、ipv四、ipv6
  驗證時,會根據設置報錯
NullBooleanField  容許爲空的布爾類型
PositiveIntegerFiel  正Integer
PositiveSmallIntegerField  正smallInteger
SlugField  減號、下劃線、字母、數字
TextField  字符串=longtext
TimeField  時間 HH:MM[:ss[.uuuuuu]]
URLField  字符串,地址正則表達式
BinaryField  二進制
ImageField    圖片
FilePathField  文件

 3.字段參數介紹

null                  是否能夠爲空
default              默認值
primary_key       主鍵
choices:      一個用來選擇值的2維元組。第一個值是實際存儲的值,第二個用來方便進行選擇。如:SEX_CHOICES= (( ‘F’,'Female’),(‘M’,'Male’),)
                             gender = models.CharField(max_length=2,choices = SEX_CHOICES)
db_column           列名
db_index             索引(db_index=True)
unique                 惟一索引(unique=True)
unique_for_date        只對日期索引
unique_for_month      只對月份索引
unique_for_year       只對年作索引
blank              django的 Admin 中添加數據時是否可容許空值
editable           若是爲假,admin模式下將不能改寫。缺省爲真
verbose_name  Admin中字段的顯示名稱
validator_list      有效性檢查。非有效產生 django.core.validators.ValidationError 錯誤

 4.模型之間的關係(表與表之間)

有三種關係:一對一,一對多,多對多

             一對一:實質就是在主外鍵的關係基礎上,給外鍵加了一個UNIQUE=True的屬性;OneToOne("UserGroup")

             一對多:就是主外鍵關係;ForeignKeyField("UserGroup",to_field="gid",default=1,on_delete=models.CASCADE)

             多對多:ORM會爲咱們自動建立第三張表(固然咱們也能夠本身建立第三張表:兩個foreign key);ManyToManyField("UserGroup")

參數:「UserGroup」    關聯的另外一張表的表名;

    to_field="gid"    可省略。另一張表的字段,默認是主鍵;

    on_delete=models.CASCADE(

      CASCADE:這就是默認的選項,級聯刪除,你無需顯性指定它。
      PROTECT: 保護模式,若是採用該選項,刪除的時候,會拋出ProtectedError錯誤。
      SET_NULL: 置空模式,刪除的時候,外鍵字段被設置爲空,前提就是blank=True, null=True,定義該字段的時候,容許爲空。
      SET_DEFAULT: 置默認值,刪除的時候,外鍵字段設置爲默認值,因此定義外鍵的時候注意加上一個默認值。)

5.建好表後,在命令行執行一下兩條語句生成映射文件並提交數據庫執行建表

python manage.py makemigrations   在應用文件夾內生成映射文件(000開頭的py文件)
python manage.py migrate            提交數據庫執行建表

 表的操做

一.增添

增添表記錄

單表:
create()方法:
        Book.objects.create(name="Python基礎",price=100)
      或
        Book.objects.create(**{"name":"Python基礎","price":"100"})
save()方法:
        b = Book(name="Python基礎",price=100) # 建立對象
        b.save()         # 將對象存數據庫
      或
        b = Book()
        b.name="Python基礎"
        b.price=100
        b.save()
有外鍵時:
    Book.objects.create(name="",price="",pub_date="",publish_id=2)  # 外鍵關聯表的id
  或
    objcet = Publish.objects.get();
    Book.objects.create(name="",price="",pub_date="",publish=object) # 外鍵=對象

 增添多對多之間的關聯關係

情景1:使用ManyToMany自動建立第三張表
    add()方法:【調用add的主體必須是單個model對象】
    例如:爲已有的一本書增添做者
            author_obj = Author.objects.filter(name="alex")
            book_obj = Book.objects.get(id=2)
            book_obj.author.add(*author_obj)  這裏的author指的是Author表。添加一個列表*  添加一個字典**
            或:
            book_obj = Book.objects.filter(name="Python")
            author_obj = Author.object.get(id=1)
            author_obj.book_set.add(*book_obj)    這裏的book指的是Book表。
            注意兩種方式 _set 的區別,源於models.py中建表的author字段
情景2:本身手動建立的第三張表BookToAuthor
    models.BookToAuthor.objects.create(author_id=1,Book_id=2)

二.刪除

刪除表記錄

Book.objects.filter(name="Python").delete()  # 看上去刪除了一條消息,實際上還刪除了book_author表中的數據, 這是django默認的級聯刪除 

 刪除多對多之間的關聯關係

clear()和remove()方法 【調用這兩種方法的主體必須是單個對象】
    remove()方法: remove方法用來刪除某個對象和某些對象的關聯關係
            爲已有的一本書刪除某個做者
            author_obj = Author.objects.filter(name="alex")
            book_obj = Book.objects.get(name="Python")
            book_obj.author.remove(*author_obj)     remove(3) 意思是刪除id=3的做者與本書的關係
            或:
            book_obj = Book.objects.filter(name="Python")
            author_obj = Author.object.get(name="Python")
            author_obj.book_set.remove(*book_obj)   
     
     clear()方法: clear方法用來刪除某個對象和全部對象的關聯關係
            book_obj.author.remove(*author_obj)   刪除做者alex和Python書之間的關聯
            book_obj.author.clear()               刪除Python和全部做者的關聯
            author_obj[0].book_set.clear()        刪除做者alex和全部書的關聯
注意:當clear()方法和remove()應用在ForeignKey上時,必需null=True才能用

三.修改

修改表數據

update()方法:
   Book.objects.filter(author="yuan").update(price=100)
save()方法:
   b = Book.objects.get(author="oldboy")
   b.price = 200
   b.save()
 
須要注意:
1.update()是QuerySet對象的方法,而save()是model對象的方法。即update能夠一次性修改多條記錄的數據,並返回一個整型數值,表示受影響的記錄條數。而save一次只能修改一條記錄
2.save() 方法會將全部屬性從新設定一遍,效率低。而update只更改指定的屬性,因此修改數據推薦用update

 若是要修改多對多表之間的關係:

沒有專門的方法,能夠將remove()和add()組合使用

四.查詢

1.查詢API:

查詢API:
filter(**kwargs)     它包含了與所給篩選條件相匹配的對象
all()                 查詢全部結果
get(**kwargs)         返回與所給篩選條件相匹配的對象,返回結果有且只有一個,若是符合篩選條件的對象超過一個或者沒有都會拋出錯誤。
  
下面的方法都是對查詢的結果再進行處理:好比 objects.filter.values()
values(*field)        返回一個ValueQuerySet——一個特殊的QuerySet,運行後獲得的並非一系列model的實例化對象,而是一個可迭代的字典序列
values_list(*field)   它與values()很是類似,它返回的是一個元組序列,values返回的是一個字典序列
exclude(**kwargs)     它包含了與所給篩選條件不匹配的對象
order_by(*field)      對查詢結果排序,例如order_by("price")按照價格從小到大排序,order_by("-price")從大到小排序
reverse()             對查詢結果反向排序
distinct()            從返回結果中剔除重複紀錄
count()               返回數據庫中匹配查詢(QuerySet)的對象數量。
first()               返回第一條記錄
last()                返回最後一條記錄
exists()              若是QuerySet包含數據,就返回True,不然返回False。不會保存在緩存裏。

 擴展查詢:有時候Django的查詢API不能方便地設置查詢條件,提供了另外的擴展查詢方法extra()

extra(select=None, where=None, params=None, tables=None,order_by=None, select_params=None)
extra能夠指定一個或多個參數,都不是必需的,但要至少使用一個。
參數詳解:
    1.select和select_params
        select這個參數內能夠直接寫SQL查詢語句或者函數,select_params用來給select中的語句傳值
        例:
            v = models.Publish.objects.all().extra(select={
                'n':"select count(1) from app01_book WHERE id=%s or id=%s",
                'm':"select count(1) from app01_author WHERE id=%s or id=%s",
            },select_params=(1,2,3,4))
            這裏取到了全部Publish表中的數據,同時還取到了如下兩個SQL語句中的東西。
            for i in v:
                print(i.n,i.m,i.id)
    2.where和params
        where用來直接寫查詢條件,parems給where中傳值
            例:
            models.Book.objects.extra(where=['parice=%s'], params=['99'])
            models.Book.objects.extra(where=["'parice=100' OR name='Python'","id=1" ])
    3.tables用來查詢整張表
        例:
        models.Author.objects.extra(tables=["app01_book"])
        <=> select * from app01_book,app01_author
        models.Book.objects.extra(tables=["app01_author"],where=["app01_book.id = app01_author.id"]) <=> select * from app01_book,app01_author where app01_book.id = app01_author.id
    4.order_by用來排序
  
綜合示例1:
    models.Author.objects.extra(
        select={"newid":"select count(1) from app01_book where id>%s"},
        select_params=[1,],
        where=["args>%s"],
        params=[18,],
        order_by=["-ages"],
    )
    <=>
    select
        app01_author.id,
        (select count(1) from app01_book where id>1) as newid
    from app01_author,app01_book
    where
        app01_author.age > 18
    order by
        app01_author.age desc
綜合示例2:
    models.Article.objects.all().filter(user=current_user).extra(select={"filter_create_date":"strftime(‘%%Y/%%m‘,create_time)"}).values_list("filter_create_date")
    #查出當前用戶的全部文章的create_time,而且只取出年份和月份

 單表模糊查詢:(雙下劃線)

萬能的 __雙下劃線
    __gt    大於
    __gte  大於等於
    __lt     小於
    __lte   小於等於
    __icontains="p"    含有p且不區分大小寫
    __contains="p"     含有p且區分大小寫
    __exat="aaa"       至關於 like "aaa"
    __iexat="aaa"      至關於忽略大小寫 like "aaa"
    _in=[10,20,33,50]      獲取id等於10,20,33,50的數據
    __exclude(id_in=[])   不等於10 20 33 50的
    __range=[1,2]    在範圍內的
    __startswith        以...開頭
    __istartswith       不以...開頭
    __endswith         以...結尾
    __iendswith        不以...結尾
    __year         日期字段的年份
    __month       日期字段的月份
    __day           日期字段的日
    __isnull=True/False
 
 
filter(price__gt=100)         篩選價格大於100的
filter(id__gt=1,id__lt=10)  id大於1小於10

 2.多表關聯查詢:(多對多和一對多沒有區別)

1.查多條記錄(__ 雙下劃線)
      找到Python這本書的出版社名稱
      Book.objects.filter(name="Python").values("publish__name")   經過外鍵[publish是Book表的外鍵]
      Publish.objects.filter(book__name="Python").values("name")  經過對象[book是Publish關聯的表名]
 
2.(只能查單條記錄,不經常使用)
    正向查詢(經過書查出版社)
       Book.objects.get(name="").publish  --> 出版社對象
       print(publish.屬性)
    反向查詢(經過出版社找書)
       Publish.objects.get(name="").book_set.all() -->全部的書籍對象
            注意:book_set 是模型對象中反向查詢的語法,book是表名,所有小寫
                    調用 _set 的主體必須是單個對象!!

  3.Queryset的惰性機制

所謂惰性機制,models查詢語句只是返回了一個Queryset對象,並不會立刻執行SQL,而是使用查詢結果的時候才執行。

例如:person_set = Person.objects.filter(first_name="Dave")這句話並無運行數據庫查詢,當遍歷Queryset、if queryset或者在模板渲染的時候使用的時候纔會執行數據庫查詢操做

Qureyset的特色:可迭代、可切片、有緩存。

※ Queryset是有緩存的

當遍歷Queryset時,匹配的記錄會從數據庫獲取,而後轉換成Django的model。這些model會保存在Queryset內置的緩存中,若是再次遍歷或者使用這個Queryset時,不須要重複的查詢。

而若是處理成千上萬的記錄時,一次性裝入內存是很是浪費的。要避免產生Queryset緩存,可使用iterator()方法來獲取數據,處理完數據就將其丟棄。

objs = Book.objects.all().iterator()
for obj in objs:
    print(obj.name)

迭代器不能重複遍歷,意味着可能會形成額外的重複查詢。而緩存是用於減小對數據庫的查詢。因此使用時要考慮需求。

4.聚合查詢的分組查詢

from django.db.models import Avg,Min,Sum,Max,Count

聚合查詢:aggregate(*args,**kwargs):

aggregate()是Queryset的一個終止子句,經過對Queryset進行計算,返回一個聚合值的字典。從整個查詢集上生成聚合值。

aggregate()子句的參數描述了咱們想要計算的聚合值,如平均Avg,求和Sum等

例如:

Book.objects.all().aggregate(Avg('price')) # 計算全部在售書的平均價錢
{'price__avg': 34.35}
Book.objects.aggregate(average_price=Avg('price')) # 自定義標識符(鍵)
{'average_price': 34.35}
Book.objects.aggregate(Avg('price'), Max('price'), Min('price')) # 同時輸出多個聚合值
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

分組查詢: annotate(*args,**kwargs):

分組查詢經常和聚合查詢一塊兒。它能夠爲查詢集的每一項生成聚合。

# 查詢每一個做者出書的總價格,按照做者分組
Book.objects.all().values("authors__name").annotate(Sum("price"))
[{'authors__name':'alex','price_sum':'140'},{'authors__name':'alvin','price_sum':'160'}]
 
# 查詢各個出版社最便宜的書的價格
Book.objects.all().values("pulisher__name").annotate(Min("price"))
[{'publisher__name':'清華大學出版社','price_min':'70'},{'publisher__name':'中國機械出版社','price_min':'90'}]

 5.F查詢和Q查詢

from django.db.models import F,Q

 F查詢:就是用來更新獲取原來值的功能

models.Author.objects.all().update(age=F("age")+1) # 將Author表裏全部人的年齡+1
models.Book.objects.all().update(price=F("price")+10)  # 將全部書的價格漲10元

Q查詢:用於構造複雜的查詢條件,如 與、或、非。

查詢條件咱們可使用filter()方法,在filter()裏面的兩個條件之間是and的關係,若是是或的關係,則只能使用Q查詢

Book.objects.filter(Q(id=1)) # 查詢條件爲id=1
Book.objects.filter(Q(price=99) | ~Q(name="Python")) # 查詢價格=99 或 名稱不等於Python的書
Book.objects.filter(Q(name="Go"),price=99)  # Q查詢能夠和關鍵字查詢組合使用,但必需要將Q放到前面
相關文章
相關標籤/搜索