Pilih DISTINCT kolom individu dalam django?

97

Saya ingin tahu apakah ada cara untuk melakukan kueri di Django yang bukan " SELECT * FROM..." di bawahnya. Saya mencoba melakukan " SELECT DISTINCT columnName FROM ..." sebagai gantinya.

Secara khusus saya memiliki model yang terlihat seperti:

class ProductOrder(models.Model):
   Product  = models.CharField(max_length=20, promary_key=True)
   Category = models.CharField(max_length=30)
   Rank = models.IntegerField()

dimana Rankadalah peringkat dalam a Category. Saya ingin dapat mengulang semua Kategori dengan melakukan beberapa operasi pada setiap peringkat dalam kategori itu.

Pertama-tama saya ingin mendapatkan daftar semua kategori dalam sistem dan kemudian membuat kueri untuk semua produk dalam kategori itu dan mengulanginya hingga setiap kategori diproses.

Saya lebih suka menghindari SQL mentah, tetapi jika saya harus pergi ke sana, itu akan baik-baik saja. Meskipun saya belum pernah mengkodekan SQL mentah di Django / Python sebelumnya.

jamida
sumber

Jawaban:

196

Salah satu cara untuk mendapatkan daftar nama kolom yang berbeda dari database adalah menggunakan distinct() dalam hubungannya dengan values().

Dalam kasus Anda, Anda dapat melakukan hal berikut untuk mendapatkan nama kategori yang berbeda:

q = ProductOrder.objects.values('Category').distinct()
print q.query # See for yourself.

# The query would look something like
# SELECT DISTINCT "app_productorder"."category" FROM "app_productorder"

Ada beberapa hal yang perlu diingat di sini. Pertama, ini akan mengembalikan a ValuesQuerySetyang berperilaku berbeda dari a QuerySet. Ketika Anda mengakses katakanlah, elemen pertama dari q(di atas) Anda akan mendapatkan kamus , BUKAN contoh dari ProductOrder.

Kedua, sebaiknya Anda membaca catatan peringatan di dokumen tentang penggunaan distinct(). Contoh di atas akan berfungsi tetapi semua kombinasi dari distinct()dan values()mungkin tidak.

PS : Sebaiknya gunakan nama huruf kecil untuk bidang dalam model. Dalam kasus Anda, ini berarti menulis ulang model Anda seperti yang ditunjukkan di bawah ini:

class ProductOrder(models.Model):
    product  = models.CharField(max_length=20, primary_key=True)
    category = models.CharField(max_length=30)
    rank = models.IntegerField()
Manoj Govindan
sumber
1
Metode yang dijelaskan di bawah ini sekarang tersedia di django 1.4 dan bagus jika Anda membutuhkan instance ProductOrder dengan field aware yang berbeda ;-)
Jonathan Liuti
65

Sebenarnya cukup sederhana jika Anda menggunakan PostgreSQL , cukup gunakan distinct(columns)( dokumentasi ).

Productorder.objects.all().distinct('category')

Perhatikan bahwa fitur ini telah dimasukkan dalam Django sejak 1.4

Wolph
sumber
@lazerscience, @Manoj Govindan: Maaf, Anda benar. Sepertinya saya telah menambal Django untuk menambahkan fitur itu. Saya telah menambahkan tautan ke tambalan
Wolph
3
Ini sekarang dalam Django SVN dan akan berada di Django 1.4
Will Hardy
14
Catatan: kecuali Anda menggunakan PostgreSQL, Anda tidak dapat memberikan argumen yang berbeda (). Tetap terbaik dengan solusi yang diterima di atas.
Mark Chackerian
dalam pengujian, ini can_distinct_on_fieldsyang tampaknya hanya untuk Postgres
Skylar Saveland
4
ditambah 1, tetapi all()tidak perlu di sini
Antony Hatchkins
17

Jawaban lainnya baik-baik saja, tetapi ini sedikit lebih bersih, karena hanya memberikan nilai seperti yang akan Anda peroleh dari kueri DISTINCT, tanpa penyimpangan dari Django.

>>> set(ProductOrder.objects.values_list('category', flat=True))
{u'category1', u'category2', u'category3', u'category4'}

atau

>>> list(set(ProductOrder.objects.values_list('category', flat=True)))
[u'category1', u'category2', u'category3', u'category4']

Dan, ini berfungsi tanpa PostgreSQL.

Ini kurang efisien daripada menggunakan .distinct (), dengan anggapan bahwa DISTINCT dalam database Anda lebih cepat daripada python set, tetapi bagus untuk noodling di sekitar shell.

Mark Chackerian
sumber
values_listtidak dimasukkan ke DISTINCTdalam kueri sql, jadi ini akan membawa banyak nilai jika ada.
mehmet
13

Pengguna memesan dengan bidang itu, dan kemudian lakukan berbeda.

ProductOrder.objects.order_by('category').values_list('category', flat=True).distinct()
SuperNova
sumber