Melakukan permintaan SQL mentahLink to this heading

Django gives you three ways of performing raw SQL queries: you can embed raw SQL fragments into ORM queries using RawSQL (see Raw SQL fragments), use Manager.raw() to perform raw queries and return model instances, or avoid the model layer entirely and execute custom SQL directly.

Raw SQL fragmentsLink to this heading

In some cases, you may need to embed raw SQL fragments directly into ORM queries — for example, in annotate() or filter() calls. Use Func() expressions for calling database functions across backends, or RawSQL for arbitrary parameterized SQL fragments.

Melakukan permintaan mentahLink to this heading

Metode pengelola raw() dapat digunakan untuk melakukan permintaan SQL mentah yang mengembalikan instance model:

Manager.raw(raw_query, params=(), translations=None)Link to this definition

This method takes a raw SQL query, executes it, and returns a django.db.models.query.RawQuerySet instance. This RawQuerySet instance can be iterated over like a normal QuerySet to provide object instances.

Ini adalah terbaik digambarkan dengan sebuah contoh. Kiranya anda mempunyai model berikut:

Code
class Person(models.Model):
    first_name = models.CharField(...)
    last_name = models.CharField(...)
    birth_date = models.DateField(...)

You could then execute custom SQL like so:

Python console
>>> for p in Person.objects.raw("SELECT * FROM myapp_person"):
...     print(p)
...
John Smith
Jane Jones

This example isn't very exciting -- it's exactly the same as running Person.objects.all(). However, raw() has a bunch of other options that make it very powerful.

Memetakan bidang permintaan ke bidang modelLink to this heading

raw() secara otomatis memetakan bidang-bidang dalam permintaan ke bidang pada model.

The order of fields in your query doesn't matter. In other words, both of the following queries work identically:

Python console
>>> Person.objects.raw("SELECT id, first_name, last_name, birth_date FROM myapp_person")
>>> Person.objects.raw("SELECT last_name, birth_date, first_name, id FROM myapp_person")

Matching is done by name. This means that you can use SQL's AS clauses to map fields in the query to model fields. So if you had some other table that had Person data in it, you could easily map it into Person instances:

Python console
>>> Person.objects.raw("""
...     SELECT first AS first_name,
...            last AS last_name,
...            bd AS birth_date,
...            pk AS id,
...     FROM some_other_table
...     """)
...

Selama nama-nama cocok, instance model akan dibuat dengan benar.

Alternatively, you can map fields in the query to model fields using the translations argument to raw(). This is a dictionary mapping names of fields in the query to names of fields on the model. For example, the above query could also be written:

Python console
>>> name_map = {"first": "first_name", "last": "last_name", "bd": "birth_date", "pk": "id"}
>>> Person.objects.raw("SELECT * FROM some_other_table", translations=name_map)

Pencarian indeksLink to this heading

raw() supports indexing, so if you need only the first result you can write:

Python console
>>> first_person = Person.objects.raw("SELECT * FROM myapp_person")[0]

However, the indexing and slicing are not performed at the database level. If you have a large number of Person objects in your database, it is more efficient to limit the query at the SQL level:

Python console
>>> first_person = Person.objects.raw("SELECT * FROM myapp_person LIMIT 1")[0]

Menangguhkan bidang-bidang modelLink to this heading

Fields may also be left out:

Python console
>>> people = Person.objects.raw("SELECT id, first_name FROM myapp_person")

The Person objects returned by this query will be deferred model instances (see defer()). This means that the fields that are omitted from the query will be loaded on demand. For example:

Python console
>>> for p in Person.objects.raw("SELECT id, first_name FROM myapp_person"):
...     print(
...         p.first_name,  # This will be retrieved by the original query
...         p.last_name,  # This will be retrieved on demand
...     )
...
John Smith
Jane Jones

From outward appearances, this looks like the query has retrieved both the first name and last name. However, this example actually issued 3 queries. Only the first names were retrieved by the raw() query -- the last names were both retrieved on demand when they were printed.

There is only one field that you can't leave out - the primary key field. Django uses the primary key to identify model instances, so it must always be included in a raw query. A FieldDoesNotExist exception will be raised if you forget to include the primary key.

Melewati parameter kedalam raw()Link to this heading

If you need to perform parameterized queries, you can use the params argument to raw():

Python console
>>> lname = "Doe"
>>> Person.objects.raw("SELECT * FROM myapp_person WHERE last_name = %s", [lname])

params is a list or dictionary of parameters. You'll use %s placeholders in the query string for a list, or %(key)s placeholders for a dictionary (where key is replaced by a dictionary key), regardless of your database engine. Such placeholders will be replaced with parameters from the params argument.

Menjalankan penyesuaian SQL langsungLink to this heading

Terkadang bahkan Manager.raw() tidak cukup: anda mungkin butuh melakukan permintaan yang tidak memetakan dengan bersih pada model, atau langsung menjalankan permintaan UPDATE, INSERT, atau DELETE.

Dalam kasus-kasus ini, anda dapat selalu mengakses basisdata secara langsung, fungsi disekitar lapisan model sepenuhnya.

Obyek django.db.connection mewakili hubungan basisdata awalan. Untuk menggunakan hubungan basisdata, panggil connection.cursor() untuk mendapatkan obyek kursor. Kemudian, panggil cursor.execute(sql, [params]) untuk menjalankan SQL dan cursor.fetchone() atau cursor.fetchall() untuk mengembalikan baris hasil.

Sebagai contoh:

Code
from django.db import connection


def my_custom_sql(self):
    with connection.cursor() as cursor:
        cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])
        cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
        row = cursor.fetchone()

    return row

Untuk melindungi terhadap penyuntikan SQL, anda tidak harus menyertakan kutipan disekitar placeholder %s dalam string SQL.

Catat bahwa jika anda ingin menyertakan harfiah tanda persen dalam permintaan, anda telah menggandakan mereka dalam kasus anda sedang melewatkan parameter:

Code
cursor.execute("SELECT foo FROM bar WHERE baz = '30%'")
cursor.execute("SELECT foo FROM bar WHERE baz = '30%%' AND id = %s", [self.id])

Jika anda sedang menggunakan more than one database 1, anda dapat menggunakan django.db.connections untuk mengambil hubungan (dan kursor ) untuk basisdata khusus. django.db.connections adalah obyek seperti-dictionary yang mengizinkan anda mengambil hubungan khusus menggunakan nama lainnya:

Code
from django.db import connections

with connections["my_db_alias"].cursor() as cursor:
    # Your code here
    ...

Secara awalan, API DB Python akan mengembalikan hasil tanpa nama-nama bidang mereka, yang berarti anda jadi list dari nilai, daripada dict. Pada penampilan kecil dan biaya memori, anda dapat mengembalikan hasil sebagai dict dengan menggunakan sesuatu seperti ini:

Code
def dictfetchall(cursor):
    """
    Return all rows from a cursor as a dict.
    Assume the column names are unique.
    """
    columns = [col[0] for col in cursor.description]
    return [dict(zip(columns, row)) for row in cursor.fetchall()]

Pilihan lain adalah menggunakan collections.namedtuple() dari pustaka standar Python. Sebuah namedtuple adalah obyek seperti-tuple yang mempunyai bidang-bidang diakses oleh atribut pencarian; itu juga dapat diindeks dan berulang. Hasilnya adalah tetap dan dapat diakses oleh bidang nama atau indeks, yang mungkin berguna:

Code
from collections import namedtuple


def namedtuplefetchall(cursor):
    """
    Return all rows from a cursor as a namedtuple.
    Assume the column names are unique.
    """
    desc = cursor.description
    nt_result = namedtuple("Result", [col[0] for col in desc])
    return [nt_result(*row) for row in cursor.fetchall()]

The dictfetchall() and namedtuplefetchall() examples assume unique column names, since a cursor cannot distinguish columns from different tables.

Here is an example of the difference between the three:

Python console
>>> cursor.execute("SELECT id, parent_id FROM test LIMIT 2")
>>> cursor.fetchall()
((54360982, None), (54360880, None))

>>> cursor.execute("SELECT id, parent_id FROM test LIMIT 2")
>>> dictfetchall(cursor)
[{'parent_id': None, 'id': 54360982}, {'parent_id': None, 'id': 54360880}]

>>> cursor.execute("SELECT id, parent_id FROM test LIMIT 2")
>>> results = namedtuplefetchall(cursor)
>>> results
[Result(id=54360982, parent_id=None), Result(id=54360880, parent_id=None)]
>>> results[0].id
54360982
>>> results[0][0]
54360982

Hubungan dan kursorLink to this heading

connection dan cursor kebanyakan menerapkan API-DB Python standar digambarkan dalam pep:249 — kecuali ketika itu datang pada transaction handling 1.

Jika anda akrab dengan DB-API Python, catat bahwa pernyataan SQL dalam cursor.execute() menggunakan placeholder, "%s", daripada menambahkan parameter langsung dalam SQL. Jika anda menggunakan teknik ini, pustaka basisdata pokok akan otomatis meloloskan parameter anda seperlunya.

Juga catat bahwa Django mengharapkan placeholder "%s", bukan placeholder "?", yang digunakan oleh pengikatan Python SQLite. Ini adalah untuk kebaikan dari ketetapan dan kesegaran.

Menggunakan sebuah kursor sebagai pengelola konteks:

Code
with connection.cursor() as c:
    c.execute(...)

setara pada:

Code
c = connection.cursor()
try:
    c.execute(...)
finally:
    c.close()

Memanggil prosedur penyimpananLink to this heading

CursorWrapper.callproc(procname, params=None, kparams=None)Link to this definition

Panggilan sebuah store procedure dengan nama diberikan. Sebuah urutan (params) atau dictionary (kparams) dari parameter masukan mungkin disediakan. Kebanyakan basisdata tidak mendukung kparams. Dari backend siap-pakai Django, hanya Oracle mendukung itu.

Sebagai contoh, diberikan ini prosedur penyimpanan dalam sebuah basisdata Oracle:

SQL
CREATE PROCEDURE "TEST_PROCEDURE"(v_i INTEGER, v_text NVARCHAR2(10)) AS
    p_i INTEGER;
    p_text NVARCHAR2(10);
BEGIN
    p_i := v_i;
    p_text := v_text;
    ...
END;

Ini akan memanggil itu:

Code
with connection.cursor() as cursor:
    cursor.callproc("test_procedure", [1, "test"])