1. 程式人生 > 實用技巧 >ORM執行原生sql, Python指令碼呼叫Django環境, ORM事務, sql模式說明

ORM執行原生sql, Python指令碼呼叫Django環境, ORM事務, sql模式說明

ORM執行原生sql語句

方式一:row

def check_book(request):
    ret = models.Book.objects.raw("select * from app01_book where id = 26")
    # raw只能操作前面表的資料,比如Book
    print(ret) # <RawQuerySet: select * from book where id = 15>
    for i in ret:
        print(i) # Book object
        print(i.price) # 11.00

    return
HttpResponse("OK")

方式二:引入connection

from django.db import connection
    cursor = connection.cursor()
    cursor.execute('select * from app01_book where id = 26')
    print(cursor.fetchall()) 
    # ((26, '希望成功', datetime.date(2020, 7, 3), Decimal('11.00'), 1, None, None, None),)

Python指令碼中呼叫Django環境

任意新建一個檔案,按照如下寫法

import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bookmanage.settings") 
    # 將Django專案中的manage.py中的環境設定拿過來
    import django
    django.setup()
    # 配置Django環境

    from app01 import models
    print(models.Book.objects.all().values("title
"))

ORM鎖和事務

加鎖寫法,必須用在事務裡面

models.Book.objects.select_for_update().all()
'''
        select * from app01_book for update;
    '''

事務寫法:

方式1
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mxshop',
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'USER': 'root',
        'PASSWORD': '123',
        
        "ATOMIC_REQUESTS": True, #全域性開啟事務,繫結的是http請求響應整個過程
        
    },
  
}

方式2  給檢視函式直接加裝飾器,表示整個檢視邏輯中的sql都捆綁為了一個事務操作
from django.db import transaction

# sql一旦出錯,會自動回滾
@transaction.atomic
def viewfunc(request):
      sid = transaction.savepoint()  #建立儲存點
    # This code executes inside a transaction.
    do_stuff()  #事務和變數等處理邏輯是沒關係的,d['xx'] = 'oo'
    ....
    
    do_other_stuff()
    transaction.savepoint_rollback(sid)  #回滾儲存點
        #transaction.savepoint_commit(sid) #提交儲存點
    
方式3  給邏輯加檢視
from django.db import transaction

def viewfunc(request):
    # This code executes in autocommit mode (Django's default).
    do_stuff()

    with transaction.atomic():  # 加事務
        # This code executes inside a transaction.
        do_more_stuff()
        #models.Book.objects.select_for_update().all()

    do_other_stuff()

mysql的sql_mode模式說明及設定

MySQL的sql_mode合理設定

sql_mode是個很容易被忽視的變數,預設值是空值,在這種設定下是可以允許一些非法操作的,比如允許一些非法資料的插入。在生產環境必須將這個值設定為嚴格模式,所以開發、測試環境的資料庫也必須要設定,這樣在開發測試階段就可以發現問題.

sql model 常用來解決下面幾類問題

(1) 通過設定sql mode, 可以完成不同嚴格程度的資料校驗,有效地保障資料準備性。

(2) 通過設定sql model 為寬鬆模式,來保證大多數sql符合標準的sql語法,這樣應用在不同資料庫之間進行遷移時,則不需要對業務sql 進行較大的修改。

(3) 在不同資料庫之間進行資料遷移之前,通過設定SQL Mode 可以使MySQL 上的資料更方便地遷移到目標資料庫中。

sql_mode常用值如下:

ONLY_FULL_GROUP_BY:

對於GROUP BY聚合操作,如果在SELECT中的列,沒有在GROUP BY中出現,那麼這個SQL是不合法的,因為列不在GROUP BY從句中

NO_AUTO_VALUE_ON_ZERO:

該值影響自增長列的插入。預設設定下,插入0或NULL代表生成下一個自增長值。如果使用者 希望插入的值為0,而該列又是自增長的,那麼這個選項就有用了。

STRICT_TRANS_TABLES:

在該模式下,如果一個值不能插入到一個事務表中,則中斷當前的操作,對非事務表不做限制

NO_ZERO_IN_DATE:

在嚴格模式下,不允許日期和月份為零

NO_ZERO_DATE:

設定該值,mysql資料庫不允許插入零日期,插入零日期會丟擲錯誤而不是警告。

ERROR_FOR_DIVISION_BY_ZERO:

在INSERT或UPDATE過程中,如果資料被零除,則產生錯誤而非警告。如 果未給出該模式,那麼資料被零除時MySQL返回NULL

NO_AUTO_CREATE_USER:

禁止GRANT建立密碼為空的使用者

NO_ENGINE_SUBSTITUTION:

如果需要的儲存引擎被禁用或未編譯,那麼丟擲錯誤。不設定此值時,用預設的儲存引擎替代,並丟擲一個異常

PIPES_AS_CONCAT:

將"||"視為字串的連線操作符而非或運算子,這和Oracle資料庫是一樣的,也和字串的拼接函式Concat相類似

ANSI_QUOTES:

啟用ANSI_QUOTES後,不能用雙引號來引用字串,因為它被解釋為識別符

ORACLE的sql_mode設定等同:PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, NO_KEY_OPTIONS, NO_TABLE_OPTIONS, NO_FIELD_OPTIONS, NO_AUTO_CREATE_USER.

如果使用mysql,為了繼續保留大家使用oracle的習慣,可以對mysql的sql_mode設定如下:

在my.cnf新增如下配置

[mysqld]

sql_mode='ONLY_FULL_GROUP_BY,NO_AUTO_VALUE_ON_ZERO,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,

ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,PIPES_AS_CONCAT,ANSI_QUOTES'

注意:MySQL5.6和MySQL5.7預設的sql_mode模式引數是不一樣的,5.6的mode是NO_ENGINE_SUBSTITUTION,其實表示的是一個空值,相當於沒有什麼模式設定,可以理解為寬鬆模式。5.7的mode是STRICT_TRANS_TABLES,也就是嚴格模式。

如果設定的是寬鬆模式,那麼我們在插入資料的時候,即便是給了一個錯誤的資料,也可能會被接受,並且不報錯,例如:我在建立一個表時,該表中有一個欄位為name,給name設定的欄位型別時char(10),如果我在插入資料的時候,其中name這個欄位對應的有一條資料的長度超過了10,例如'1234567890abc',超過了設定的欄位長度10,那麼不會報錯,並且取前十個字元存上,也就是說你這個資料被存為了'1234567890',而'abc'就沒有了,但是我們知道,我們給的這條資料是錯誤的,因為超過了欄位長度,但是並沒有報錯,並且mysql自行處理並接受了,這就是寬鬆模式的效果,其實在開發、測試、生產等環境中,我們應該採用的是嚴格模式,出現這種錯誤,應該報錯才對,所以MySQL5.7版本就將sql_mode預設值改為了嚴格模式,並且我們即便是用的MySQL5.6,也應該自行將其改為嚴格模式,而你記著,MySQL等等的這些資料庫,都是想把關於資料的所有操作都自己包攬下來,包括資料的校驗,其實好多時候,我們應該在自己開發的專案程式級別將這些校驗給做了,雖然寫專案的時候麻煩了一些步驟,但是這樣做之後,我們在進行資料庫遷移或者在專案的遷移時,就會方便很多,這個看你們自行來衡量。mysql除了資料校驗之外,你慢慢的學習過程中會發現,它能夠做的事情還有很多很多,將你程式中做的好多事情都包攬了。

改為嚴格模式後可能會存在的問題:

若設定模式中包含了NO_ZERO_DATE,那麼MySQL資料庫不允許插入零日期,插入零日期會丟擲錯誤而不是警告。例如表中含欄位TIMESTAMP列(如果未宣告為NULL或顯示DEFAULT子句)將自動分配DEFAULT '0000-00-00 00:00:00'(零時間戳),也或者是本測試的表day列預設允許插入零日期 '0000-00-00' COMMENT '日期';這些顯然是不滿足sql_mode中的NO_ZERO_DATE而報錯。

模式設定和修改(以解決上述問題為例):

方式一:先執行select @@sql_mode,複製查詢出來的值並將其中的NO_ZERO_IN_DATE,NO_ZERO_DATE刪除,然後執行set sql_mode = '修改後的值'或者set session sql_mode='修改後的值';,例如:set session sql_mode='STRICT_TRANS_TABLES';改為嚴格模式

此方法只在當前會話中生效,關閉當前會話就不生效了。

方式二:先執行select @@global.sql_mode,複製查詢出來的值並將其中的NO_ZERO_IN_DATE,NO_ZERO_DATE刪除,然後執行set global sql_mode = '修改後的值'。

此方法在當前服務中生效,重新MySQL服務後失效

方法三:在mysql的安裝目錄下,或my.cnf檔案(windows系統是my.ini檔案),新增 sql_mode = ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,

新增my.cnf如下:

[mysqld]

sql_mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER

然後重啟mysql。

此方法永久生效.當然生產環境上是禁止重啟MySQL服務的,所以採用方式二加方式三來解決線上的問題,那麼即便是有一天真的重啟了MySQL服務,也會永久生效了。