資料庫檢測

為了幫助您理解並控制您的程式碼所發出的查詢,Django 提供了一個掛鉤,用於在資料庫查詢執行時安裝包裝函式。例如,包裝函式可以計數查詢、測量查詢持續時間、記錄查詢,甚至阻止查詢執行(例如,確保在使用預先提取的資料渲染範本時不會發出查詢)。

包裝函式的模型仿照 中介軟體 – 它們是可呼叫的物件,將另一個可呼叫的物件作為其參數之一。它們呼叫該可呼叫物件以調用(可能已包裝的)資料庫查詢,並且它們可以在該呼叫前後執行任何它們想做的事情。然而,它們是由使用者程式碼建立和安裝的,因此不需要像中介軟體一樣的單獨工廠。

安裝包裝函式是在上下文管理器中完成的 – 因此包裝函式是臨時的,並且特定於您程式碼中的某些流程。

如上所述,包裝函式的一個例子是查詢執行阻擋器。它可能看起來像這樣

def blocker(*args):
    raise Exception("No database access allowed here.")

它將在視圖中使用,以阻止範本中的查詢,如下所示

from django.db import connection
from django.shortcuts import render


def my_view(request):
    context = {...}  # Code to generate context with all data.
    template_name = ...
    with connection.execute_wrapper(blocker):
        return render(request, template_name, context)

傳遞給包裝函式的參數為

  • execute – 可呼叫的物件,應使用其餘參數進行呼叫,以執行查詢。

  • sqlstr,要傳送至資料庫的 SQL 查詢。

  • params – SQL 命令的參數值清單/元組,或者如果包裝的呼叫是 executemany(),則為清單/元組的清單/元組。

  • manybool,指示最終調用的呼叫是 execute() 還是 executemany() (以及是否預期 params 為值序列,還是值序列的序列)。

  • context – 包含有關呼叫上下文的更多資料的字典。這包括連線和游標。

使用這些參數,一個稍微複雜的阻擋器版本可以在錯誤訊息中包含連線名稱

def blocker(execute, sql, params, many, context):
    alias = context["connection"].alias
    raise Exception("Access to database '{}' blocked here".format(alias))

對於更完整的範例,查詢記錄器可能看起來像這樣

import time


class QueryLogger:
    def __init__(self):
        self.queries = []

    def __call__(self, execute, sql, params, many, context):
        current_query = {"sql": sql, "params": params, "many": many}
        start = time.monotonic()
        try:
            result = execute(sql, params, many, context)
        except Exception as e:
            current_query["status"] = "error"
            current_query["exception"] = e
            raise
        else:
            current_query["status"] = "ok"
            return result
        finally:
            duration = time.monotonic() - start
            current_query["duration"] = duration
            self.queries.append(current_query)

若要使用此功能,您需要建立一個記錄器物件並將其安裝為包裝函式

from django.db import connection

ql = QueryLogger()
with connection.execute_wrapper(ql):
    do_queries()
# Now we can print the log.
print(ql.queries)

connection.execute_wrapper()

execute_wrapper(wrapper)

傳回一個上下文管理器,該管理器在進入時,會在資料庫查詢執行周圍安裝一個包裝函式,並在退出時移除該包裝函式。包裝函式安裝在執行緒本機連線物件上。

wrapper 是一個可呼叫的物件,接受五個參數。它在上下文管理器的範圍內的每個查詢執行時都會被呼叫,參數為 executesqlparamsmanycontext,如上所述。它預期會呼叫 execute(sql, params, many, context) 並傳回該呼叫的傳回值。

返回頂部