Django4.0 模型和數(shù)據(jù)庫(kù)-數(shù)據(jù)庫(kù)工具

2022-03-16 18:01 更新

為了幫助你更好的理解并控制由你的代碼所產(chǎn)生的數(shù)據(jù)庫(kù)查詢,Django提供了一個(gè)鉤子函數(shù),在這個(gè)鉤子函數(shù)中你可以在數(shù)據(jù)庫(kù)查詢方法外層添加一層?wrappers?方法。 ?wrappers?方法可以記錄數(shù)據(jù)庫(kù)查詢的數(shù)量, 計(jì)算查詢持續(xù)的事件, 為查詢記錄日志, 甚至可以阻止查詢的執(zhí)行(例如在渲染使用了預(yù)取的數(shù)據(jù)的模板時(shí)確保沒(méi)有數(shù)據(jù)庫(kù)查詢被執(zhí)行).

裝飾器是在 ?middleware ?之后建模的--它們是可調(diào)用的,并把其他調(diào)用作為它們的參數(shù)之一。它們調(diào)用可調(diào)用函數(shù)來(lái)調(diào)用(可能是包裝的)數(shù)據(jù)庫(kù)查詢,并且它們可以圍繞這個(gè)調(diào)用做一些工作。然而,它們通過(guò)用戶代碼來(lái)創(chuàng)建和安裝,因此不需要獨(dú)立像中間件這樣的獨(dú)立文件。

wrapper?方法的安裝是在上下文管理器中完成的 -- 因此?wrapper?方法是暫時(shí)的, 也是針對(duì)于你代碼里的某些特定邏輯的.

正如上面提到的, 一個(gè)使用?wrapper?方法的例子是阻塞查詢的執(zhí)行. 類似的代碼為:

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

它可以被用在視圖里阻止來(lái)自模板的查詢,如下所示:

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)

發(fā)給wrapper方法的參數(shù)是:

  • ?execute ?-- 一個(gè)可以被執(zhí)行的對(duì)象, 使用剩下的參數(shù)觸發(fā)來(lái)執(zhí)行查詢。
  • ?sql ?-- 一個(gè) ?str?,要發(fā)送到數(shù)據(jù)庫(kù)的SQL 查詢。
  • ?params ?-- SQL命令行參數(shù)值的列表/二元組,或者列表集/二元組集的一個(gè)列表/二元組(如果包裝過(guò)的調(diào)用是 ?executemany()? 的話)。
  • ?many ?-- 一個(gè)布爾值,標(biāo)識(shí)最終的調(diào)用是否是 ?execute()? 還是 ?executemany()? (以及 ?params ?是否是一個(gè)值序列,還是一系列值的序列)。
  • ?context ?-- 一個(gè)字典,包含帶有關(guān)于調(diào)用上下文的數(shù)據(jù)。

使用這個(gè)參數(shù),稍微復(fù)雜一點(diǎn)的阻塞函數(shù)包含在錯(cuò)誤信息中的連接名:

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

有關(guān)更完整的例子,一個(gè)查詢?nèi)罩酒骺雌饋?lái)像這樣:

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)

要使用它,你可以創(chuàng)建一個(gè)日志器對(duì)象,并且將其作為裝飾器來(lái)安裝:

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)

返回一個(gè)上下文管理器,當(dāng)進(jìn)入時(shí),會(huì)安裝一個(gè)圍繞數(shù)據(jù)庫(kù)查詢執(zhí)行的裝飾器,當(dāng)離開(kāi)時(shí),會(huì)移除這個(gè)裝飾器。裝飾器是在本地線程的連接對(duì)象上安裝的。
wrapper 是一個(gè)帶有五個(gè)參數(shù)的可調(diào)用函數(shù)。它在上下文管理器的范圍內(nèi)被每個(gè)查詢調(diào)用,帶著上面所述的五個(gè)參數(shù) ?execute?, ?sql?, ?params?, ?many?,?context ?。它預(yù)計(jì)會(huì)調(diào)用 ?execute(sql, params, many, context) ?,并且返回那個(gè)調(diào)用的返回值。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)