itsource

Django가 실행 중인 원시 SQL 쿼리를 확인하는 방법은 무엇입니까?

mycopycode 2022. 9. 16. 23:10
반응형

Django가 실행 중인 원시 SQL 쿼리를 확인하는 방법은 무엇입니까?

쿼리를 실행하는 동안 Django가 실행 중임을 SQL에 표시할 수 있는 방법이 있습니까?

문서 FAQ: "Django가 실행 중인 원시 SQL 쿼리를 확인하는 방법"을 참조하십시오.

django.db.connection.queries:에 SQL목록을 나타냅니다 쿼리가 SQL쿼리의 목록을 포함합니다.

from django.db import connection
print(connection.queries)

쿼리 세트에는 실행할 쿼리를 포함하는 속성도 있습니다.

print(MyModel.objects.filter(name="my name").query)

쿼리 출력은 다음 이유로 유효하지 않습니다.

"Django는 실제로 매개 변수를 보간하지 않습니다. 쿼리 및 매개 변수를 데이터베이스 어댑터에 별도로 전송하여 적절한 작업을 수행합니다."

장고 버그 리포트 #17741에서.

따라서 쿼리 출력을 직접 데이터베이스로 전송해서는 안 됩니다.

만약 당신에게 질의를 다시 설정해야 할 지 많은 질문은 주어진 시간 안에 실행하고 있고 들어 예를을 사용할 수 있특정 기간 동안 몇 개의 쿼리가 실행 중인지 확인하기 위해 쿼리를 재설정해야 하는 경우를 참조하십시오.reset_queries부터에서django.db::

from django.db import reset_queries

reset_queries()
print(connection.queries)
>>> []

django-extensions에는 파라미터가 포함된 명령어 shell_plus가 있습니다.print-sql

./manage.py shell_plus --print-sql

django-shell에서 실행된 모든 쿼리가 인쇄됩니다.

예:

User.objects.get(pk=1)
SELECT "auth_user"."id",
       "auth_user"."password",
       "auth_user"."last_login",
       "auth_user"."is_superuser",
       "auth_user"."username",
       "auth_user"."first_name",
       "auth_user"."last_name",
       "auth_user"."email",
       "auth_user"."is_staff",
       "auth_user"."is_active",
       "auth_user"."date_joined"
FROM "auth_user"
WHERE "auth_user"."id" = 1

Execution time: 0.002466s [Database: default]

<User: username>

debug_toolbar를 참조해 주세요.디버깅에 매우 편리합니다.

문서와 소스는 http://django-debug-toolbar.readthedocs.io/ 에서 구할 수 있습니다.

디버깅 툴바 스크린샷

쿼리는 실제로 모델 API에 포함되어 있습니다.

q = Query.objects.values('val1','val2','val_etc')

print(q.query)

이 방법을 다루는 다른 답변은 없습니다.따라서 다음과 같습니다.

가장 유용하고 간편하며 신뢰할 수 있는 방법은 데이터베이스에 문의하는 것입니다.예를 들어 Postgres용 Linux에서는 다음을 수행할 수 있습니다.

sudo su postgres
tail -f /var/log/postgresql/postgresql-8.4-main.log

데이터베이스마다 절차가 조금씩 다릅니다.데이터베이스 로그에는 원시 SQL뿐만 아니라 django가 시스템에 배치한 연결 설정 또는 트랜잭션 오버헤드가 표시됩니다.

제공된 코드로 할 수 있지만, 디버깅 툴바 앱을 사용하는 것은 쿼리를 표시하는 데 매우 좋은 도구입니다.여기 github에서 다운로드 할 수 있어요.

그러면 지정된 페이지에서 실행된 모든 쿼리와 쿼리 소요 시간을 표시할 수 있습니다.또한 페이지 상의 쿼리 수와 빠른 검토를 위한 총 시간도 요약됩니다.이것은 장고 ORM이 뒤에서 무엇을 하는지 보고 싶을 때 좋은 도구입니다.그 밖에도, 마음에 들면 사용할 수 있는 기능이 많이 있습니다.

또 다른 옵션은 이 게시물에서 설명하는 settings.py의 로깅 옵션을 참조하십시오.

http://dabapps.com/blog/logging-sql-queries-django-13/

debug_debug를 지정하면 개발 서버의 각 페이지 로드가 느려지지만 로깅은 느려지지 않으므로 속도가 빨라집니다.출력을 콘솔 또는 파일로 덤프할 수 있으므로 UI가 좋지 않습니다.그러나 SQL이 많은 뷰의 경우 각 페이지 로드가 너무 느리기 때문에 debug_toolbar를 통해 SQL을 디버깅하고 최적화하는 데 오랜 시간이 걸릴 수 있습니다.

settings.py 파일에 다음 항목이 있는지 확인합니다.

  1. django.core.context_processors.debug에 기재되어에 나열된 있다CONTEXT_PROCESSORS
  2. DEBUG=True
  3. 당신의 당신의.IP그 에서에INTERNAL_IPStuple태플

그럼 그 후,수 있습니다 할에 액세스에 접근해야 한다.sql_queries변수.변수. 나는:이 것처럼 보이는 각 페이지에 대한 글을 덧붙였다.다음과 같은 각 페이지에 바닥글을 추가합니다.

{%if sql_queries %}
  <div class="footNav">
    <h2>Queries</h2>
    <p>
      {{ sql_queries|length }} Quer{{ sql_queries|pluralize:"y,ies" }}, {{sql_time_sum}} Time
    {% ifnotequal sql_queries|length 0 %}
      (<span style="cursor: pointer;" onclick="var s=document.getElementById('debugQueryTable').style;s.disp\
lay=s.display=='none'?'':'none';this.innerHTML=this.innerHTML=='Show'?'Hide':'Show';">Show</span>)
    {% endifnotequal %}
    </p>
    <table id="debugQueryTable" style="display: none;">
      <col width="1"></col>
      <col></col>
      <col width="1"></col>
      <thead>
        <tr>
          <th scope="col">#</th>
          <th scope="col">SQL</th>
          <th scope="col">Time</th>
        </tr>
      </thead>
      <tbody>
        {% for query in sql_queries %}
          <tr class="{% cycle odd,even %}">
            <td>{{ forloop.counter }}</td>
            <td>{{ query.sql|escape }}</td>
            <td>{{ query.time }}</td>
          </tr>
        {% endfor %}
      </tbody>
    </table>
  </div>
{% endif %}

나는 가변 변수를다 찾았어요sql_time_sum라인 행을 추가함으로써를 첨가하여

context_extras['sql_time_sum'] = sum([float(q['time']) for q in connection.queries])

django_src/django/core/debug_processors.py의 디버깅 함수로 이동합니다.

이를 위해 확장기능을 개발했기 때문에 뷰 기능에 데코레이터를 쉽게 배치하여 몇 개의 쿼리를 실행할 수 있는지 확인할 수 있습니다.

설치하는 방법:

$ pip install django-print-sql

콘텍스트 매니저로 사용하려면:

from django_print_sql import print_sql

# set `count_only` to `True` will print the number of executed SQL statements only
with print_sql(count_only=False):

  # write the code you want to analyze in here,
  # e.g. some complex foreign key lookup,
  # or analyzing a DRF serializer's performance

  for user in User.objects.all()[:10]:
      user.groups.first()

데코레이터로 사용하려면:

from django_print_sql import print_sql_decorator


@print_sql_decorator(count_only=False)  # this works on class-based views as well
def get(request):
    # your view code here

Github: https://github.com/rabbit-aaron/django-print-sql

덧붙이자면, 다음과 같은 질문이 있는 경우 django로 입력합니다.

MyModel.objects.all()

다음 작업을 수행합니다.

MyModel.objects.all().query.sql_with_params()

또는 다음과 같이 입력합니다.

str(MyModel.objects.all().query)

sql 문자열을 가져오다

이것은 매우 늦은 답이지만 다른 사람들은 검색을 통해 여기에 왔다.

나는 매우 단순한는 벌목 방법을 소개하고 추가매우 간단한방법을 소개합니다 로깅고 싶다.django.db.backends목꾼. 화화

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

또한 환경변수를 사용하여 레벨을 설정하고 있습니다.SQL 쿼리를 표시할 때는 환경변수를 설정하면 디버깅로그에 실제 쿼리가 표시됩니다.

Django SQL Sniffer는 Django ORM을 사용하는 프로세스에서 발생하는 원시 실행 쿼리를 표시(및 통계 표시)하기 위한 또 다른 대안입니다.지금까지 본 적이 없는 특정 사용 사례를 만족시키기 위해 구축했습니다. 즉, 다음과 같습니다.

  • 대상 프로세스가 실행 중인 소스 코드에 대한 변경 없음(django 설정에서 새 앱을 등록할 필요 없음, 데코레이터 가져오기 등)
  • 로깅 구성에 변경이 없음(예를 들어 설정이 적용되는 프로세스 전체가 아닌 특정 프로세스에 관심이 있기 때문)
  • 타깃 프로세스를 재부팅할 필요가 없음(예를 들어 중요한 컴포넌트이기 때문에 재부팅 시 다운타임이 발생할 수 있음)

따라서 Django SQL Sniffer는 애드혹을 사용하여 이미 실행 중인 프로세스에 연결할 수 있습니다.그런 다음 실행된 쿼리를 "스니핑"하고 실행 시 콘솔에 출력합니다.툴이 정지되면 통계 요약이 몇 가지 가능한 메트릭(카운트, 최대 지속시간 및 총 결합 지속시간)에 따라 특이치 쿼리와 함께 표시됩니다.

다음은 Python 쉘에 첨부한 예시의 스크린샷입니다.

라이브 데모 및 자세한 내용은 github 페이지에서 확인하실 수 있습니다.

이 기능을 프로젝트의 앱 중 하나에 있는 util 파일에 넣었습니다.

import logging
import re

from django.db import connection

logger = logging.getLogger(__name__)

def sql_logger():
    logger.debug('TOTAL QUERIES: ' + str(len(connection.queries)))
    logger.debug('TOTAL TIME: ' + str(sum([float(q['time']) for q in connection.queries])))

    logger.debug('INDIVIDUAL QUERIES:')
    for i, query in enumerate(connection.queries):
        sql = re.split(r'(SELECT|FROM|WHERE|GROUP BY|ORDER BY|INNER JOIN|LIMIT)', query['sql'])
        if not sql[0]: sql = sql[1:]
        sql = [(' ' if i % 2 else '') + x for i, x in enumerate(sql)]
        logger.debug('\n### {} ({} seconds)\n\n{};\n'.format(i, query['time'], '\n'.join(sql)))

그런 다음 필요할 때 Import하여 필요한 컨텍스트(보통 뷰)에서 호출합니다. 예를 들어 다음과 같습니다.

# ... other imports
from .utils import sql_logger

class IngredientListApiView(generics.ListAPIView):
    # ... class variables and such

    # Main function that gets called when view is accessed
    def list(self, request, *args, **kwargs):
        response = super(IngredientListApiView, self).list(request, *args, **kwargs)

        # Call our function
        sql_logger()

        return response

API 뷰(일반적으로 Django Rest Framework)가 있는 경우 템플릿 외부에서 이 작업을 수행하는 것이 좋습니다.

Postgre를 사용하고 있다면 이 방법이 효과가 있을 것이라고 생각합니다.SQL:

from django.db import connections
from app_name import models
from django.utils import timezone

# Generate a queryset, use your favorite filter, QS objects, and whatnot.
qs=models.ThisDataModel.objects.filter(user='bob',date__lte=timezone.now())

# Get a cursor tied to the default database
cursor=connections['default'].cursor()

# Get the query SQL and parameters to be passed into psycopg2, then pass
# those into mogrify to get the query that would have been sent to the backend
# and print it out. Note F-strings require python 3.6 or later.
print(f'{cursor.mogrify(*qs.query.sql_with_params())}')

다음은 https://code.djangoproject.com/ticket/17741을 기반으로 쿼리를 유효한 SQL로 반환합니다.

def str_query(qs):
    """
    qs.query returns something that isn't valid SQL, this returns the actual
    valid SQL that's executed: https://code.djangoproject.com/ticket/17741
    """
    cursor = connections[qs.db].cursor()
    query, params = qs.query.sql_with_params()
    cursor.execute('EXPLAIN ' + query, params)
    res = str(cursor.db.ops.last_executed_query(cursor, query, params))
    assert res.startswith('EXPLAIN ')
    return res[len('EXPLAIN '):]

사용할 수 있는 작은 조각을 만들었습니다.

from django.conf import settings
from django.db import connection


def sql_echo(method, *args, **kwargs):
    settings.DEBUG = True
    result = method(*args, **kwargs)
    for query in connection.queries:
        print(query)
    return result


# HOW TO USE EXAMPLE:
# 
# result = sql_echo(my_method, 'whatever', show=True)

파라미터 함수(sql 쿼리 포함)로 검사하고 해당 함수를 호출하는 데 필요한 arg, kwargs를 사용합니다.그 결과 콘솔에 SQL 쿼리를 반환하고 인쇄하는 함수가 반환됩니다.

django에서 데이터베이스로 결과 쿼리를 가져오려면(올바른 파라미터 치환을 사용하여) 다음 함수를 사용할 수 있습니다.

from django.db import connection

def print_database_query_formatted(query):
    sql, params = query.sql_with_params()
    cursor = connection.cursor()
    cursor.execute('EXPLAIN ' + sql, params)
    db_query = cursor.db.ops.last_executed_query(cursor, sql, params).replace('EXPLAIN ', '')

    parts = '{}'.format(db_query).split('FROM')
    print(parts[0])
    if len(parts) > 1:
        parts = parts[1].split('WHERE')
        print('FROM{}'.format(parts[0]))
        if len(parts) > 1:
            parts = parts[1].split('ORDER BY')
            print('WHERE{}'.format(parts[0]))
            if len(parts) > 1:
                print('ORDER BY{}'.format(parts[1]))

# USAGE
users = User.objects.filter(email='admin@admin.com').order_by('-id')
print_database_query_formatted(users.query)

출력 예시

SELECT "users_user"."password", "users_user"."last_login", "users_user"."is_superuser", "users_user"."deleted", "users_user"."id", "users_user"."phone", "users_user"."username", "users_user"."userlastname", "users_user"."email", "users_user"."is_staff", "users_user"."is_active", "users_user"."date_joined", "users_user"."latitude", "users_user"."longitude", "users_user"."point"::bytea, "users_user"."default_search_radius", "users_user"."notifications", "users_user"."admin_theme", "users_user"."address", "users_user"."is_notify_when_buildings_in_radius", "users_user"."active_campaign_id", "users_user"."is_unsubscribed", "users_user"."sf_contact_id", "users_user"."is_agree_terms_of_service", "users_user"."is_facebook_signup", "users_user"."type_signup" 
FROM "users_user" 
WHERE "users_user"."email" = 'admin@admin.com' 
ORDER BY "users_user"."id" DESC

다음 티켓 코멘트를 기반으로 합니다. https://code.djangoproject.com/ticket/17741#comment:4

일부 사용자 지정 SQL에 대해 쿼리를 재사용해야 하는 경우 유용한 다른 방법이 있습니다.저는 이것을 Django의 ORM이 편하게 할 수 있는 수준을 훨씬 뛰어넘는 분석 앱에서 사용하고 있기 때문에, ORM에서 생성된 SQL을 서브 쿼리로 포함시키고 있습니다.

from django.db import connection
from myapp.models import SomeModel

queryset = SomeModel.objects.filter(foo='bar')

sql_query, params = queryset.query.as_sql(None, connection)

그러면 플레이스 홀더가 있는 SQL과 사용할 쿼리 매개 변수가 있는 태플이 제공됩니다.이를 DB에 직접 전달할 수 있습니다.

with connection.connection.cursor(cursor_factory=DictCursor) as cursor:
    cursor.execute(sql_query, params)
    data = cursor.fetchall()

django.db.connection을 사용하여 쿼리를 표시합니다.쿼리

from django.db import connection
print(connection.queries)

QuerySet 개체에 대한 원시 SQL 쿼리 액세스

 qs = MyModel.objects.all()
 print(qs.query)

Django 2.2의 경우:

대부분의 답변이 사용 시 큰 도움이 되지 않았기 때문에./manage.py shell마침내 답을 찾았다.★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

모든 쿼리를 표시하려면:

from django.db import connection
connection.queries

단일 쿼리에 대한 쿼리를 보려면:

q=Query.objects.all()
q.query.__str__()

q.query물건을 전시하고 있을 뿐이에요.「 」의 __str__()(문자열 표현)에 풀 쿼리가 표시되었습니다.

CREATE / UPDATE / DELETE / 명령어에 대한 SQL을 생성하려면 이 명령어는 Django에 있습니다.

from django.db.models import sql

def generate_update_sql(queryset, update_kwargs):
    """Converts queryset with update_kwargs
    like : queryset.update(**update_kwargs) to UPDATE SQL"""

    query = queryset.query.clone(sql.UpdateQuery)
    query.add_update_values(update_kwargs)
    compiler = query.get_compiler(queryset.db)
    sql, params = compiler.as_sql()
    return sql % params
from django.db.models import sql

def generate_delete_sql(queryset):
    """Converts select queryset to DELETE SQL """
    query = queryset.query.chain(sql.DeleteQuery)
    compiler = query.get_compiler(queryset.db)
    sql, params = compiler.as_sql()
    return sql % params
from django.db.models import sql

def generate_create_sql(model, model_data):
    """Converts queryset with create_kwargs
    like if was: queryset.create(**create_kwargs) to SQL CREATE"""
    
    not_saved_instance = model(**model_data)
    not_saved_instance._for_write = True

    query = sql.InsertQuery(model)

    fields = [f for f in model._meta.local_concrete_fields if not isinstance(f, AutoField)]
    query.insert_values(fields, [not_saved_instance], raw=False)

    compiler = query.get_compiler(model.objects.db)
    sql, params = compiler.as_sql()[0]
    return sql % params

테스트 및 사용방법

    def test_generate_update_sql_with_F(self):
        qs = Event.objects.all()
        update_kwargs = dict(description=F('slug'))
        result = generate_update_sql(qs, update_kwargs)
        sql = "UPDATE `api_event` SET `description` = `api_event`.`slug`"
        self.assertEqual(sql, result)

    def test_generate_create_sql(self):
        result = generate_create_sql(Event, dict(slug='a', app='b', model='c', action='e'))
        sql = "INSERT INTO `api_event` (`slug`, `app`, `model`, `action`, `action_type`, `description`) VALUES (a, b, c, e, , )"
        self.assertEqual(sql, result)

Django의 경우 정확한 SQL 쿼리를 확인하는 가장 좋은 방법은 특정 데이터베이스 자체의 로그를 확인하는 것입니다.

예를 들어, 장고 웹사이트를 개발할 때 Postgre와의 원자거래 쿼리 "BEGIN"과 "COMMIT"를 확인하고 싶었다.SQL(버전 14)

그래서 정확한 Postgre 로그를 생성하기 위해SQL 쿼리에서 다음 코드를 "postgresql.conf" 끝에 추가했습니다.이 코드는 "C:\Program Files\PostgreSQL\14\data\postgresql.conf"를 선택합니다.

log_min_duration_statement = 0

아래 그림과 같이 위의 코드를 "postgresql.conf" 끝에 추가했습니다.

#------------------------------------------------------------------------------
# CUSTOMIZED OPTIONS
#------------------------------------------------------------------------------

# Add settings for extensions here
log_min_duration_statement = 0

그리고 Postgre를 조회한 후Django Admin을 통한 SQL 쿼리, Postgre를 통한 원자 트랜잭션 쿼리 "BEGIN" 및 "COMMIT"를 확인할 수 있었습니다."postgresql-2022-08-20_0000.log"를 열면 다음과 같은 SQL이 나타납니다.이거는 "C:\Program Files\PostgreSQL\14\data\log\postgresql-2022-08-20_000000.log"를 선택합니다.

2022-08-20 22:09:12.549 JST [26756] LOG:  duration: 0.025 ms  statement: BEGIN
2022-08-20 22:09:12.550 JST [26756] LOG:  duration: 1.156 ms  statement: SELECT "store_person"."id", "store_person"."first_name", "store_person"."last_name" FROM "store_person" WHERE "store_person"."id" = 33 LIMIT 21
2022-08-20 22:09:12.552 JST [26756] LOG:  duration: 0.178 ms  statement: UPDATE "store_person" SET "first_name" = 'Bill', "last_name" = 'Gates' WHERE "store_person"."id" = 33
2022-08-20 22:09:12.554 JST [26756] LOG:  duration: 0.784 ms  statement: INSERT INTO "django_admin_log" ("action_time", "user_id", "content_type_id", "object_id", "object_repr", "action_flag", "change_message") VALUES ('2022-08-20T13:09:12.553273+00:00'::timestamptz, 1, 20, '33', 'Bill Gates', 2, '[]') RETURNING "django_admin_log"."id"
2022-08-20 22:09:12.557 JST [26756] LOG:  duration: 1.799 ms  statement: COMMIT

또한 MSQL을 사용하여 정확한 MSQL(Microsoft SQL Server) 쿼리의 로그를 확인할 수도 있지만 SQLite에는 정확한 SQLite 쿼리의 로그를 저장하는 기능이 없는 것 같아 SQLite를 사용하여 정확한 SQLite 쿼리의 로그를 확인할 수 없습니다.

또한 콘솔 상의 정확한 SQL 쿼리 로그를 표시하기 위해 아래 코드를 "settings.py"추가함으로써 @GianMarco의 답변(swer)을 사용했습니다.

"settings.py"

LOGGING = {
    'version': 1,
    'filters': {
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        }
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
        }
    },
    'loggers': {
        'django.db.backends': {
            'level': 'DEBUG',
            'handlers': ['console'],
        }
    }
}

단, 아래 그림과 같이 원자성 트랜잭션 쿼리 "BEGIN""COMMIT" 없이 SQL 쿼리 "SELECT", "UPDATE", "INSERT" 등을 표시했을 뿐입니다.

(0.000) SELECT "store_person"."id", "store_person"."first_name", "store_person"."last_name" FROM "store_person" WHERE "store_person"."id" = 33 LIMIT 21; args=(33,)
(0.000) UPDATE "store_person" SET "first_name" = 'Bill', "last_name" = 'Gates' WHERE "store_person"."id" = 33; args=('Bill', 'Gates', 33)
(0.000) INSERT INTO "django_admin_log" ("action_time", "user_id", "content_type_id", "object_id", "object_repr", "action_flag", "change_message") VALUES ('2022-08-20T13:55:19.226541+00:00'::timestamptz, 1, 20, '33', 'Bill Gates', 2, '[]') RETURNING "django_admin_log"."id"; args=(datetime.datetime(2022, 8, 20, 13, 55, 19, 226541, tzinfo=<UTC>), 1, 20, '33', 'Bill Gates', 2, '[]')

그리고 settings.py에 아래 코드를 추가하여 @geowa4의 답변(swer)을 사용하여 콘솔에 있는 정확한 SQL 쿼리의 로그를 표시하였습니다.* '장고==3.1.7' 사용 :

# "settings.py"

from django.db import connection 

print(connection.queries) # Causes error

그러나 아래 오류가 발생했습니다.

django.core.exceptions.ImproperlyConfigured: settings.DATABASES is improperly configured. Please supply the ENGINE value. Check settings documentation for more details.

그래서 나는 "인쇄(연결을 제거했다.질의를)"아래에 다음 오류는 전혀 콘솔에 정확한 SQL쿼리의 어떠한 통나무들을 못 보여 줬어 해결되었다 보여 주고 있다.

# "settings.py"

from django.db import connection 

# print(connection.queries) # Causes error

언급URL:https://stackoverflow.com/questions/1074212/how-to-see-the-raw-sql-queries-django-is-running

반응형