(轉)Django 數據庫

     轉: https://blog.csdn.net/ayhan_huang/article/details/77575186     

目錄

數據庫說明php

配置數據庫css

  在屏幕輸出orm操做對應的sql語句html

modelsjava

  定義模型python

  字段類型mysql

  關係字段linux

  字段選項git

ORM操做sql

  增刪改查shell

  QuerySet

  提升數據庫性能

  提升數據庫性能——db的讀寫分離

  外鍵關係處理

  多表查詢

  聚合&分組查詢

  F&Q查詢

  多表查詢和表建立總結

  表的自引用(即外鍵是本身)

  繼承自帶用戶表

 

1、數據庫框架

數據庫框架是數據庫的抽象層,也稱爲對象關係映射(Object-Relational Mapper, ORM),它將高層的面向對象操做轉換成低層的數據庫指令,比起直接操做數據庫引擎,ORM極大的提升了易用性。這種轉換會帶來必定的性能損耗,但ORM對生產效率的提高遠遠超過這一丁點兒性能下降。
Django中內置的SQLAlchemy ORM就是一個很好的數據庫框架,它爲多種關係型數據庫引擎提供抽象層,好比MySQL, Postgres,SQLite,而且使用相同的面向對象接口。所以,使用SQLAlchemy ORM,不只能極大的提升生產力,並且能夠方便的在多種數據庫之間遷移。

2、配置數據庫

咱們能夠在項目文件夾的settins.py中配置數據庫引擎。
Django默認使用sqlite:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',  # sqlite引擎
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

 

若是要要使用mysql, 須要進行以下配置:
1 編輯項目文件夾下的settings.py :

DATABASES = {

    'default': {

        'ENGINE': 'django.db.backends.mysql',  # mysql引擎
        'NAME': 'BookManagement',    
        # 數據庫名稱, 須要經過命令‘CREATE DATABASE BookManagement’在mysql命令窗口中提早建立
        'USER': 'root',   #你的數據庫用戶名
        'PASSWORD': '***', #你的數據庫密碼
        'HOST': '', #你的數據庫主機,留空默認爲localhost
        'PORT': '3306', #你的數據庫端口
    }
}

2 編輯項目文件夾下的__init__.py :
因爲mysql在Django中默認驅動是MySQLdb, 而該驅動不適用於python3, 所以,咱們須要更改驅動爲PyMySQL

import pymysql

pymysql.install_as_MySQLdb()

 

3 顯示SQL語句
前面咱們說了ORM將高層的面向對象的操做,轉換爲低層的SQL語句,若是想在終端打印對應的SQL語句,能夠在setting.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',
        },
    }
}

 

3、模型

在ORM中,用模型(Model)表示數據庫中一張表。模型的具體實現是一個Python類,類中的屬性對應數據庫表中的字段,這個類的實例化對象,對應表中的一條記錄。
總結:類 –> 表; 類屬性 –> 表字段; 類實例 –> 表記錄

定義模型

定義模型就是定義一個python類,以建立一個圖書管理系統爲例,基本形式以下:

from django.db import models

class Publish(models.Model):
    name = models.CharField(max_length=60)
    addr = models.CharField(max_length=60)

    def __str__(self):
        return self.name


class Author(models.Model):
    name = models.CharField(max_length=30)

    def __str__(self):
        return self.name


class Book(models.Model):
    name = models.CharField(max_length=60)
    price = models.DecimalField(max_digits=6, decimal_places=2)
    publish = models.ForeignKey(Publish)
    # 定義書與出版社的多對一關係
    # 默認綁定到Publish表中的主鍵字段
    authors = models.ManyToManyField(Author)
    # 定義書與做者的多對多關係,ORM將自動建立多對多關係的第三張表

 

說明:
1. 定義完模型後,或者修改了模型後,要執行數據庫遷移操做:
python manage.py makemigrations
python manage.py migrate
執行完命令後,查看數據庫的表目錄,能夠看到上述表格成功建立:
這裏寫圖片描述
2. 上述模型中都沒有設置主鍵,在完成上遷移操做後,orm會自動建立主鍵。
3. orm會自動將Book表中的關聯字段publish, 在數據庫中存爲publish_id, 因此不要多此一舉本身命名爲publish_id,不然你在數據庫中看到的是publish_id_id
4. 外鍵引用的主表要麼在子表前建立,要麼使用字符串形式指定,不然子表找不到主表。
5. 若是咱們實例化一個Book對象,book_obj, 那麼經過book_obj.publish獲得的是publish_id對應的那個Publish對象。這是ORM做的設定,緣由很簡單,若是經過book_obj.publish獲得只是一個publish_id,對咱們並無多大用。
6. 雖然不是強制的,可是建議在每一個類中定義__str__方法(或__repr__方法),這樣當咱們打印對象時,能夠顯示具備可讀性的字符串信息,方便調試。
7. 只要在一張表中定義了多對多關係,orm會自動建立實現多對多關係的第三張表。固然,你也能夠手動建立,以下:

class BookToAuthor(models.Model): # 手動建立書與做者的多對多關係的第三張表 book = models.ForeignKey(Book) author = models.ForeignKey(Author)

 

仍是不建議手動建立,一是麻煩,而是後面我在執行刪除記錄的操做時,提示找不到第三關聯表,多是我表名命名問題,猜想應該將第三張表命名爲Book_authors的格式,這樣才能和orm自動建立的第三張表同名,未驗證。。。

字段類型

類型名 Python類型 說明
IntegerField int 普通整數,一般是32位,-2147483648 to 2147483647
SmallIntegerField int 小整數,通常是16位,-32768 to 32767
BigIntegerField int/long 64位的整數,-9223372036854775808 to 9223372036854775807
FloatField float 浮點數
DecimalField(max_digits=None, decimal_places=None decimal.Decimal 定點數,精度更高;要求指定位數和小數點精度。
CharField(max_length=None) str 字符串;要求指定最大長度
TextField str 變長字符串
BooleanField bool 布爾值
DateField datetime.date 日期,好比:2017-08-25
DateTimeField datetime.datetime 日期和時間
BinaryField str 二進制

更多類型請參考官網 field types

關係字段

字段 說明
ForeignKey(othermodel) 多對一關係,須要指定關係表
ManyToManyField(othermodel) 多對多關係,須要指定關係表
OneToOneField(othermodel) 一對一關係,須要指定關係表

 on_delete字段見下。

字段選項

選項 說明
primary_key 若是設置primary_key=True, 這列就是表的主鍵;若是不指定,Django會自動添加一個AutoField字段來盛放主鍵,因此咱們通常無需設定主鍵。
unique 若是設置unique=True, 這列不容許出現重複的值
db_index 若是設置db_index=True, 爲這列建立索引,提高查詢效率
null 若是設置null=True, 這列容許使用空值;若是新增了字段,建議設置該選項,由於新增字段以前的記錄沒有該字段
default 爲這列定義默認值;若是新增了字段,建議設置該選項,由於新增字段以前的記錄沒有該字段
related_name 在一對多關係多所在的表中定義反向引用;這樣在一所在的表中反向查詢多所在的表時,直接用這個字段就好了,能夠替代下面要講到的_set反向查詢
on_delete(外鍵)

on_delete=None,               # 刪除關聯表中的數據時,當前表與其關聯的field的行爲
on_delete=models.CASCADE,     # 刪除關聯數據,與之關聯也刪除
on_delete=models.DO_NOTHING,  # 刪除關聯數據,什麼也不作
on_delete=models.PROTECT,     # 刪除關聯數據,引起錯誤ProtectedError
# models.ForeignKey('關聯表', on_delete=models.SET_NULL, blank=True, null=True)
on_delete=models.SET_NULL,    # 刪除關聯數據,與之關聯的值設置爲null(前提FK字段須要設置爲可空,一對一同理)
# models.ForeignKey('關聯表', on_delete=models.SET_DEFAULT, default='默認值')
on_delete=models.SET_DEFAULT, # 刪除關聯數據,與之關聯的值設置爲默認值(前提FK字段須要設置默認值,一對一同理)
on_delete=models.SET,         # 刪除關聯數據,
  a. 與之關聯的值設置爲指定值,設置:models.SET(值)
  b. 與之關聯的值設置爲可執行對象的返回值,設置:models.SET(可執行對象)

更多選項請參考官網 filed options

4、數據庫的操做

下面經過python shell來演示數據庫的操做。在終端切換到項目根目錄下,輸入命令python manage.py shell進入shell操做環境:

增刪改查

1 建立記錄

方式一:實例化

>>> from app.models import Author #導入模型
>>> a = Author(name="張三")  # 實例化
>>> a.save()  # 插入記錄
>>> print(a)
張三

 

方式二:create()工廠函數

>>> Author.objects.create(name='李四')
<Author: 李四>

 

經過get_or_create()建立記錄,這種方法能夠防止重複(速度稍慢,由於會先查詢數據庫),它返回一個元組,第一個是實例對象(記錄),建立成功返回True,已存在則返回False,不執行建立。

>>> Author.objects.get_or_create(name='李四')
(<Author: 李四>, False)

 

2 查詢記錄

1 Author.objects.all()查詢全部

>>> Author.objects.all() <QuerySet [<Author: 張三>, <Author: 李四>]> 

 

咱們也能夠對查詢結果進行切片索引操做:Author.objects.all()[start:end:step],注意,不支持負索引:

>>> Author.objects.all()[-1]
AssertionError: Negative indexing is not supported.

 

2 Author.objects.filter(name='李四') 過濾查詢

3 萬能的雙下劃線查詢__,對應SQL的where語句
__contains, __regex, __gt, __th, 多個條件之間以逗號分隔

>>> Author.objects.filter(name__contains='李')  # 查詢姓名中包含‘李’的記錄;若是是__icontains則不區分大小寫
<QuerySet [<Author: 李四>, <Author: 李白>]>

>>> Author.objects.filter(name__regex=r'^李') #正則查詢,以‘李’開頭的記錄;若是是__iregex則不區分大小寫
<QuerySet [<Author: 李四>, <Author: 李白>]>

>>> Author.objects.filter(id__gt=3)  # 查詢id大於3的記錄
<QuerySet [<Author: 李白>, <Author: 光緒>, <Author: Martin>]>

>>> Author.objects.filter(id__lt=3) # 查詢id小於3的記錄
<QuerySet [<Author: 張三>]>

 

__in判斷字段在列表內。另外一般用pk指主鍵,不限於id,適用性更好。

models.Server.objects.filter(pk__in=id_list).delete()

其它還有: __startswith(), __istartswith(), __endswith(), __iendswith()
4 Author.objects.get() 只能獲得一個對象,多了少了都報錯

>>> Author.objects.get(name='李四')
<Author: 李四>

 

5 first(), last()獲取查詢結果中的單個對象

>>> Author.objects.filter(id__gt=2).first()  # 獲取第一個
<Author: 李四>
>>> Author.objects.filter(id__gt=2).last()  # 獲取最後一個
<Author: Martin>

 

6 values(*field) 用字典形式,返回指定字段的查詢結果;多個字段間以逗號分隔

>>> Author.objects.values('name')
<QuerySet [{'name': '李白'}, {'name': '光緒'}]>
# values()方法前能夠加過濾條件,若是不加,至關於Author.objects.all().values()

 

7 values_list(*field),同上,用元組形式

<QuerySet [('李白',), ('光緒',)]>

 

8 exclude(**kwargs)反向過濾

>>> Author.objects.exclude(name__contains='魯迅') # 過濾全部姓名不包含‘魯迅的’
<QuerySet [<Author: 李白>, <Author: 光緒>, <Author: Martin>]>

 

9 order_by(*field) 根據字段排序
10 reverse() 反向排序,用在·order_by後面
11 distinct() 剔除重複
12 count() 統計數量
13 exists() QuerySet包含數據返回True, 不然返回False

3 修改記錄

方式一:QuerySet.update(field=var)
修改的前提是先查找,而後調用update(field=val)方法,只有QuerySet集合對象才能調用該方法,也就是說經過get(), first(), last()獲取的對象沒有該方法。

>>> Author.objects.filter(name='李白').update(name='李小白')
1 # 更新了一條記錄
# 對應的SQL語句
UPDATE `app_author` SET `name` = '李小白' WHERE `app_author`.`name` = '李白'; args=('李小白', '李白')

 

方式二:對象賦值,不推薦,效率低

>>> obj = Author.objects.filter(name='李小白').first()
>>> obj.name='李白'
>>> obj.save()
# SQL語句:
UPDATE `app_author` SET `name` = '李白' WHERE `app_author`.`id` = 3; args=('李白', 3) 從SQL語句能夠看出,經過對象賦值的方式,會將該對象的全部字段從新賦值,故而效率低。

 

4 刪除記錄

刪除的前提是先查找,而後調用delete()方法;不一樣於update()方法,delete()支持QuerySet集合對象的刪除,也支持單個對象的刪除。
delete()默認就是級聯刪除:刪除一條記錄後,在多對多關係的關聯表中與該記錄有關的記錄也會刪除。

>>> Author.objects.filter(id=1).delete()
(1, {'app.Book_authors': 0, 'app.Author': 1})   # 該記錄在本表和關聯表中的刪除狀況

 

QuerySet

從數據庫從數據庫查詢出來的結果通常是一個集合,哪怕只有一個對象,這個集合叫QuerySet。
QuerySet特性:
1 支持切片操做
2 可迭代:for循環
3 惰性機制:只有使用QuerySet時,纔會走數據庫,好比執行res = Author.objects.all()時,並不會真正執行數據庫查詢,只是翻譯爲SQL語句。而當咱們執行if res, print res, for obj in res這些操做時,纔會執行SQL語句,進行數據庫查詢。這一點能夠經過在setting.py中加上日誌記錄顯示SQL語句獲得證明。
4 緩存機制:每次執行了數據庫查詢後,會將結果放在QuerySet的緩存中,下次再使用QuerySet時,不走數據庫,直接從緩存中拿數據。緩存機制減小了對數據庫的訪問,有利於提升性能。可是一旦數據庫數據更新,除非從新訪問數據庫,不然緩存也不會更新,下面咱們來證明這一點:

>>> res = Author.objects.all()
>>> for item in res:
...     print(item.name)
...
李白
光緒
魯迅
Martin
>>> Author.objects.create(name="Susan")
<Author: Susan>
# 往數據庫插入一條記錄
>>> for item in res:
...     print(item.name)
...
李白
光緒
魯迅
Martin
# 以上結果能夠驗證QuerySet對象的緩存機制,儘管新插入了一條記錄,但打印結果沒變,說明,它不會從新走數據庫,而是從緩存中拿。

# 下面咱們從新訪問數據庫, 緩存更新,打印出了咱們剛剛插入的那條記錄:
>>> res = Author.objects.all()
>>> for item in res:
...     print(item.name)
...
李白
光緒
魯迅
Martin
Susan

 

提升數據庫性能

iterator()迭代器

若是咱們查詢出的數據很大,QeurySet的緩存確定會崩。解決方案:對QeurySet應用.iterator()方法,將查詢結果轉化爲迭代器。

>>> g = Author.objects.all().iterator()
>>> for item in g:
...     print(item.name)
...
李白
光緒
魯迅
Martin
Susan
>>> for item in g:
...     print(item.name)
...
>>>
# 第一次for循環迭代器迭代完了,因此第二次不會打印出來

 

儘管轉化爲迭代器會節省內存,可是這也意味着,會形成額外的數據庫查詢。

exists()

好比咱們拿到一個QuerySet對象,res = Book.objects.all(),想肯定記錄是否存在,若是用if res,將會查詢數據庫中的全部記錄,這會極大的影響性能,解決方案:if res.exists() 這樣會限定只查詢一條記錄(低層轉化爲SQL語句中的limit 1)

select_related主動連表查詢

提升數據庫性能的關鍵一點是減小對數據庫的查詢,咱們來看一個栗子:

1. 建立一張Role角色表,和UserInfo表,創建一對多關係:

from django.db import models


class Role(models.Model):
    title = models.CharField(max_length=32)


class UserInfo(models.Model):
    name = models.CharField(max_length=32)
    role = models.ForeignKey("Role", null=True)

 

2.往UserInfo表中插入3個用戶,並指定角色:略

3.在視圖中經過以下方式查詢用戶名和用戶的角色名:

def index(request):
    user_list = UserInfo.objects.all()
    for user in user_list:
        print(user.name, user.role.title)
    return HttpResponse('ok')

 

4.在settings.py中配置打印SQL命令;經過瀏覽器訪問http://127.0.0.1:8000/index.html/執行index視圖函數,查看SQL命令的執行結果:

(0.000) SELECT "app01_userinfo"."id", "app01_userinfo"."name", "app01_userinfo"."pwd", "app01_userinfo"."role_id" FROM "app01_userinfo"; args=()
(0.000) SELECT "app01_role"."id", "app01_role"."title" FROM "app01_role" WHERE "app01_role"."id" = 1; args=(1,)
(0.000) SELECT "app01_role"."id", "app01_role"."title" FROM "app01_role" WHERE "app01_role"."id" = 2; args=(2,)
(0.000) SELECT "app01_role"."id", "app01_role"."title" FROM "app01_role" WHERE "app01_role"."id" = 3; args=(3,)

 

SQL語句顯示一共執行了4次數據庫查詢,第一次對應user_list = UserInfo.objects.all(),剩餘三次是for user in user_list: print(user.name, user.role.title) 循環時,針對三個用戶,查詢了三次角色表。若是用戶數量不少,這樣一次次的查詢數據庫,將極大影響數據庫性能。

下面咱們經過select_related執行查詢:

def index(request):
    user_list = UserInfo.objects.all().select_related("role")
    for user in user_list:
        print(user.name, user.role.title)
    return HttpResponse('ok')

 


查看此次的SQL語句:只執行了一次數據庫查詢

(0.001) SELECT "app01_userinfo"."id", "app01_userinfo"."name", "app01_userinfo"."role_id", "app01_role"."id", "app01_role"."title" FROM "app01_userinfo" LEFT OUTER JOIN "app01_role" ON ("app01_userinfo"."role_id" = "app01_role"."id"); args=()

 

select_related('FK')取當前表數據和表外鍵關聯字段,所以,在一次查詢中得到了全部須要的信息。
若是要連多個表,經過雙下劃線鏈接更多外鍵字段便可:
select_related('FK1__FK2')

prefetch_related

咱們將上面的栗子中的select_related改成prefetch_related

def index(request):
    user_list = UserInfo.objects.all().prefetch_related("role")
    for user in user_list:
        print(user.name, user.role.title)
    return HttpResponse('ok')

 

查看SQL語句:

(0.000) SELECT "app01_userinfo"."id", "app01_userinfo"."name", "app01_userinfo"."role_id" FROM "app01_userinfo"; args=()
(0.001) SELECT "app01_role"."id", "app01_role"."title" FROM "app01_role" WHERE "app01_role"."id" IN (1, 2, 3); args=(1, 2, 3)

 

執行了兩次查詢,第二次查詢是經過判斷用戶角色是否在角色表,並將關聯的角色取出來。由於一般用戶數量不少,可是角色相對會少不少,所以,這種方式也減小了對數據庫的訪問。

only

UserInfo.objects.all().only("name")

 

only()方法只取某個字段,所以,若是須要只是須要用到指定的字段,經過這種方式能夠提供性能。區別於values()only()的查詢結果仍是對象,而values()的查詢結果是字典。

defer

only()相反,排除某個字段。

提升數據庫性能——db的讀寫分離

http://www.javashuo.com/article/p-ooajtlsc-gv.html

關聯關係的處理

在視圖函數中操做數據庫的語法與在python shell中是同樣的

添加一對多關係

from django.shortcuts import render, HttpResponse
from  .models import *

def add(request):

    # 方式一,經過真實字段賦值
    Book.objects.get_or_create(
        title = 'chinese',
        price = 10.00,
        publish_id = 1, # Book的publish字段在數據庫中真實表示是publish_id
    )

    # 方式二, 經過對象賦值
    publish_obj = Publish.objects.get(id=2)
    Book.objects.create(
        title ='English',
        price = 18.88,
        publish = publish_obj, #經過對象賦值
    )

    return HttpResponse('OK')

 

添加/解除多對多關係

from django.shortcuts import render, HttpResponse
from  .models import *

def add(request):

    # 添加多對多關係的前提是記錄已經建立好,沒法在建立記錄的同時添加多對多關係
    # 逐個添加 add(obj)
    author_obj1 = Author.objects.get(id=1)
    author_obj2 = Author.objects.get(id=2)
    book_obj = Book.objects.get(id='8')
    book_obj.authors.add(author_obj2, author_obj1)

    # 批量添加 add(queryset)
    author_list = Author.objects.all()
    book_obj = Book.objects.get(id='1')
    book_obj.authors.add(*author_list)
    # * + 列表,將列表傳給函數
    # * + 字典,將字典傳給函數

    # 打印authors --> 對象集合
    book_obj = Book.objects.get(id='8')
    print(book_obj.authors.all())
    # 打印結果:<QuerySet [<Author: Egon>, <Author: Alex>, <Author: 魯迅>, <Author: 光緒>]>

    # 解除部分綁定 remove(obj)
    book_obj = Book.objects.get(id='8')
    author = Author.objects.get(id=2)
    book_obj.authors.remove(author)
    # 若是要解除多個:
    # * + 列表,將列表傳給函數
    # * + 字典,將字典傳給函數

    # 解除全部綁定 clear()
    book_obj = Book.objects.get(id='8')
    book_obj.authors.clear()


    return HttpResponse('OK')

 

多表查詢

正向查詢:經過當前表中存在的字段查詢

例1:一對多:查詢一本書出版社的名字

>>> b = Book.objects.filter(name__contains='現代').first() 
>>> b.publish.name  # b.publish 是一個對象,對應主表Publish中的一條記錄
'復旦出版'
# 經過publish拿到對應主表中的對象,訪問其屬性

 

例2:多對多:查詢一本書的做者

>>> b = Book.objects.get(name='linux')
>>> author_list = b.authors.all()  # 拿到某本書的全部author對象
>>> print(author_list)
<QuerySet [<Author: 李白>, <Author: 光緒>]>

 

以上兩例是基於對象屬性的正向查詢。
例3:查詢某出版社出版了哪些書:

>>> pid = Publish.objects.get(name='人民郵電').id
>>> book_list = Book.objects.filter(publish_id=pid)
# 正向查詢,先拿到出版社id, 而後根據id查詢

 

反向查詢

Publish表中沒有book相關的字段,可是能夠經過反向查詢來作:book_set(用關聯的表名小寫,下劃線加set)來找到與出版社關聯的書籍的對象的集合
仍是例3,若是用反向查詢:

>>> pub = Publish.objects.get(name='人民郵電')
>>> book_list = pub.book_set.all()
>>> print(book_list)
<QuerySet [<Book: linux>, <Book: python>]>

 

book_set : 關聯表名,set集合;all()取出全部數據。
注意,若是是一對一關聯,那麼就不用加_set
基於反向查詢的語法,咱們也能夠執行反向綁定關係:
僞代碼形式:

a = Author.object.get(..) 拿到做者對象
book_list = ... # 拿到書籍對象的集合
a.book_set.add(*book_list.all()) # 經過反向查詢來增長

 

基於values(), filter(), 雙下劃線的多表查詢

以上幾種多表查詢方式都略顯麻煩,如今咱們經過values(), filter(), 雙下劃線,來簡化一下:
例1:查詢一本書出版社的名字(正向思路):

>>> Book.objects.filter(name='水滸傳').values('publish__name')
<QuerySet [{'publish__name': '機械工業'}]>
# publish(子表中的關聯字段) + __(雙下劃線) + name(Publish表中的字段)
# 對應的SQL語句:valuse("publish__name")應用了表聯結:
SELECT `app_publish`.`name` FROM `app_book` INNER JOIN `app_publish` ON (`app_book`.`publi
sh_id` = `app_publish`.`id`) WHERE `app_book`.`name` = '水滸傳' LIMIT 21; args=('水滸傳',)

 

例2: 查詢出版了某本書的的出版社名字(反向思路):

>>> Publish.objects.filter(book__name='linux').values('name')
<QuerySet [{'name': '人民郵電'}]>
# book(子表名) + __(雙下劃線) + name(子表中的字段)
# 對應的低層SQL語句:filter(book__title="linux")應用了表聯結 
 SELECT `app_publish`.`name` FROM `app_publish` INNER JOIN `app_book` ON (`app_publish`.`id
` = `app_book`.`publish_id`) WHERE `app_book`.`name` = 'linux' LIMIT 21; args=('linux',)

 

例3:查詢價格大於10的書籍的做者姓名:

正向:
Book.objects.filter(price__gt=10).values("authors__name")
# authors(子表與主表關聯字段) + __(雙下劃線) + name(主表目標字段)
反向:
Author.objects.filter(book__price__gt=10).values("name")
# book(子表名) + __(雙下劃線) + price__gt=10(子表字段,條件)

 

聚合&分組查詢

SQL語言中有聚合函數:Avg, Min, Max, Sum, Count,能夠方便進行數據統計;在ORM中,QuerySet的aggregate()方法對此提供了支持,它返回一個統計結果的鍵值對。下面咱們看看如何使用,
基本格式:QuerySet.aggregate(func(field))
例1 查詢某做家出版書籍的價格總和

>>> from django.db.models import Avg, Sum, Min, Max, Count # 導入聚合函數
>>> Book.objects.filter(authors__name='魯迅').aggregate(Sum('price'))
{'price__sum': Decimal('39.90')}
# orm會根據字段和和聚合函數自動拼接鍵,值是聚合值;
# 也能夠自定義key, 經過以下方式:
QuerySet.aggregate('your key' = Sum(field))

 

若是要統計多個做者,那就要用到分組查詢,QuerySet的anotate()方法對此提供了支持。
例2 每一個做者出版過的書的平均價格

>>> from django.db.models import Avg, Sum, Min, Max, Count # 導入聚合函數
>>> Book.objects.values('authors__name').annotate(Avg('price'))
# values()根據做者名字進行分組,annotate()顯示分組後的統計結果

 

F&Q查詢

不少時候單一的關鍵字查詢沒法知足查詢要求,可使用F&和Q查詢,使用前請先導入:
from django.db.models import F, Q

F對字段取值

F用於取字段取值,咱們來看一個例子:
對數據庫中每本書的價格加10元:
Book.objects.all.update(price=price+10)
直接報錯 NameError: name ‘price’ is not defined,提示price+10中的price未定義,取不到值。下面咱們經過F對price字段取值:

>>> Book.objects.all().update(price=F('price')+10)
# 對應的SQL語句:
UPDATE `app_book` SET `price` = (`app_book`.`price` + 10); args=(10,)

Q組合多個查詢條件

假設咱們要查詢某個做家,價格大於10元的書,那麼filter()函數中經過逗號,放兩個過濾條件能夠實現:

>>> Book.objects.filter(authors__name='光緒', price__gt=10)
<QuerySet [<Book: linux>, <Book: 現代編程方法>]>

 

上面這個狀況,逗號就是處理邏輯與。那若是要處理邏輯非,邏輯或,這些過濾條件呢?這時Q查詢就能夠很靈活處理:
1 將查詢條件用Q包起來
2 經過:, & | ~ 且,或,非,運算符來鏈接多個過濾條件
下面咱們看栗子:
例1 查詢某個做家的,或者價格大於10的書

>>> Book.objects.filter(Q(authors__name='光緒') | Q(price__gt=10)) <QuerySet [<Book: python>, <Book: linux>, <Book: 現代編程方法>, <Book: linux>, <Book: 蘇菲的世界>, <Book: 水滸傳>]> # 光緒寫的,或者價格大於10的書

 

例2 查詢非莫個做家寫的,而且是某個出版社的書

>>> Book.objects.filter(~Q(authors__name='李白') & Q(publish__name='機械工業'))
<QuerySet [<Book: 蘇菲的世界>, <Book: 水滸傳>]>
# 不是李白寫的,而且是由機械工業出版社出版的書

 

 

Q查詢的面向對象方式

若是查詢條件是一個以下的字典形式:

search_condictions = {'ID': [1, 2], 'hostname': ['c1.com', 'c2.com']}

 

分析查詢邏輯:

字典中每個元素下鍵對應的列表中的元素:OR

Q('ID'=1) | Q('ID'=2)
Q('hostname'='c1.com') | Q('hostname'='c2.com')

 

字典中ID與hostname – AND, 最終組合查詢條件以下:

Q((Q('ID'=1) | Q('ID'=2)) & (Q('hostname'='c1.com') | Q('hostname'='c2.com')))

 

下面咱們用Q查詢的面向對象方式:

from django.db.models import Q

query = Q()

temp1 = Q()
temp1.connector = 'OR'
temp1.children.append(('ID', 1))
temp1.children.append(('ID', 2))
# 至關於:
# Q('ID'=1) | Q('ID'=2)

temp2 = Q()
temp2.connector = 'OR'
temp2.children.append(('hostname', 'c1.com'))
temp2.children.append(('hostname', 'c2.com'))
# 至關於:
# Q('hostname'='c1.com') | Q('hostname'='c2.com')

query.add(temp1, 'AND')
query.add(temp2, 'ADN')
# 至關於:
# Q((Q('ID'=1) | Q('ID'=2)) & (Q('hostname'='c1.com') | Q('hostname'='c2.com')))

 

 

當查詢條件長度不肯定時,顯然咱們沒法經過簡單的對Q進行組合來查詢,那麼Q查詢的面向對象方式就能夠發揮用處:

from django.db.models import Q

query = Q()

for k, v in search_condictions.items():
    # k: AND; for i in v: OR
    temp = Q()
    temp.connector = 'OR'
    for i in v:
        temp.children.append((k, i))
    query.add(temp, 'AND')

res = models.Server.objects.filter(query).all()

 

多表查詢和表建立總結

多表查詢:正向查詢用字段,反向查詢用表名(小寫)

  1. 一對一關係:

    # 正向:
    
    b_obj = a_obj.field
    
    # 反向:由於是一對一,全部查詢出來只有一個,不須要_set
    
    a_obj = b_obj.model
  2. 一對多關係:

    # 正向:
    
    b_obj = a_obj.field
    
    # 反向:_set取到集合
    
    QuerySet_obj = b_obj.model_set.all()
  3. 多對多關係:

    # 正向:
    
    QuerySet_obj = a_obj.field.all()
    
    # 反向:
    
    QuerySet_obj = b_obj.model_set.all()

建立表:多表關係的建立

class Article(models.Model):
    # 自定義主鍵;通常不須要定義,默認會本身建立。
    nid = models.BigAutoField(primary_key=True)

    title = models.CharField(max_length=50, verbose_name='文章標題')

    # 一對一關係;to_field屬性通常不用定義,orm會自動找到關聯表的主鍵字段
    body = models.OneToOneField(verbose_name='文章內容', to='ArticleDetail', to_field='nid')

    # 一對多關係
    blog = models.ForeignKey(verbose_name='所屬博客', to='Blog', to_field='nid')

    # 多對多關係;默認自動建立第三張表,經過定義through和through_fields屬性,來手動定義多對多關係。若是須要操做第三張表,選擇手動定義。
    tags = models.ManyToManyField(
        to="Tag",
        through='Article2Tag',
        through_fields=('article', 'tag'),
    )

    # 靜態字段
    type_choices = [
        (1, "Python"),
        (2, "Linux"),
        (3, "OpenStack"),
        (4, "GoLang"),
    ]

    article_type_id = models.IntegerField(choices=type_choices, default=None)

# 手動建立多對多關聯表
class Article2Tag(models.Model):
    nid = models.AutoField(primary_key=True)
    article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid')
    tag = models.ForeignKey(verbose_name='標籤', to="Tag", to_field='nid')

    class Meta:
        unique_together = [
            ('article', 'tag'),
        ]

 

說明:表中出現靜態字段做爲choices源的字段,存的值是Integer,若是想獲取對應的文本,使用:

obj.get_field_display()便可顯示,省去本身寫循環判斷的麻煩。對於這裏來講,field是article_type_id

本表和本表的關係

自引用一對多

class Menu(models.Model):
    """ 菜單 """
    title = models.CharField(verbose_name='菜單名稱', max_length=32, unique=True)
    parent = models.ForeignKey(verbose_name='父級菜單', to="Menu", null=True, blank=True)
    # 定義本表的自引用一對多關係
    # blank=True 意味着在後臺管理中填寫能夠爲空,根菜單沒有父級菜單

 

class Customer(models.Model):
    """ 客戶表 """
    name = models.CharField(verbose_name='姓名', max_length=16)
    gender_choices = ((1, '男'), (2, '女'))
    gender = models.SmallIntegerField(verbose_name='性別', choices=gender_choices)

    referral_from = models.ForeignKey(
        'self',  # 與本表的自引用一對多
        blank=True,
        null=True,
        verbose_name="轉介紹自客戶",
        help_text="若此客戶是轉介紹自內部會員,請在此處選擇會員姓名",
        related_name="internal_referral"
    )
    # related_name定義反向引用關係,經過該字段直接查找,而不用反向查找。

 

自引用多對多,好比用戶互相關注

class UserInfo(AbstractUser):
    """ 用戶信息 """
    nid = models.BigAutoField(primary_key=True)
    nickname = models.CharField(verbose_name='暱稱', max_length=32)

    fans = models.ManyToManyField(verbose_name='粉絲們',
                                  to='UserInfo',
                                  through='UserFans',
                                  through_fields=('user', 'follower'))


class UserFans(models.Model):
    """ 互粉關係表 """
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey(verbose_name='用戶', to='UserInfo', to_field='nid', related_name='users')
    follower = models.ForeignKey(verbose_name='粉絲', to='UserInfo', to_field='nid', related_name='followers')

    class Meta:
        unique_together = [
            ('user', 'follower'),
        ]

 

繼承自帶用戶表

Django自帶一張用戶表,其中提供了不少字段,包括密文密碼。而用戶自定義的用戶表密碼是明文的,若是須要使用Django自帶用戶表的特性。能夠繼承自帶的用戶表。

  1. 配置settings.py

    AUTH_USER_MODEL='app.UserInfo' # app名 加 表名
    • 1
  2. 繼承AbstractUser表後,自帶用戶表中的全部字段可用,而且能夠定義其它字段。

    from django.contrib.auth.models import AbstractUser
    
    class UserInfo(AbstractUser):
        """ 用戶信息 """
        pass
相關文章
相關標籤/搜索