Melakukan permintaan SQL mentahLink to this heading

Ketika model query APIs 1 tidak pergi jauh, anda dapat kembali menulis SQL mentah. Django memberikan anda dua cara dari melaksanakan permintaan SQL mentah: anda dapat menggunakan Manager.raw() untuk perform raw queries and return model instances, atau anda dapat menghindari lapisan model seluruhnya dan execute custom SQL directly.

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=None, translations=None)Link to this definition

Metode ini mengambil sebuah permintaan SQL mentah, menjalankan itu, dan mengembalikan sebuah instance django.db.models.query.RawQuerySet. Instance RawQuerySet ini dapat diulang terhadap seperti QuerySet biasa untuk menyediakan instance-instance obyek.

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(...)

Anda dapat kemudian menjalankan SQL penyesuaian seperti itu:

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

Tentu saja, contoh ini tidak sangat menarik -- itu tepatnya sama seperti menjalankan Person.objects.all(). Bagaimanapun, raw() mempuyai banyak dari pilihan lain yang memuat itu lebih bertenaga.

Memetakan bidang permintaan ke bidang modelLink to this heading

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

Urutan dari bidang-bidang daam permintaan anda tidak masalah. Dengan kata lain, kedua dari permintaan berikut bekerja mirip:

Code
>>> 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')
...

Pencocokan selesai berdasarkan nama. Ini berarti bahwa anda dapat menggunakan klausa AS SQL untuk memetakan bidang-bidang dalam permintaan ke bidang-bidang model. Jadi jika anda mempunyai beberapa tabel lain yang mempunyai data Person dalam nya, anda dapat dengan mudah memetakan itu menjadi instance Person:

Code
>>> 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.

Kalau tidak, anda dapat memetakan bidang-bidang dalam permintaan ke bidang-bidang model menggunakan argumen translations pada raw(). Ini adalah nama-nama pemetaan dictionary dalam permintaan pada nama-nama dari bidang pada model. Sebagai contoh, permintaan diatas dapat juga ditulis:

Code
>>> 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() mendukung pengindeksan, jadi jika anda butuh hanya hasil pertama anda dapat menulis:

Code
>>> first_person = Person.objects.raw('SELECT * FROM myapp_person')[0]

Bagaimanapun, pengindeksan dan pemotongan tidak dilakukan pada tingkat basisdata. Jika anda mempunyai sejumlah besar obyek Person dalam basisdata anda, itu lebih efesien membatasi permintaan pada tingkat SQL:

Code
>>> first_person = Person.objects.raw('SELECT * FROM myapp_person LIMIT 1')[0]

Menangguhkan bidang-bidang modelLink to this heading

Bidang-bidang mungkin juga ditiadakan:

Code
>>> people = Person.objects.raw('SELECT id, first_name FROM myapp_person')

Obyek Person dikembalikan oleh permintaan ini akan meniadakan instance-instance model (lihat defer()). Ini berarti bahwa bidang-bidang yang dihilangkan dari permintaan akan dimuat pada permintaan. Sebagai contoh:

Code
>>> 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

Dari penampilan luar, ini terlihat seperti permintaan telah mengambil kedua nama pertama dan nama terakhir. Bagaimanapun, contoh ini sebenarnya menerbitkan 3 permintaan. Hanya nama pertama yang diambil oleh permintaan raw() -- nama terakhir keduanya diambil pada permintaan mereka telah cetak.

Hanya ada sat bidang yang anda tidak dapat tinggalkan - bidang primary key. Django menggunakan primary key untuk mencirikan instance-instance model, jadi itu harus selalu disertakan dalam permintaan mentah. Sebuah pengecualian InvalidQuery akan dimunculkan jika anda lupa menyertakan primary key.

Menambahkan keteranganLink to this heading

Anda dapat juga menjalankan permintaan mengandung bidang-bidang yang tidak ditentukan pada model. Sebagai contoh, kami dapat menggunakan PostgreSQL's age() function untuk mendapatkan daftar orang dengan umur mereka dihitung oleh basisdata:

Code
>>> people = Person.objects.raw('SELECT *, age(birth_date) AS age FROM myapp_person')
>>> for p in people:
...     print("%s is %s." % (p.first_name, p.age))
John is 37.
Jane is 42.
...

Melewati parameter kedalam raw()Link to this heading

Jika anda butuh melakukan permintaan gambaran parameter, anda dapat menggunakan argumen params pada raw():

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

params adalah sebuah parameter list atau dictionary. Anda akan menggunakan palceholder %s dalam string permintaan untuk daftar, atau placeholder %(key)s untuk sebuah dictionary (dimana key diganti oleh kunci dictionary, tentu saja), tidak peduli dari mesin basisdata anda. Placeholder itu akan diganti dengan parameter dari argumen params.

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

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
cursor = connections['my_db_alias'].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"
    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"
    desc = cursor.description
    nt_result = namedtuple('Result', [col[0] for col in desc])
    return [nt_result(*row) for row in cursor.fetchall()]

Ini adalah sebuah contoh perbedaan diantara tiga:

Code
>>> 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)Link to this definition

Memanggil prosedur penyimpanan basisdata dengan nama diberikan dan urutan pilihan dari parameter masukan.

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