ฉันจะดูคำสั่ง SQL แบบ raw ที่ Django ทำงานอยู่ได้อย่างไร?


307

มีวิธีแสดง SQL ที่ Django ทำงานอยู่ในขณะที่ทำการสืบค้นหรือไม่?

คำตอบ:


372

ดูเอกสารคำถามที่พบบ่อย: " ฉันจะดูคำถาม SQL ดิบที่ Django กำลังทำงานอยู่ได้อย่างไร "

django.db.connection.queries มีรายการแบบสอบถาม SQL:

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

แบบสอบถามยังมีqueryแอตทริบิวต์ที่มีแบบสอบถามที่จะดำเนินการ:

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

โปรดทราบว่าผลลัพธ์ของแบบสอบถามไม่ใช่ SQL ที่ถูกต้องเพราะ:

"Django ไม่เคย interpolates พารามิเตอร์จริง ๆ : มันส่งแบบสอบถามและพารามิเตอร์แยกต่างหากไปยังการ์ดเชื่อมต่อฐานข้อมูลซึ่งทำการดำเนินการที่เหมาะสม"

จาก Django รายงานข้อผิดพลาด# 17741

เนื่องจากการที่คุณไม่ควรส่งผลลัพธ์แบบสอบถามโดยตรงไปยังฐานข้อมูล


13
เพื่อเป็นการพิสูจน์ในอนาคตคำตอบนี้คุณควรจะเชื่อมโยงเอกสารของ Django เวอร์ชันปัจจุบัน: docs.djangoproject.com/en/dev/faq/models/ …
Andre Miller

5
คำตอบที่ดี อย่างไรก็ตามขอแนะนำให้ใช้str()ฟังก์ชันbuiltin Pythonian ที่ระบุซึ่งเรียกใช้__str__()เมธอดภายใน เช่นstr(MyModel.objects.filter(name="my name").query) ฉันขอแนะนำให้ใช้ IPython และ Django shell ของโครงการของคุณ แท็บเสร็จแล้วให้วิปัสสนาวัตถุ ในฐานะที่เป็น Django เป็นที่รู้จักกันสำหรับรูปแบบการตั้งชื่อที่แน่วแน่วิธีการนี้จึงมีประโยชน์มาก
Lorenz Lo Sauer

7
โปรดทราบว่าผลลัพธ์ของqueryไม่ใช่ SQL ที่ถูกต้องเพราะ "Django ไม่เคย interpolates พารามิเตอร์จริง ๆ : มันส่งแบบสอบถามและพารามิเตอร์แยกต่างหากไปยังอะแดปเตอร์ฐานข้อมูลซึ่งดำเนินการตามความเหมาะสม" ที่มา: code.djangoproject.com/ticket/17741
gregoltsov

3
@AndreMiller คุณควรใช้stableไม่ได้devที่จะเชื่อมโยงไปยังรุ่นปัจจุบันของ Django เช่นนี้docs.djangoproject.com/en/stable/faq/models/...
Flimm

3
django.db.connection.queries ส่งคืนรายการว่าง
fantastory

61

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>

1
ฉันใช้กับ --print-sql หรือกับ SHELL_PLUS_PRINT_SQL = จริงและมันก็ไม่ช่วยฉันยังไม่เห็นข้อความค้นหา คิดอะไรทำไม django 1.8
Dejell

1
คุณต้องตั้งค่า DEBUG = จริงใน settings.py ของคุณเพื่อดูข้อความค้นหา
Konstantin Voschanov

50

ลองดูที่debug_toolbarมันมีประโยชน์มากสำหรับการดีบัก

เอกสารและแหล่งที่มาที่มีอยู่ในhttp://django-debug-toolbar.readthedocs.io/

สกรีนช็อตของแถบเครื่องมือดีบั๊ก


1
debug_toolbar มีประโยชน์อย่างยิ่งเมื่อคุณมีแบบสอบถามที่ล้มเหลวด้วยข้อผิดพลาดทางไวยากรณ์ SQL; มันจะแสดงแบบสอบถามล่าสุดที่พยายามเรียกใช้ (และล้มเหลว) ทำให้ง่ายต่อการตรวจแก้จุดบกพร่อง
scoopseven

สิ่งเดียวคือคุณเห็นข้อความค้นหา SQL บนเบราว์เซอร์ หากคุณเรียกใช้การทดสอบจากเทอร์มินัลและต้องการดูที่นั่นนั่นไม่ใช่วิธีแก้ไข ยังดีมากฉันใช้มันจนถึงทุกวันนี้
Erdin Eray

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

print q.query

คำตอบที่ง่ายมาก! ดี
Espoir Murhabazi

ฟังก์ชั่นนี้ถูกลบไปแล้วหรือยัง? มันไม่ทำงานเมื่อฉันจะm = MyModel.objects.get(...)ตามมาด้วยm.query
SG

นั่นเป็นเพราะmไม่ใช่แบบสอบถามอีกต่อไป ใช้q = MyModel.objects.filter(...)แล้วนั้นq.query m = q.get()
Brouwer

24

ไม่มีคำตอบอื่นที่ครอบคลุมวิธีนี้ดังนั้น:

ฉันพบว่าวิธีที่มีประโยชน์ง่ายที่สุดและน่าเชื่อถือที่สุดคือถามฐานข้อมูลของคุณ ตัวอย่างเช่นบน Linux สำหรับ Postgres คุณอาจทำ:

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

แต่ละฐานข้อมูลจะมีขั้นตอนแตกต่างกันเล็กน้อย ในบันทึกฐานข้อมูลคุณจะเห็นไม่เพียง แต่ SQL ดิบ แต่การตั้งค่าการเชื่อมต่อหรือการทำธุรกรรมค่าใช้จ่าย django จะวางอยู่บนระบบ


8
อย่าลืมที่จะตั้งlog_statement='all'ในpostgresql.confสำหรับวิธีการนี้
RickyA

2
คุณสามารถค้นหาของคุณpostgresql.confโดยวิ่งpsql -U postgres -c 'SHOW config_file'
kramer65

17

แม้ว่าคุณสามารถทำได้ด้วยรหัสที่ให้มา แต่ฉันพบว่าการใช้แอพ debug toolbar เป็นเครื่องมือที่ยอดเยี่ยมในการแสดงข้อความค้นหา คุณสามารถดาวน์โหลดได้จาก GitHub ที่นี่

สิ่งนี้ช่วยให้คุณมีตัวเลือกในการแสดงข้อความค้นหาทั้งหมดที่วิ่งบนหน้าเว็บที่ระบุพร้อมกับเวลาในการสืบค้น นอกจากนี้ยังสรุปจำนวนข้อความค้นหาในหน้าพร้อมกับเวลาทั้งหมดเพื่อการตรวจสอบอย่างรวดเร็ว นี่เป็นเครื่องมือที่ยอดเยี่ยมเมื่อคุณต้องการดูว่า Django ORM ทำอะไรอยู่เบื้องหลัง นอกจากนี้ยังมีคุณสมบัติที่ดีอื่น ๆ อีกมากมายที่คุณสามารถใช้หากคุณต้องการ


2
ดูเหมือนว่าฉันจะเป็นรุ่นที่ดีที่สุด: github.com/django-debug-toolbar/django-debug-toolbar
philfreo

15

ตัวเลือกอื่นดูตัวเลือกการบันทึกใน settings.py ที่โพสต์นี้อธิบาย

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

debug_toolbar ทำให้การโหลดแต่ละหน้าบนเซิร์ฟเวอร์ dev ช้าลงการบันทึกไม่ได้เร็วขึ้น เอาต์พุตสามารถถูกดัมพ์ไปยังคอนโซลหรือไฟล์ดังนั้น UI จึงไม่ดี แต่สำหรับมุมมองที่มี SQL จำนวนมากอาจต้องใช้เวลานานในการดีบักและปรับ SQL ให้เหมาะสมผ่าน debug_toolbar เนื่องจากการโหลดแต่ละหน้าช้ามาก


ยอดเยี่ยม แม้ว่าแถบเครื่องมือจะดูดี แต่ฉันคิดว่าคำตอบนี้ควรเป็นคำตอบที่ยอมรับได้ นี่เป็นวิธีแก้ปัญหาที่ฉันต้องการเพราะช่วยให้ "manage.py runserver" บันทึก SQL ไปยังคอนโซลและทำงานกับ "manage.py migrate" หลังให้ฉันเห็นว่า "ในการลบน้ำตก" ไม่แน่นอนเมื่อตั้งค่าตารางของฉัน เป็นที่น่าสังเกตว่าคำตอบนี้ขึ้นอยู่กับdocs.djangoproject.com/en/1.9/topics/logging/ …
LS

10

หากคุณแน่ใจว่าไฟล์ 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 / context_processors.py


1
ฉันเพิ่งลองและ (ลบส่วน sql_time_sum), ได้รับ: ไม่มีรอบชื่อในเทมเพลต 'คี่แม้' ไม่ได้ถูกกำหนด - ฉันขาดอะไร
ถูกลอยแพ

8

ฉันพัฒนาส่วนขยายสำหรับจุดประสงค์นี้เพื่อให้คุณสามารถวางมัณฑนากรบนฟังก์ชันมุมมองของคุณและดูว่ามีการประมวลผลแบบสอบถามจำนวนเท่าใด

ติดตั้ง:

$ 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


3

ฉันเชื่อว่าสิ่งนี้ควรจะทำงานถ้าคุณใช้ PostgreSQL:

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())}')

สิ่งนี้ใช้ได้แม้ใน Python 2 เฉพาะ refactor เช่น print (cursor.mogrify (* qs.query.sql_with_params ())) เท่านั้นที่ต้องการ
iChux

IIRC Cursor.mogrify ส่งกลับสตริงดังนั้นฉันคิดว่าการใช้งานของสตริงฉสำหรับการจัดรูปแบบเป็นฟุ่มเฟือย ..
Chander

2

ต่อไปนี้จะส่งคืนแบบสอบถามเป็น SQL ที่ถูกต้องตามhttps://code.djangoproject.com/ticket/17741 :

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 '):]

2

ฉันได้สร้างตัวอย่างข้อมูลขนาดเล็กที่คุณสามารถใช้:

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) เพื่อตรวจสอบและ args, kwargs จำเป็นต้องเรียกใช้ฟังก์ชั่นที่ ดังนั้นมันจะส่งคืนฟังก์ชันที่ส่งกลับและพิมพ์เคียวรี SQL ในคอนโซล


1

ฉันใส่ฟังก์ชั่นนี้เป็นไฟล์ 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)))

จากนั้นเมื่อจำเป็นฉันจะนำเข้ามันและเรียกมันจากบริบทใด ๆ (โดยปกติจะเป็นมุมมอง) ที่จำเป็นเช่น:

# ... 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) ก็สามารถใช้งานได้เช่นกัน


1

สำหรับ Django 2.2:

./manage.py shellเป็นที่สุดของคำตอบไม่ได้ช่วยให้ฉันมากเมื่อใช้ ในที่สุดฉันก็พบคำตอบ หวังว่านี่จะช่วยให้ใครบางคน

วิธีดูข้อความค้นหาทั้งหมด:

from django.db import connection
connection.queries

วิธีดูคิวรีสำหรับเคียวรีเดียว:

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

q.queryแค่แสดงวัตถุให้ฉัน การใช้__str__()(การแสดงสตริง) แสดงคำค้นหาแบบเต็ม


0

ดูแบบสอบถามโดยใช้ django.db.connection.queries

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

เข้าถึงแบบสอบถาม SQL ดิบบนวัตถุ QuerySet

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.