Django-模型層(多表操做)

1.建立模型

表和表之間的關係python

一對1、多對1、多對多,用book表和publish表本身來想一想關係,想一想裏面的操做,加外鍵約束和不加外鍵約束的區別,一對一的外鍵約束是在一對多的約束上加上惟一約束mysql

實例:咱們來假定下面這些概念,字段和關係git

做者模型:一個做者有姓名和年齡sql

做者詳細模型:把做者的詳情放到詳情表,包含生日,手機號,家庭住址等信息。做者詳情模型和做者模型之間是一對一的關係(one-to-one)數據庫

出版商模型:出版商有名稱,所在城市以及emaildjango

書籍模型: 書籍有書名和出版日期,一本書可能會有多個做者,一個做者也能夠寫多本書,因此做者和書籍的關係就是多對多的關聯關係(many-to-many);一本書只應該由一個出版商出版,因此出版商和書籍是一對多關聯關係(one-to-many)app

模型創建以下:函數

from django.db import models

# Create your models here.

class Author(models.Model): #比較經常使用的信息放到這個表裏面
    nid = models.AutoField(primary_key=True)
    name=models.CharField( max_length=32)
    age=models.IntegerField()
    # 與AuthorDetail創建一對一的關係,一對一的這個關係字段寫在兩個表的任意一個表裏面均可以
    authorDetail=models.OneToOneField(to="AuthorDetail",to_field="nid",on_delete=models.CASCADE) #就是foreignkey+unique,只不過不須要咱們本身來寫參數了,而且orm會自動幫你給這個字段名字拼上一個_id,數據庫中字段名稱爲authorDetail_id

class AuthorDetail(models.Model):#不經常使用的放到這個表裏面
    nid = models.AutoField(primary_key=True)
    birthday=models.DateField()
    telephone=models.BigIntegerField()
    addr=models.CharField( max_length=64)

class Publish(models.Model):
    nid = models.AutoField(primary_key=True)
    name=models.CharField( max_length=32)
    city=models.CharField( max_length=32)
    email=models.EmailField()

#多對多的表關係,咱們學mysql的時候是怎麼創建的,是否是手動建立一個第三張表,而後寫上兩個字段,每一個字段外鍵關聯到另外兩張多對多關係的表,orm的manytomany自動幫咱們建立第三張表,兩種方式創建關係均可以,之後的學習咱們暫時用orm自動建立的第三張表,由於手動建立的第三張表咱們進行orm操做的時候,不少關於多對多關係的表之間的orm語句方法沒法使用#若是你想刪除某張表,你只須要將這個表註銷掉,而後執行那兩個數據庫同步指令就能夠了,自動就刪除了。

class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField( max_length=32)
    publishDate=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2)

    # 與Publish創建一對多的關係,外鍵字段創建在多的一方,字段publish若是是外鍵字段,那麼它自動是int類型
    publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE) #foreignkey裏面能夠加不少的參數,都是須要我們學習的,慢慢來,to指向表,to_field指向你關聯的字段,不寫這個,默認會自動關聯主鍵字段,on_delete級聯刪除   字段名稱不須要寫成publish_id,orm在翻譯foreignkey的時候會自動給你這個字段拼上一個_id,這個字段名稱在數據庫裏面就自動變成了publish_id
    # 與Author表創建多對多的關係,ManyToManyField能夠建在兩個模型中的任意一個,自動建立第三張表,而且注意一點,你查看book表的時候,你看不到這個字段,由於這個字段就是建立第三張表的意思,不是建立字段的意思,因此只能說這個book類裏面有authors這個字段屬性
    authors=models.ManyToManyField(to='Author',) #注意無論是一對多仍是多對多,寫to這個參數的時候,最後後面的值是個字符串,否則你就須要將你要關聯的那個表放到這個表的上面

1.1方式一:自行建立第三張表

class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="書名")


class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="做者姓名")

# 本身建立第三張表,分別經過外鍵關聯書和做者
class Author2Book(models.Model):
    author = models.ForeignKey(to="Author")
    book = models.ForeignKey(to="Book")
    class Meta:
        unique_together = ("author", "book") #聯合鍵

1.2方式二:經過ManyToManyField自動建立第三張表

class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="書名")

# 經過ORM自帶的ManyToManyField自動建立第三張表
class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="做者姓名")
    books = models.ManyToManyField(to="Book", related_name="authors")  #自動生成的第三張表咱們是沒有辦法添加其餘字段的

建立一對一關係字段時的一些參數工具

to
    設置要關聯的表。

to_field
    設置要關聯的字段。
    
on_delete
    同ForeignKey字段。

建立一對多關係字段時的一些參數

to
    設置要關聯的表

to_field
    設置要關聯的表的字段

related_name
    反向操做時,使用的字段名,用於代替原反向查詢時的'表名_set'。
related_query_name
    反向查詢操做時,使用的鏈接前綴,用於替換表名。

on_delete
    當刪除關聯表中的數據時,當前表與其關聯的行的行爲。

建立多對多字段時的一些參數

to
設置要關聯的表

related_name
同ForeignKey字段。

related_query_name
同ForeignKey字段。
through
在使用ManyToManyField字段時,Django將自動生成一張表來管理多對多的關聯關係。

但咱們也能夠手動建立第三張表來管理多對多關係,此時就須要經過        
through來指定第三張表的表名。

through_fields
設置關聯的字段。

db_table
默認建立第三張表時,數據庫中表的名稱。

建立表時的一些元信息設置

元信息
    ORM對應的類裏面包含另外一個Meta類,而Meta類封裝了一些數據庫的信息。主要字段以下:
class Author2Book(models.Model):
    author = models.ForeignKey(to="Author")
    book = models.ForeignKey(to="Book")
    class Meta:
        unique_together = ("author", "book")

db_table
    ORM在數據庫中的表名默認是 app_類名,能夠經過db_table能夠重寫表名。db_table = 'book_model'

index_together
    聯合索引。

unique_together
    聯合惟一索引。

ordering
    指定默認按什麼字段排序。
    ordering = ['pub_date',]
    只有設置了該屬性,咱們查詢到的結果才能夠被reverse(),不然是能對排序了的結果進行反轉(order_by()方法排序過的數據)

1.3關於db_column和verbose_name

1.指定字段名: 在定義字段的時候,增長參數db_column=’real_field’;

    2.指定表名: 在model的class中,添加Meta類,在Meta類中指定表名db_table

例如在某個models.py文件中,有一個類叫Info:

class Info(models.Model):  
    ''''' 
            信息統計 
    '''  
    app_id = models.ForeignKey(App)  
    app_name = models.CharField(verbose_name='應用名',  max_length=32, db_column='app_name2')  
  
    class Meta:  
        db_table = 'info'  
        verbose_name = '信息統計'  
        verbose_name_plural = '信息統計'

其中db_column指定了對應的字段名,db_table指定了對應的代表;

若是不這樣指定,字段名默認爲app_name, 而代表默認爲app名+類名: [app_name]_info.

verbose_name指定在admin管理界面中顯示中文;verbose_name表示單數形式的顯示,verbose_name_plural表示複數形式的顯示;中文的單數和複數通常不做區別。

建立完這個表,咱們本身能夠經過navicat工具來看看數據庫裏面的那些表,出版社這個表裏面沒有任何的關係字段,這種單表的數據,咱們能夠先添加幾條數據,在進行下面的增刪改查的操做。

生成表以下: 

注意事項:

  • 表的名稱myapp_modelName,是根據 模型中的元數據自動生成的,也能夠覆寫爲別的名稱  
  • id 字段是自動添加的
  • 對於外鍵字段,Django 會在字段名上添加"_id" 來建立數據庫中的列名
  • 這個例子中的CREATE TABLE SQL 語句使用PostgreSQL 語法格式,要注意的是Django 會根據settings 中指定的數據庫類型來使用相應的SQL 語句。
  • 定義好模型以後,你須要告訴Django _使用_這些模型。你要作的就是修改配置文件中的INSTALL_APPSZ中設置,在其中添加models.py所在應用的名稱。
  • 外鍵字段 ForeignKey 有一個 null=True 的設置(它容許外鍵接受空值 NULL),你能夠賦給它空值 None 。

1.4關於on_delete(瞭解)

關於on_delete參數

on_delete
當刪除關聯表中的數據時,當前表與其關聯的行的行爲。

models.CASCADE
刪除關聯數據,與之關聯也刪除

models.DO_NOTHING
刪除關聯數據,引起錯誤IntegrityError

models.PROTECT
刪除關聯數據,引起錯誤ProtectedError

models.SET_NULL
刪除關聯數據,與之關聯的值設置爲null(前提FK字段須要設置爲可空)

models.SET_DEFAULT
刪除關聯數據,與之關聯的值設置爲默認值(前提FK字段須要設置默認值)

models.SET
刪除關聯數據,
a. 與之關聯的值設置爲指定值,設置:models.SET(值)
b. 與之關聯的值設置爲可執行對象的返回值,設置:models.SET(可執行對象)

ForeignKey的db_contraint參數

關係和約束你們要搞清楚,我不加外鍵能不能表示兩個表之間的關係啊,固然能夠

可是咱們就不能使用ORM外鍵相關的方法了,因此咱們單純的將外鍵換成一個其餘字段類型,只是單純的存着另一個關聯表的主鍵值是不能使用ORM外鍵方法的。

#db_constraint=False只加二者的關係,沒有強制約束的效果,而且ORM外鍵相關的接口(方法)還能使用,因此若是未來公司讓你創建外鍵,而且不能有強制的約束關係,那麼就能夠將這個參數改成False
    customer = models.ForeignKey(verbose_name='關聯客戶', to='Customer',db_constraint=False)

2.添加表記錄

操做前先簡單的錄入一些數據:仍是create和save兩個方法,和單表的區別就是看看怎麼添加關聯字段的數據

publish表:

author表:

authordetail表:

2.1一對多

方式1:
   publish_obj=Publish.objects.get(nid=1) #拿到nid爲1的出版社對象
   book_obj=Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=100,publish=publish_obj) #出版社對象做爲值給publish,其實就是自動將publish字段變成publish_id,而後將publish_obj的id給取出來賦值給publish_id字段,注意你若是不是publish類的對象確定會報錯的,別亂昂
方式2:
   book_obj=Book.objects.create(title="金瓶眉",publishDate="2012-12-12",price=100,publish_id=1)  #直接能夠寫id值,注意字段屬性的寫法和上面不一樣,這個是publish_id=xxx,上面是publish=xxx。

2.2多對多

方式一: 多對多通常在前端頁面上使用的時候是多選下拉框的樣子來給用戶選擇多個數據,這裏可讓用戶選擇多個書籍,多個做者  # 當前生成的書籍對象
    book_obj=Book.objects.create(title="追風箏的人",price=200,publishDate="2012-11-12",publish_id=1)
    # 爲書籍綁定的作做者對象
    yuan=Author.objects.filter(name="yuan").first() # 在Author表中主鍵爲2的紀錄,注意取的是author的model對象
    egon=Author.objects.filter(name="alex").first() # 在Author表中主鍵爲1的紀錄
  #有人可能會說,咱們能夠直接給第三張表添加數據啊,這個自動生成的第三張表你能經過models獲取到嗎,是獲取不到的,用不了的,固然若是你知道了這個表的名字,那麼你經過原生sql語句能夠進行書的添加,因此要經過orm間接的給第三張表添加數據,若是是你手動添加的第三張表你是能夠直接給第三張表添加數據
    # 綁定多對多關係,即向關係表book_authors中添加紀錄,給書添加兩個做者,下面的語法就是告訴orm給第三張表添加兩條數據
    book_obj.authors.add(yuan,egon)    #  將某些特定的 model 對象添加到被關聯對象集合中。   =======    book_obj.authors.add(*[])    #book_obj是書籍對象,authors是book表裏面那個多對多的關係字段名稱。    #其實orm就是先經過book_obj的authors屬性找到第三張表,而後將book_obj的id值和兩個做者對象的id值組合成兩條記錄添加到第三張表裏面去  方式二
  book_obj.authors.add(1,2)
  book_obj.authors.add(*[1,2]) #這種方式用的最多,由於通常是給用戶來選擇,用戶選擇是多選的,選完給你發送過來的就是一堆的id值

book表

book_authors表

多對多關係其它經常使用API:

book_obj.authors.remove()      # 將某個特定的對象從被關聯對象集合中去除。    ======   book_obj.authors.remove(*[1,2]),將多對多的關係數據刪除
book_obj.authors.clear()       #清空被關聯對象集合
book_obj.authors.set()         #先清空再設置   =====

刪除示例:

book_obj = models.Book.objects.filter(nid=4)[0]
    # book_obj.authors.remove(2) #將第三張表中的這個book_obj對象對應的那個做者id爲2的那條記錄刪除
    # book_obj.authors.clear()
    # book_obj.authors.set('2') #先清除掉全部的關係數據,而後只給這個書對象綁定這個id爲2的做者,因此只剩下一條記錄  3---2,好比用戶編輯數據的時候,選擇做者發生了變化,那麼須要從新選擇,因此咱們就能夠先清空,而後再從新綁定關係數據,注意這裏寫的是字符串,數字類型不能夠
    book_obj.authors.set(['1',]) #這麼寫也能夠,可是注意列表中的元素是字符串,列表前面沒有*,以前我測試有*,感受是版本的問題,沒事,可以用哪一個用哪一個

一對一和一對多的刪改和單表的刪改是同樣的,別忘了刪除表的時候,我們是作了級聯刪除的

更新:
book_obj = models.Book.objects.get(id=1) #獲取一個書籍對象
data = {'title':'xxx','price':100} #這個書籍對象更新後的數據
models.Book.objects.filter(id=n).update(**data) #將新數據更新到原來的記錄中
book_obj.authors.set(author_list) #將數據和做者的多對多關係加上

刪除:
models.Book.objects.filter(id=1).delete()

3.基於對象的跨表查詢

  一對多查詢(Publish 與 Book)

正向查詢(按字段:publish):關聯屬性字段所在的表查詢被關聯表的記錄就是正向查詢,反之就是反向查詢

# 查詢主鍵爲1的書籍的出版社所在的城市
book_obj=Book.objects.filter(pk=1).first()
# book_obj.publish 是主鍵爲1的書籍對象關聯的出版社對象,book對象.外鍵字段名稱
print(book_obj.publish.city)  

反向查詢(按表名:book_set,由於加上_set是由於反向查詢的時候,你查詢出來的多是多條記錄的集合):

publish=Publish.objects.get(name="蘋果出版社")
#publish.book_set.all() : 與蘋果出版社關聯的全部書籍對象集合,寫法:小寫的表名_set.all(),獲得queryset類型數據
book_list=publish.book_set.all()    
for book_obj in book_list:
       print(book_obj.title)

3.2一對一查詢(Author與AuthorDetail)

正向查詢(按字段:authorDetail):

egon=Author.objects.filter(name="egon").first()
print(egon.authorDetail.telephone) egon.authorDeail就拿到了這個對象,由於一對一找到的就是一條記錄,注意寫法:做者對象.字段名,就拿到了那個關聯對象

反向查詢(按表名:author):不須要_set,由於一對一正向反向都是找到一條記錄

# 查詢全部住址在北京的做者的姓名
authorDet=AuthorDetail.objects.filter(addr="beijing")[0]
authorDet.author.name

3.3 多對多查詢(Author與Book)

正向查詢(按字段:authors):

 

反向查詢(按表名:book_set):

 

注意:

你能夠經過在 ForeignKey() 和ManyToManyField的定義中設置 related_name 的值來覆寫 FOO_set 的名稱。例如,若是 Article model 中作一下更改:

 

那麼接下來就會如咱們看到這般:

 

在這裏咱們補充一點,由於你很快就要接觸到了,那就是form表單裏面的button按鈕和form表單外面的button按鈕的區別,form表單裏面的button按鈕其實和input type='submit'的標籤是有一樣的效果的,都可以提交form表單的數據,可是若是放在form表單外面的button按鈕,那就只是個普通的按鈕了。,還有一點,input type='submit'按鈕放到form表單外面那就成了一個普通的按鈕。

4.基於雙下劃線的跨表查詢(基於join實現的)

Django 還提供了一種直觀而高效的方式在查詢(lookups)中表示關聯關係,它能自動確認 SQL JOIN 聯繫。要作跨關係查詢,就使用兩個下劃線來連接模型(model)間關聯字段的名稱,直到最終連接到你想要的model 爲止。

'''
    基於雙下劃線的查詢就一句話:正向查詢按字段,反向查詢按表名小寫用來告訴ORM引擎join哪張表,一對1、一對多、多對多都是一個寫法,注意,咱們寫orm查詢的時候,哪一個表在前哪一個表在後都沒問題,由於走的是join連表操做。
'''

4.1一對多查詢

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

    # 正向查詢 按字段:publish

    queryResult=Book.objects
            .filter(publish__name="蘋果出版社")  #經過__告訴orm將book表和publish表進行join,而後找到全部記錄中publish.name='蘋果出版社'的記錄(注意publish是屬性名稱),而後select book.title,book.price的字段值
            .values_list("title","price") #values或者values_list

    # 反向查詢 按表名:book

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

4.2多對多查詢  

# 練習: 查詢yuan出過的全部書籍的名字(多對多)

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

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

4.3一對一查詢

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

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

4.4進階練習(連續跨表)

# 練習: 查詢人民出版社出版過的全部書籍的名字以及做者的姓名


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


# 練習: 手機號以151開頭的做者出版過的全部書籍名稱以及出版社名稱
    # 方式1:
    queryResult=Book.objects
            .filter(authors__authorDetail__telephone__regex="151")
            .values_list("title","publish__name")
    # 方式2:    
    ret=Author.objects
              .filter(authordetail__telephone__startswith="151")
              .values("book__title","book__publish__name")

  反向查詢時,若是定義了related_name ,則用related_name替換 表名,例如:

publish = ForeignKey(Blog, related_name='bookList')
# 練習: 查詢人民出版社出版過的全部書籍的名字與價格(一對多)# 反向查詢 再也不按表名:book,而是related_name:bookList
    queryResult=Publish.objects
              .filter(name="人民出版社")
              .values_list("bookList__title","bookList__price")

5.聚合查詢和分組查詢

5.1聚合

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')}

5.2分組

咱們在這裏先複習一下SQL語句的分組。

假設如今有一張公司職員表:

咱們使用原生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")

連表查詢的分組:

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")

更多示例:

示例1:統計每一本書的做者個數

>>> book_list = models.Book.objects.all().annotate(author_num=Count("author"))
>>> for obj in book_list:
...     print(obj.author_num)
...

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

>>> publisher_list = models.Publisher.objects.annotate(min_price=Min("book__price"))
>>> for obj in publisher_list:
...     print(obj.min_price)
...     
9.90
19.90

方法二:

>>> models.Book.objects.values("publisher__name").annotate(min_price=Min("price"))
<QuerySet [{'publisher__name': '沙河出版社', 'min_price': Decimal('9.90')}, {'publisher__name': '人民出版社', 'min_price': Decimal('19.90')}]>

示例3:統計不止一個做者的圖書

>>> models.Book.objects.annotate(author_num=Count("author")).filter(author_num__gt=1)
<QuerySet [<Book: 番茄物語>]>

示例4:根據一本圖書做者數量的多少對查詢集 QuerySet進行排序

>>> models.Book.objects.annotate(author_num=Count("author")).order_by("author_num")
<QuerySet [<Book: 香蕉物語>, <Book: 橘子物語>, <Book: 番茄物語>]>

示例5:查詢各個做者出的書的總價格

>>> models.Author.objects.annotate(sum_price=Sum("book__price")).values("name", "sum_price")
<QuerySet [{'name': '小精靈', 'sum_price': Decimal('9.90')}, {'name': '小仙女', 'sum_price': Decimal('29.80')}, {'name': '小魔女', 'sum_price': Decimal('9.90')}]>

6.F查詢和Q查詢

6.1F查詢

在上面全部的例子中,咱們構造的過濾器都只是將字段值與某個常量作比較。若是咱們要對兩個字段的值作比較,那該怎麼作呢?

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(")"))

6.2Q查詢

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: 橘子物語>]>

7.事務

import os

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

    import datetime
    from app01 import models

    try:
        from django.db import transaction
        with transaction.atomic():
            new_publisher = models.Publisher.objects.create(name="火星出版社")
            models.Book.objects.create(title="橘子物語", publish_date=datetime.date.today(), publisher_id=10)  # 指定一個不存在的出版社id
    except Exception as e:
        print(str(e))

8.其餘不爲人知的操做(有個印象便可)

8.1Django 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()

8.2QuerySet方法大全

##################################################################
# 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):
   # 是否有結果

9.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語句。

10.在Python腳本中調用Django環境

import os

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

    from app01 import models

    books = models.Book.objects.all()
    print(books)
相關文章
相關標籤/搜索