Django基礎(3)----模型層-單表操做,多表建立

昨日內容回顧:css

1. {% include '' %}
2. extend

    base.html:

        <html>
            .....
            .....
            .....
            {% block content%}
            {% endblock%}
        </html>
        
    index.html:
        {% extend 'base.html'%}
        <p>python</p>
        {% block content%}
        <p>hello</p>
        {% endblock%} 
        
    子網頁,也能夠設置css和js。好比:
        {% block css %}
        {% endblock %}
        
        {% block js %}
        {% endblock %}
    
models:ORM

     class          -----    表
     屬性變量       -----   字段名稱
     屬性值         -----   字段約束 
     
     對象           ------  記錄
     
     
建立表的流程
    
    1. 在models裏設計模型類
      class Book(models.Model):
            nid=models.AutoField(primary_key=True)
            ...
    2. 更改數據庫爲mysql,在settings中:
        2.1 先註冊app
        INSTALLED_APPS = [
        ...
        'app01',
        ]
        2.2 更改數據庫引擎
        DATABASES = {
            'default': {
                'ENGINE': 'django.db.backends.mysql',   # 數據庫引擎mysql
                'NAME': 'book',       # 你要存儲數據的庫名,事先要建立之
                'USER': 'root',      # 數據庫用戶名
                'PASSWORD': '',      # 密碼
                'HOST': 'localhost', # 主機
                'PORT': '3306',      # 數據庫使用的端口
            }
        }
            
    
    3. 建立數據庫book
    4. 必須安裝2個模塊
        pip3 install pymysql
        pip3 install mysqlclient
    
    5.
       python manage.py makemigrations(同步)
       python manage.py migrate(執行sql)
View Code

1、單表操做

添加表紀錄

create(**kwargs) 創建新對象

返回值是添加的model對象html

方式1 

create方法的返回值book_obj就是插入book表中的python葵花寶典這本書籍紀錄對象python

book_obj=Book.objects.create(title="python葵花寶典",state=True,price=100,publish="蘋果出版社",
pub_date="2012-12-12")

方式2

book_obj=Book(title="python葵花寶典",state=True,price=100,publish="蘋果出版社",pub_date="2012-12-12")
book_obj.save()

舉例:mysql

修改settings.py,打印orm轉換過程當中的sql。最後一行,添加以下內容:git

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}
View Code

修改urls.py,增長add路徑sql

from app01 import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('add/', views.add),
]
View Code

models.py內容以下:數據庫

class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32, unique=True)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    pub_date = models.DateField()
    publish = models.CharField(max_length=32)
View Code

表已經生成了,可是表記錄是空的。django

Pycharm有自帶的工具,能夠操做MySQL。點擊右側的Databaseapp

點擊加號-->Data Source-->MySQLide

這裏提示要安裝MySQL驅動,點擊旁邊的Download

 正在下載安裝

 

出現下面的效果,表示安裝成功。

點擊測試鏈接,出現Successful,表示鏈接成功。

雙擊展開,就能夠看到app01_book表了。雖然models.py裏面的類名是Book,可是實際建立的表名是應用名+models.py裏面的類名。用下劃線分割,大寫字母會變成小寫。

修改views.py,增長add視圖函數。

注意,須要導入models模塊

這裏面的title,price...變量是和Book類的屬性是一一對應的,不然會報錯。

from django.shortcuts import render,HttpResponse
from app01.models import Book
# Create your views here.
def add(request):
    book = Book(title="北京摺疊",price="11.11",pub_date="2012-12-12",publish="蘋果出版社")
    book.save()

    return HttpResponse("添加成功")
View Code

這是第一種方式:實例化一個對象,並傳參。

pub_date的值,必須是xxxx_xx_xx格式。用下劃線分隔,其餘符合是不能夠的。

訪問網頁,效果以下:

雙擊右邊的book表,查看錶記錄。點擊刷新按鈕,就能夠看到數據了。

 注意:只要django執行了視圖函數的那2句代碼,就能夠插入一條記錄。

並不必定,非要在views.py裏面才行。具體放在哪裏,是根據業務邏輯來處理。

通常請求下,是放在views.py裏面。

查看控制檯輸出信息,會看到一條insert語句

(0.001) INSERT INTO `app01_book` (`title`, `price`, `pub_date`, `publish`) VALUES ('北京摺疊', '11.11', '2012-12-12', ' 蘋果出版社'); args=['北京摺疊', '11.11', '2012-12-12', '蘋果出版社']

那麼book.save(),就是執行上面這句SQL,來插入一條記錄的。

 

第二種方式:推薦使用
objects:表示管理器。
Book.objects:表示管理book表。
book.objects.create:表示增長操做。

def add(request):
    book = Book.objects.create(title="放風箏的人", price="14.11", pub_date="2017-12-12", publish="蘋果出版社")
    print(book)
    print(book.title)

    return HttpResponse("添加成功")
View Code

這種方式,一行代碼,就搞定了。

create是有返回值的,create內部裏面有save。


訪問add頁面

查看控制檯

(0.002) INSERT INTO `app01_book` (`title`, `price`, `pub_date`, `publish`) VALUES ('放風箏的人', '14.11', '2017-12-12', '蘋果出版社'); args=['放風箏的人', '14.11', '2017-12-12', '蘋果出版社']
Book object (2)
放風箏的人
View Code

create的返回值是一個類對象,好比:Book object

book_obj.title的返回值,就是create裏面的值。

create執行的SQL和save是同樣的。區別是create代碼更精簡!

 

若是create少一個字段,好比price呢?測試一下

def add(request):
    book_obj = Book.objects.create(title="放風箏的人2",pub_date="2018-12-12",publish="蘋果出版社")

    return HttpResponse("添加成功")
View Code

訪問頁面,提示price不容許爲空

 

更改models.py裏面的price屬性,設置價格屬性爲空

添加字段is_pub,表示是否發佈

class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32, unique=True)
    price = models.DecimalField(max_digits=8, decimal_places=2,null=True)
    pub_date = models.DateField()
    publish = models.CharField(max_length=32)
    is_pub = models.BooleanField()
View Code

Pycharm有一個工具,不一樣執行繁瑣的同步命令

Tools-->Run manage.py Task...

輸入ma,就會有提示信息。選擇makemigrations,直接回車

 最後輸出以下信息

意思就是is_pub字段必須設置一個默認值

從新修改models.py下的Book類,增長默認值

is_pub = models.BooleanField(default=False)

關閉下面的小窗口,從新運行命令makemigrations

 輸出下面信息,表示成功了

執行migrate

查看錶記錄,發現多了一個is_pub字段。BooleanField對應的MySQL類型是tinyint(1)

那麼False對應的值是0,true對應的值是1。

再次刷新頁面,就能夠了

查看錶記錄,點擊刷新按鈕

 再次刷新頁面,會報錯!由於title字段是惟一的。

 

修改views裏面的add視圖函數

book_obj = Book.objects.create(title="放風箏的人3", pub_date="2018-12-12", publish="蘋果出版社")

再次刷新頁面,就能夠了。查看錶記錄,刷新一下

注意:在開發項目時,最好打開日誌,這樣能夠查看ORM轉換的SQL語句,方便後面的調試。

 

查詢表紀錄

查詢API

<1> all():                  查詢全部結果
  
<2> filter(**kwargs):       它包含了與所給篩選條件相匹配的對象
  
<3> get(**kwargs):          返回與所給篩選條件相匹配的對象,返回結果有且只有一個,
                            若是符合篩選條件的對象超過一個或者沒有都會拋出錯誤。
  
<4> exclude(**kwargs):      它包含了與所給篩選條件不匹配的對象
 
<5> order_by(*field):       對查詢結果排序
  
<6> reverse():              對查詢結果反向排序
  
<8> count():                返回數據庫中匹配查詢(QuerySet)的對象數量。
  
<9> first():                返回第一條記錄
  
<10> last():                返回最後一條記錄
  
<11> exists():              若是QuerySet包含數據,就返回True,不然返回False
 
<12> values(*field):        返回一個ValueQuerySet——一個特殊的QuerySet,運行後獲得的並非一系列
                            model的實例化對象,而是一個可迭代的字典序列
<13> values_list(*field):   它與values()很是類似,它返回的是一個元組序列,values返回的是一個字典序列
 
<14> distinct():            從返回結果中剔除重複紀錄
View Code

all() 查詢全部結果

它返回的是QuerySet數據類型對象,它是django orm 特有的數據類型。

數據格式爲:[model對象1,model對象2,model對象3 ...]

它和下面的 animal_list相似。先是一個列表,列表的元素是一個對象

class Annimal(object):
        def __init__(self, name, age):
            self.name = name
            self.age = age

        def call(self):
            return "汪汪汪"

    dog = Annimal("旺財", 3)
    cat = Annimal("小雪", 4)
    duck = Annimal("小黃", 5)

    animal_list = [dog, cat, duck]
View Code

一個對象,就是一條記錄。有多少條記錄,就有多少個對象。

舉例:

修改urls.py,增長query路徑

urlpatterns = [
    path('admin/', admin.site.urls),
    path('add/', views.add),
    path('query/', views.query),
]
View Code

修改views.py,增長query視圖函數

def query(request):
    ret = Book.objects.all()
    print(ret)
    
    return HttpResponse("查詢成功")
View Code

訪問頁面,效果以下:

查看控制檯輸出信息:

(0.000) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` LIMIT 21; args=()
<QuerySet [<Book: Book object (1)>, <Book: Book object (2)>, <Book: Book object (3)>, <Book: Book object (5)>]>
View Code

它執行的sql,就是select...。返回結果是一個QuerySet數據類型

它是一個列表,裏面放了多個對象。個數取決於返回的行數!

QuerySet和model對象的區別:

只有model對象,才能調用屬性。
queryset不能直接調用屬性,由於它是一個列表。

既然是列表,就可使用for循環,打印title屬性。

修改query視圖函數

def query(request):
    book_list = Book.objects.all()
    print(book_list)
    for obj in book_list:
        print(obj.title)  # 打印title屬性

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯,就會有4個書名

北京摺疊
放風箏的人
放風箏的人2
放風箏的人3
View Code

 

filter(**kwargs):  它包含了與所給篩選條件相匹配的對象

filter能夠用指定條件來查詢,它會返回一條或者多條記錄。

它的返回值是QuerySet數據類型對象

舉例:查看標題爲北京摺疊的記錄

修改query視圖函數

def query(request):
    ret = Book.objects.filter(title='北京摺疊')
    print(ret)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯信息

(0.001) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` WHERE `app01_book`.`title` = '北京摺疊' LIMIT 21; args=('北京摺疊',)
<QuerySet [<Book: Book object (1)>]>
View Code

它執行的sql,是用了where條件的。

注意,下面這行代碼,不能直接執行

print(ret.price)

刷新頁面,報錯。QuerySet不能直接調用屬性,即便它只有一個對象

雖然能夠經過切片來調用屬性,可是不推薦使用。
由於它的長度是不固定的。

須要使用for循環來執行。

 

first(): 返回第一條記錄

first用來取一條記錄,若是返回結果有多條,只會取第一條。

它的返回結果是model對象。

修改query視圖函數

def query(request):
    obj = Book.objects.all().first()
    print(obj)
    
    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

(0.000) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` ORDER BY `app01_book`.`nid` ASC LIMIT 1; args=()
Book object (1)
北京摺疊
View Code

查看sql,發現它使用order by排序。對主鍵nid,作了升序,而且使用limit 1返回一條結果。

ret的返回結果一個是model對象,那麼就能夠直接調用對象了。因此輸出:北京摺疊

學習API接口,重要的是它的返回值是什麼

 

last(): 返回最後一條記錄

last用來取最後一條記錄,若是返回結果有多條,只會取最後一條。

它的返回結果是model對象。能夠直接調用屬性!

修改query視圖函數

def query(request):
    obj = Book.objects.all().last()
    print(obj)
    print(obj.title)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

(0.002) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` ORDER BY `app01_book`.`nid` DESC LIMIT 1; args=()
Book object (5)
放風箏的人3
View Code

查看sql,發現它使用order by排序。對主鍵nid,作了降序,而且使用limit 1返回一條結果。

因此最後輸出:放風箏的人3

 

還能夠對all()進行切片操做

def query(request):
    obj = Book.objects.all()[1:3]
    print(obj)
    for i in obj:
        print(i.title)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

(0.001) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` LIMIT 2 OFFSET 1; args=()
<QuerySet [<Book: Book object (2)>, <Book: Book object (3)>]>
(0.000) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` LIMIT 2 OFFSET 1; args=()
放風箏的人
放風箏的人2
View Code

[1:3],返回2條記錄。切片原則:顧頭不顧尾。

 

get(**kwargs): 返回與所給篩選條件相匹配的對象

返回結果有且只有一個,若是符合篩選條件的對象超過一個或者沒有都會拋出錯誤。

它的返回結果是一個model對象。

舉例:查詢nid爲1的記錄

修改query視圖函數‘

def query(request):
    obj = Book.objects.get(nid=2)
    print(obj)
    print(obj.title)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

(0.001) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` WHERE `app01_book`.`nid` = 2; args=(2,)
Book object (2)
放風箏的人
View Code

查看sql,發現它對應的where條件是nid = 2。返回結果的title屬性爲:放風箏的人

 

舉例:查詢結果大於1時

查看錶記錄,修改第3條記錄的價格爲14.11,並點擊上面的提交按鈕

修改query視圖函數,使用get查詢價格等於14.11的記錄。

def query(request):
    obj = Book.objects.get(price=14.11)
    print(obj)

    return HttpResponse("查詢成功")
View Code

刷新頁面,報錯!提示返回結果過多,不能大於2條。

 

舉例:查詢結果大於1時,也就是記錄不存在時

修改query視圖函數,查詢價格等於110

def query(request):
    obj = Book.objects.get(price=110)
    print(obj)

    return HttpResponse("查詢成功")
View Code

刷新頁面,報錯!提示查詢結果不存在

下面的代碼,也能夠返回一條記錄

obj = Book.objects.filter(nid=2).first()

可是使用first,它會執行order by。上面的代碼轉爲SQL爲:

 SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` WHERE `app01_book`.`nid` = 2 ORDER BY `app01_book`.`nid` ASC LIMIT 1;

那麼它的效果不如直接使用get!就一臺記錄,還排序幹啥?

 

總結:

使用get有且只有一個結果時纔有意義。

推薦使用get時,利用主鍵查詢,由於主鍵是惟一的。

 

exclude(**kwargs): 它包含了與所給篩選條件不匹配的對象

exists用來作排除的,它的返回結果是QuerySet

舉例:查詢價格不等於100的

修改query視圖函數

def query(request):
    obj = Book.objects.exclude(price=100)
    print(obj)
    for i in obj:
        print(i.title,i.price)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

<QuerySet [<Book: Book object (1)>, <Book: Book object (2)>, <Book: Book object (3)>, <Book: Book object (5)>]>
(0.001) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` WHERE NOT (`app01_book`.`price` = '100' AND `app01_book`.`price` IS NOT NULL); args=(Decimal('100'),)
北京摺疊 11.11
放風箏的人 14.11
放風箏的人2 14.11
放風箏的人3 None
View Code

查看sql,發現它對應的where條件用了Not,返回結果是QuerySet

 

order_by(*field): 對查詢結果排序

order_by,默認是升序,它的返回結果是QuerySet

舉例:查詢全部書籍,按照價格排序

修改表記錄,效果以下:

修改query視圖函數

def query(request):
    obj = Book.objects.order_by("price")
    print(obj)
    for i in obj:
        print(i.title,i.price)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

<QuerySet [<Book: Book object (5)>, <Book: Book object (1)>, <Book: Book object (2)>, <Book: Book object (3)>]>
(0.000) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` ORDER BY `app01_book`.`price` ASC; args=()
水滸傳 11.00
北京摺疊 11.11
放風箏的人 14.11
西遊記 233.00
View Code

若是指定的字段值爲空,則按照主鍵排序。

每次for循環查看結果,太麻煩了。能夠在models.py裏面增長一個__str__方法。

修改models.py,增長__str__方法,完整代碼以下:

from django.db import models

# Create your models here.
class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32, unique=True)
    price = models.DecimalField(max_digits=8, decimal_places=2,null=True)
    pub_date = models.DateField()
    publish = models.CharField(max_length=32)
    is_pub = models.BooleanField(default=False)

    def __str__(self):
        return '{}:{}'.format(self.title,self.price)
View Code

修改views.py下的query方法,代碼以下:

def query(request):
    obj = Book.objects.order_by("price")
    print(obj)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

(0.001) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` ORDER BY `app01_book`.`price` ASC LIMIT 21; args=()
<QuerySet [<Book: 水滸傳:11.00>, <Book: 北京摺疊:11.11>, <Book: 放風箏的人:14.11>, <Book: 西遊記:233.00>]>
View Code

能夠直接看到title和價格!

注意:只有QuerySet數據類型對象,才能調用order_by

 

reverse():  對查詢結果反向排序

它的返回結果是QuerySet

舉例:

修改query方法

def query(request):
    obj = Book.objects.all().order_by("price").reverse()
    print(obj)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

<QuerySet [<Book: 西遊記:233.00>, <Book: 放風箏的人:14.11>, <Book: 北京摺疊:11.11>, <Book: 水滸傳:11.00>]>
(0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.000) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` ORDER BY `app01_book`.`price` DESC LIMIT 21; args=()
View Code

它的sql語句用的是desc

 

count(): 返回數據庫中匹配查詢的對象數量。

它的返回結果數據類型是int

它是queryset的終止函數。爲何呢?由於它不能進行鏈式操做!

下面這種,就屬於鏈式操做。由於queryset能夠調用API接口,只要前一個接口的返回值是queryset,它能夠能夠一直調用API接口,除非遇到返回值不是queryset的狀況下,鏈式操做,才能夠終止。由於count的返回值是int,因此到這裏,就結束了!不能再調用API接口了!

Book.objects.all().filter(price__gt=100).order_by("pirce").count()

 

舉例:

修改query視圖函數

def query(request):
    obj = Book.objects.all().count()
    print(obj)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

4
(0.000) SELECT @@SQL_AUTO_IS_NULL; args=None
(0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.000) SELECT COUNT(*) AS `__count` FROM `app01_book`; args=()
View Code

obj的返回結果爲4

 

exists(): 若是QuerySet包含數據,就返回True,不然返回False

它的返回結果是一個布爾值

舉例:若是表裏面有書,輸入ok,不然輸出none

修改query視圖函數,使用常規方法

def query(request):
    ret = Book.objects.all()
    if ret:
        print('ok')

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

ok
(0.000) SELECT @@SQL_AUTO_IS_NULL; args=None
(0.001) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.000) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book`; args=()
View Code

ret結果爲ok,它使用全表查詢,可是這樣有一個bug

若是數據庫有1000萬本,這樣一查,數據庫就崩潰了。

 

修改query視圖函數,使用exists

def query(request):
    ret = Book.objects.all().exists()
    if ret:
        print('ok')

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

ok
(0.000) SELECT @@SQL_AUTO_IS_NULL; args=None
(0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.001) SELECT (1) AS `a` FROM `app01_book` LIMIT 1; args=()
View Code

ret結果爲ok,它用limit 1。它只查詢了一條記錄!

即便數據庫記錄過多,也不影響。這樣纔是合理的!

上述這些API方法,都是QuerySet在調用

下面即將講到的values,values_list,distinct。是一系列操做!

 

values(*field): 返回一個ValueQuerySet

它是一個特殊的QuerySet,運行後獲得的並非一系列model的實例化對象,而是一個可迭代的字典序列

它的返回結果是一個特殊的QuerySet,列表每個元素都是字典

除了使用for循環以外,還有一個更好的方式。使用values,它內部封裝了for循環

舉例:查詢全部書籍名稱

修改query視圖函數

def query(request):
    ret = Book.objects.all().values("title")
    print(ret)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

(0.000) SELECT `app01_book`.`title` FROM `app01_book` LIMIT 21; args=()

<QuerySet [{'title': '北京摺疊'}, {'title': '放風箏的人'}, {'title': '水滸傳'}, {'title': '西遊記'}]>
View Code

values返回的是QuerySet,列表的每個元素都是字典。

values能夠接收多個參數,好比

ret = Book.objects.all().values("title","price")

字典的key,就是參數。一個字典,對應一條記錄。

跟上面講的QuerySet區別就在於:

不加values,QuerySet存放的是model對象。

加了values以後,QuerySet存放的是字典。

查詢某一個字段時,推薦使用values

 

values_list(*field):   values返回的是一個字典序列

它與values()很是類似,它返回的是一個元組序列

它的返回結果是一個特殊的QuerySet,列表每個元素都是元組

舉例:

修改query視圖函數

def query(request):
    book_list = Book.objects.all().values_list("title","price","pub_date")
    print(book_list)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

(0.000) SELECT `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date` FROM `app01_book` LIMIT 21; args=()

<QuerySet [('北京摺疊', Decimal('11.11'), datetime.date(2012, 12, 12)), ('放風箏的人', Decimal('14.11'), datetime.date(2017, 12, 12)), ('西遊記', Decimal('233.00'), datetime.date(2018, 7, 1)), ('水滸傳', Decimal('11.00'), datetime.date(2018, 7, 2))]>
View Code

values_list和values的區別在於:values_list的類型是元組,vallues是字典。

 

distinct(): 從返回結果中剔除重複紀錄

它的返回結果是QuerySet,注意:是一個常規的QuerySet

修改表記錄,將前2本書的價格修改成同樣的。

使用原生sql查詢價格,去除重複的值

mysql> select distinct(price) from app01_book;
+--------+
| price  |
+--------+
| 100.00 |
| 233.00 |
|  11.00 |
+--------+
rows in set (0.00 sec)
View Code

若是使用distinct對主鍵作去重,是沒有意義的。由於主鍵是惟一的!

mysql> select distinct(nid) from app01_book;
+-----+
| nid |
+-----+
|   1 |
|   2 |
|   5 |
|   3 |
+-----+
rows in set (0.01 sec)
View Code

對全部字段作去重,也是沒有意義的,由於每一條記錄,不可能有重複的,這不還有主鍵嘛

修改query視圖函數

def query(request):
    ret = Book.objects.all().distinct()
    print(ret)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

<QuerySet [<Book: 北京摺疊:100.00>, <Book: 放風箏的人:100.00>, <Book: 西遊記:233.00>, <Book: 水滸傳:11.00>]>
(0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.000) SELECT DISTINCT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` LIMIT 21; args=()
View Code

全部記錄都有了,毫無心義

 

舉例:查看全部書籍的價格,結果是不重複的

修改query視圖函數

def query(request):
    ret = Book.objects.all().values("price").distinct()
    print(ret)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

(0.000) SELECT DISTINCT `app01_book`.`price` FROM `app01_book` LIMIT 21; args=()

<QuerySet [{'price': Decimal('100.00')}, {'price': Decimal('233.00')}, {'price': Decimal('11.00')}]>
View Code

舉例:查看全部書籍的價格以及出版社,價格和出版社同時不能重複

修改query視圖函數

def query(request):
    ret = Book.objects.all().values("price","publish").distinct()
    print(ret)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

<QuerySet [{'publish': '蘋果出版社', 'price': Decimal('100.00')}, {'publish': '橘子出版社', 'price': Decimal('233.00')}, {'publish': '橘子出版社', 'price': Decimal('11.00')}]>
(0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.001) SELECT DISTINCT `app01_book`.`price`, `app01_book`.`publish` FROM `app01_book` LIMIT 21; args=()
View Code

發現結果只有3條,它使用了組合去重。2個字段,同時不惟一!

 

基於雙下劃線的模糊查詢

Book.objects.filter(price__in=[100,200,300])
Book.objects.filter(price__gt=100)
Book.objects.filter(price__lt=100)
Book.objects.filter(price__range=[100,200])
Book.objects.filter(title__contains="python")
Book.objects.filter(title__icontains="python")
Book.objects.filter(title__startswith="py")
Book.objects.filter(pub_date__year=2012)
View Code

字段名__gt(大於)

舉例:查詢價格大於100的

不能這麼寫

ret = Book.objects.filter(price>100)
View Code

正確寫法:

def query(request):
    ret = Book.objects.filter(price__gt=100)
    print(ret)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

<QuerySet [<Book: 西遊記:233.00>]>
(0.001) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.000) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` WHERE `app01_book`.`price` > '100' LIMIT 21; args=(Decimal('100'),)
View Code

價格大於100的,只有西遊記

 

字段名__lt(小於)

舉例:查詢價格小於100的

修改query視圖函數

def query(request):
    ret = Book.objects.filter(price__lte=100)
    print(ret)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

<QuerySet [<Book: 北京摺疊:100.00>, <Book: 放風箏的人:100.00>, <Book: 水滸傳:11.00>]>
(0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.000) SELECT VERSION(); args=None
(0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.000) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` WHERE `app01_book`.`price` <= '100' LIMIT 21; args=(Decimal('100'),)
View Code

價格小於等於100的,有北京摺疊,放風箏的人,水滸傳

 

字段名__gte(大於等於)

舉例:查詢價格小於等於100的

修改query視圖函數

def query(request):
    ret = Book.objects.filter(price__gte=100)
    print(ret)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

<QuerySet [<Book: 北京摺疊:100.00>, <Book: 放風箏的人:100.00>, <Book: 西遊記:233.00>]>
(0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.001) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` WHERE `app01_book`.`price` >= '100' LIMIT 21; args=(Decimal('100'),)
View Code

價格小於等於100的,有北京摺疊,放風箏的人,西遊記

 

字段名__in(支持多個選擇)

舉例:查詢價格分別等於100,200,300的記錄

修改query視圖函數

def query(request):
    ret = Book.objects.filter(price__in=[100,200,300])
    print(ret)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

(0.000) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` WHERE `app01_book`.`price` IN ('200', '100', '300') LIMIT 21; args=(Decimal('200'), Decimal('100'), Decimal('300'))
[27/Jun/2018 20:58:08] "GET /query/ HTTP/1.1" 200 12
<QuerySet [<Book: 北京摺疊:100.00>, <Book: 放風箏的人:100.00>]>
View Code

價格等於100,200,300的有 北京摺疊,放風箏的人

 

字段名__range(介於兩個值之間的數據範圍)

舉例:查詢價格在100到233之間的記錄

修改query視圖函數

def query(request):
    ret = Book.objects.filter(price__range=[100,300])
    print(ret)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

<QuerySet [<Book: 北京摺疊:100.00>, <Book: 放風箏的人:100.00>, <Book: 西遊記:233.00>]>
(0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.000) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` WHERE `app01_book`.`price` BETWEEN '100' AND '300' LIMIT 21;arg=(Decimal('100'),Decimal('300'))
View Code

價格在100到233之間的有 北京摺疊,放風箏的人,西遊記

 

字段名__startswith(以什麼開頭的)

舉例:查詢以"北京"開頭的書名有哪些

修改query視圖函數

def query(request):
    ret = Book.objects.filter(title__startswith="北京")
    print(ret)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

(0.000) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` WHERE `app01_book`.`title` LIKE BINARY '北京%' LIMIT 21; args=('北京%',)

<QuerySet [<Book: 北京摺疊:100.00>]>
View Code

以"北京"開頭的書名的有  北京摺疊

 

字段名__contains(以什麼結尾)

舉例:查詢書名包含「傳」的有哪些

修改query視圖函數

def query(request):
    ret = Book.objects.filter(title__contains="")
    print(ret)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

<QuerySet [<Book: 水滸傳:11.00>]>
(0.001) SELECT @@SQL_AUTO_IS_NULL; args=None
(0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.001) SELECT VERSION(); args=None
(0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.000) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` WHERE `app01_book`.`title` LIKE BINARY '%傳%' LIMIT 21; args=('%傳%',)
View Code

書名包含「傳」的有 水滸傳

 

字段名__year(查詢某年)

舉例:查詢出版日期是2018年的有哪些

修改query視圖函數

def query(request):
    ret = Book.objects.filter(pub_date__year="2018")
    print(ret)

    return HttpResponse("查詢成功")
View Code

刷新頁面,查看控制檯輸出信息:

(0.000) SELECT `app01_book`.`nid`, `app01_book`.`title`, `app01_book`.`price`, `app01_book`.`pub_date`, `app01_book`.`publish`, `app01_book`.`is_pub` FROM `app01_book` WHERE `app01_book`.`pub_date` BETWEEN '2018-01-01' AND '2018-12-31' LIMIT 21; args=('2018-01-01', '2018-12-31')

<QuerySet [<Book: 西遊記:233.00>, <Book: 水滸傳:11.00>]>
View Code

出版日期是2018年的有 西遊記,水滸傳

 

刪除表紀錄

刪除方法就是 delete()。它運行時當即刪除對象而不返回任何值。例如:

model_obj.delete()

你也能夠一次性刪除多個對象。每一個 QuerySet 都有一個 delete() 方法,它一次性刪除 QuerySet 中全部的對象。

它的返回值是元組,元組第一個值,是執行狀態。1表示成功,0表示失敗!

例如,下面的代碼將刪除 pub_date 是2005年的 Entry 對象:

Entry.objects.filter(pub_date__year=2005).delete()

在 Django 刪除對象時,會模仿 SQL 約束 ON DELETE CASCADE 的行爲,換句話說,刪除一個對象時也會刪除與它相關聯的外鍵對象。例如:

b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()

要注意的是: delete() 方法是 QuerySet 上的方法,但並不適用於 Manager 自己。這是一種保護機制,是爲了不意外地調用 Entry.objects.delete() 方法致使 全部的 記錄被誤刪除。若是你確認要刪除全部的對象,那麼你必須顯式地調用:

Entry.objects.all().delete()  

若是不想級聯刪除,能夠設置爲:

pubHouse = models.ForeignKey(to='Publisher', on_delete=models.SET_NULL, blank=True, null=True)

 

舉例:刪除價格等於100的

修改urls.py,增長delbook路徑

urlpatterns = [
    path('admin/', admin.site.urls),
    path('add/', views.add),
    path('query/', views.query),
    path('change/', views.change),
    path('delbook/', views.delbook),
]
View Code

 

修改views.py,增長delbook視圖函數

注意:修改是基於查詢的結果來修改的。因此是先有查詢,再有修改!

def delbook(request):
    ret = Book.objects.filter(price=100).delete()
    print(ret)

    return HttpResponse("刪除成功")
View Code

訪問頁面

查看控制檯輸出信息:

(0.001) DELETE FROM `app01_book` WHERE `app01_book`.`price` = '100'; args=(Decimal('100'),)
(1, {'app01.Book': 1})
View Code

 輸出1,表示刪除成功!

查看錶記錄,點擊刷新按鈕,發現少了一條記錄!

 

QuerySet和model均可以調用delete

舉例:查詢價格小於100的,並將第一條記錄刪除

修改delbook視圖函數

def delbook(request):
    ret = Book.objects.filter(price__lte=100).delete()
    print(ret)

    return HttpResponse("刪除成功")
View Code

查看控制檯輸出信息:

(0.000) DELETE FROM `app01_book` WHERE `app01_book`.`price` <= '100'; args=(Decimal('100'),)
[27/Jun/2018 21:41:49] "GET /delbook/ HTTP/1.1" 200 12
(1, {'app01.Book': 1})
View Code

輸出結果爲1,說明刪除成功了!

查看錶記錄,點擊刷新按鈕,發現少了一條記錄!

 

舉例3:根據url的id值,來刪除對應的記錄

若是url的id爲2,就刪除nid爲2的記錄

修改urls.py,爲delbook增長有名分組

注意:要導入re_path模塊,完整代碼以下:

from django.contrib import admin
from django.urls import path,re_path
from app01 import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('add/', views.add),
    path('query/', views.query),
    path('change/', views.change),
    re_path('delbook/(?P<id>\d+)', views.delbook),
]
View Code

修改delbook視圖函數

def delbook(request,id):
    ret = Book.objects.filter(nid=id).delete()
    print(ret)

    return HttpResponse("刪除成功")
View Code

訪問url: http://127.0.0.1:8000/delbook/3

查看控制檯輸出信息:

(0.001) DELETE FROM `app01_book` WHERE `app01_book`.`nid` = 3; args=(3,)
(1, {'app01.Book': 1})
View Code

輸出結果爲1,說明刪除成功了!

查看錶記錄,點擊刷新按鈕,發現少了一條記錄!

 

若是刪除一條不存在的記錄呢?

訪問url:http://127.0.0.1:8000/delbook/4

查看控制檯輸出信息:

(0, {'app01.Book': 0})
(0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.001) SELECT VERSION(); args=None
(0.001) DELETE FROM `app01_book` WHERE `app01_book`.`nid` = 4; args=(4,)
View Code

輸出結果爲0,說明刪除失敗了!

可是頁面提示刪除成功是不對的。

修改delbook視圖函數

def delbook(request,id):
    ret = Book.objects.filter(nid=id).delete()
    print(ret)
    print(ret,type(ret))

    if ret[0]:
        return HttpResponse("刪除成功")
    else:
        return HttpResponse("刪除失敗")
View Code

再次訪問url:http://127.0.0.1:8000/delbook/4

查看控制檯輸出信息:

(0, {'app01.Book': 0})
(0.001) SELECT @@SQL_AUTO_IS_NULL; args=None
(0, {'app01.Book': 0}) <class 'tuple'>
(0.000) SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; args=None
(0.000) SELECT VERSION(); args=None
(0.000) DELETE FROM `app01_book` WHERE `app01_book`.`nid` = 4; args=(4,)
View Code

它的返回值是一個元組,經過取第一個元素,就能夠獲得數字0。那麼就能夠進行if判斷了!

 

修改表紀錄

Book.objects.filter(title__startswith="py").update(price=120)

  此外,update()方法對於任何結果集(QuerySet)均有效,這意味着你能夠同時更新多條記錄update()方法會返回一個整型數值,表示受影響的記錄條數。

update的返回值爲int。1表示成功,0表示失敗。

舉例:修改nid爲1的記錄,將價格修改成1000元

修改urls.py,增長change路徑

urlpatterns = [
    path('admin/', admin.site.urls),
    path('add/', views.add),
    path('query/', views.query),
    path('change/', views.change),
]
View Code

 

修改views.py,增長change視圖函數

注意:修改是基於查詢的結果來修改的。因此是先有查詢,再有修改!

def change(request):
    nid = 1
    ret = Book.objects.filter(nid=nid).update(price=1000)
    print(ret)

    return HttpResponse("修改爲功")
View Code

訪問頁面

查看控制檯輸出信息:

(0.001) UPDATE `app01_book` SET `price` = '1000.00' WHERE `app01_book`.`nid` = 1; args=('1000.00', 1)
1
View Code

舉例:修改西遊記的價格爲133以及is_pub修改成1

修改change視圖函數

def change(request):
    nid = 3
    ret = Book.objects.filter(nid=nid).update(price=133,is_pub=1)
    print(ret)

    return HttpResponse("修改爲功")def change(request):
    nid = 3
    ret = Book.objects.filter(nid=nid).update(price=133,is_pub=1)
    print(ret)

    return HttpResponse("修改爲功")
View Code

刷新頁面,查看控制檯輸出信息:

(0.001) UPDATE `app01_book` SET `price` = '133.00', `is_pub` = 1 WHERE `app01_book`.`nid` = 3; args=('133.00', True, 3)
1
View Code

ret的返回結果爲1,表示修改爲功!

查看錶記錄,點擊刷新按鈕,發現價格和is_pub已經修改了!

相關文章
相關標籤/搜索