# dokumentasi Django 2.2.9.dev20191202104323

# Melakukan permintaan SQL mentah

Django gives you two ways of performing raw SQL queries: you can use `Manager.raw()` to perform raw queries and return model instances, or you can avoid the model layer entirely and execute custom SQL directly.

Menjelajahi ORM sebelum menggunakan SQL mentah!

ORM Django menyediakan banyak alat-alat untuk menyatakan permintaan tanpa menulis SQL mentah. Sebagai contoh:

- QuerySet API adalah luas.
- You can `annotate` and aggregate using many built-in database functions. Beyond those, you can create custom query expressions.

Before using raw SQL, explore the ORM. Ask on django-users or the #django IRC channel to see if the ORM supports your use case.

Peringatan

Anda harus berhati-hati ketika anda menulis SQL mentah. Setiap kali anda menggunakan itu, anda harus dengan benar meloloskan parameter apapun yang pengguna dapat mengendalikan menggunakan `params` untuk melindungi terhadap serangan SQL injection. Harap membaca lebih tentang SQL injection protection 1.

## Melakukan permintaan mentah

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

`Manager.``raw`(raw_query, params=None, translations=None)

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:

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

Anda dapat kemudian menjalankan SQL penyesuaian seperti itu:

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

Nama tabel model

Dimana nama dari tabel `Person` datang dalam contoh itu?

Secara awalan, Django mencari tahu nama tabel basisdata dengan menggabungkan "app label" model -- nama anda gunakan dalam `manage.py startapp` -- pada nama kelas model, dengan sebuah garis bawah diantara mereka. Dalam contoh kami telah menganggap bahwa model `Person` tinggal dalam sebuah aplikasi bernama `myapp`, jadi tabelnya akan berupa `myapp_person`.

Untuk rincian lebih periksa dokumentasi untuk pilihan `db_table`, yang juga membiarkan anda secara manual menyetel nama tabel basisdata.

Peringatan

Tidak ada pemeriksaan selesai pada pernyataan SQL yang dilewatkan ke `.raw()`. Django mengharapkan pernyataan itu akan mengembalikan sekumpulan baris dari basisdata, tetapi tidak melakukan apapun untuk memaksa itu. Jika permintaan tidak mengembalikan baris, sebuah (kemungkinan samar) kesalahan akan menghasilkan.

Peringatan

Jika anda sedang melakukan permintaan pada MySQL, catat bahwa paksaan jenis diam MySQL mungkin menyebabkan hasil tidak diharapkan ketika mencampur jenis. Jika anda meminta pada sebuah kolom jenis string, tetapi dengan sebuah nilai integer, MySQL akan memaksa jenis-jenis dari semua nilai dalam tabel ke integer sebelum melakukan perbandingan. Sebagai contoh, jika tabel anda mengandung nilai-nilai `'abc'`, `'def'` dan anda meminta untuk `WHERE mycolumn=0`, kedua baris akan cocok. Untuk mencegah ini, lakukan pembenaran typecast sebelum menggunakan nilai dalam sebuah permintaan.

### Memetakan bidang permintaan ke bidang model

`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:

    >>> 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`:

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

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

`raw()` mendukung pengindeksan, jadi jika anda butuh hanya hasil pertama anda dapat menulis:

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

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

### Menangguhkan bidang-bidang model

Bidang-bidang mungkin juga ditiadakan:

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

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

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:

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

You can often avoid using raw SQL to compute annotations by instead using a Func() expression.

### Melewati parameter kedalam `raw()`

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

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

Catatan

Parameter dictionary tidak didukung dengan backend SQLite; dengan backend ini, anda harus melewatkan parameter sebagai sebuah list.

Peringatan

Jangan menggunakan string berbentuk pada permintaan mentah atau mengutip placeholder dalam string-string SQL anda !

Itu sangat menggoda menulis permintaan diatas sebagai:

    >>> query = 'SELECT * FROM myapp_person WHERE last_name = %s' % lname
    >>> Person.objects.raw(query)

Anda mungkin juga berpikir anda harus menulis permintaan anda seperti ini (dengan kutipan disekitar `%s`):

    >>> query = "SELECT * FROM myapp_person WHERE last_name = '%s'"

Jangan membuat salah satu dari kesalahan-kesalahan ini.

Seperti diobrolkan dalam Perlindungan penyisipan SQL, menggunakan argumen `params` dan membiarkan placeholder tidak dikutip melindungi anda dari SQL injection attacks, pemanfaatan umum dimana penyerang memasukkan SQL berubah-ubah kedalam basisdata anda. Jika adan menggunakan penyisipan string atau mengutip placeholder, anda sedang pada resiko untuk penyisipan SQL.

## Menjalankan penyesuaian SQL langsung

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:

    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:

    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:

    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:

    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:

    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:

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

`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:

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

setara pada:

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

#### Memanggil prosedur penyimpanan

`CursorWrapper.``callproc`(procname, params=None, kparams=None)

  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:

      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:

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