Bagaimana melakukan SELECT MAX di Django?

91

Saya memiliki daftar objek bagaimana saya dapat menjalankan kueri untuk memberikan nilai maksimal bidang:

Saya menggunakan kode ini:

def get_best_argument(self):
    try:
        arg = self.argument_set.order_by('-rating')[0].details
    except IndexError:
        return 'no posts'
    return arg

peringkat adalah bilangan bulat

Johnd
sumber

Jawaban:

173

Lihat ini . Kode Anda akan menjadi seperti berikut:

from django.db.models import Max
# Generates a "SELECT MAX..." query
Argument.objects.aggregate(Max('rating')) # {'rating__max': 5}

Anda juga dapat menggunakan ini di queryset yang ada:

from django.db.models import Max
args = Argument.objects.filter(name='foo') # or whatever arbitrary queryset
args.aggregate(Max('rating')) # {'rating__max': 5}

Jika Anda memerlukan contoh model yang berisi nilai maksimal ini, maka kode yang Anda posting mungkin adalah cara terbaik untuk melakukannya:

arg = args.order_by('-rating')[0]

Perhatikan bahwa ini akan error jika queryset kosong, yaitu jika tidak ada argumen yang cocok dengan query (karena [0]bagian tersebut akan memunculkan IndexError). Jika Anda ingin menghindari perilaku itu dan sebaliknya hanya kembali Nonedalam kasus itu, gunakan .first():

arg = args.order_by('-rating').first() # may return None
Sasha Chedygov
sumber
4
Saya membutuhkan objek argumen sebenarnya yang memiliki Max itu, jadi saya bisa mencetak bidang detail. Panggilan args.aggregate (Max ('rating')) hanya mengembalikan rating tertinggi. Saya mencari argumen dengan peringkat tertinggi.
Johnd
1
Apa yang salah dengan kode keluar Anda - Argument.objects.order_by ("- rating") [0]?
AdamKG
73

Django juga mempunyai fungsi ' latest (field_name = None) ' yang menemukan entri terbaru (nilai maks.). Ini tidak hanya bekerja dengan bidang tanggal tetapi juga dengan string dan integer.

Anda bisa memberi nama field saat memanggil fungsi itu:

max_rated_entry = YourModel.objects.latest('rating')
return max_rated_entry.details

Atau Anda sudah dapat memberikan nama bidang itu di meta data model Anda:

from django.db import models

class YourModel(models.Model):
    #your class definition
    class Meta:
        get_latest_by = 'rating'

Sekarang Anda dapat memanggil 'latest ()' tanpa parameter apa pun:

max_rated_entry = YourModel.objects.latest()
return max_rated_entry.details
Lutz Schönemann
sumber
3
Ini adalah cara yang bagus untuk menggunakan latest(). Jika Anda membutuhkan catatan dengan nilai minimum, Anda dapat menggunakan earliest().
SaeX
7
latest()dan earliest()bekerja dengan bidang non-tanggal juga, tetapi ini merupakan efek samping dari penerapan. Anda harus menggunakan <your-queryset>.order_by('<interested-field>').first()atau <your-queryset>.order_by('<interested-field>').last()untuk memastikan kode anda akan tetap bekerja meskipun para pengembang Django akan mengubah latest()dan earliest()implementasi untuk bekerja dengan bidang tanggal saja.
mrnfrancesco
Ini adalah jawaban yang buruk: 1) seperti yang telah diketahui sebelumnya, detail penerapannya dan dapat diubah di masa mendatang 2) tidak terlalu mudah dibaca - nomor maks belum tentu yang terakhir disimpan (atau dalam arti lain)
Mikhail Gerasimov
47

Saya telah menguji ini untuk proyek saya, ia menemukan maks / menit dalam waktu O (n):

from django.db.models import Max

# Find the maximum value of the rating and then get the record with that rating. 
# Notice the double underscores in rating__max
max_rating = App.objects.aggregate(Max('rating'))['rating__max']
return App.objects.get(rating=max_rating)

Ini dijamin membuat Anda mendapatkan salah satu elemen maksimum secara efisien, daripada menyortir seluruh tabel dan mendapatkan yang teratas (sekitar O (n * logn)).

afahim
sumber
8
Big-O terkadang tidak berhasil dalam praktiknya, terutama untuk n yang lebih kecil, di mana koefisien dan implementasi penting. Kode Anda dalam python / django, yang dikompilasi menjadi bytecode dan mungkin atau mungkin tidak dioptimalkan. Basis data kemungkinan besar ditulis dalam bahasa yang dioptimalkan dan dikompilasi ke instruksi mesin. Selain itu, database tidak memiliki alasan untuk mengurutkan, jika fungsinya hanya mencari nilai maksimal. Saya ingin melihat data waktu sebelum saya merasa nyaman menggunakan kode buatan tangan melalui fungsi database bawaan.
benevolentprof
5
@afahim Saya rasa kode yang Anda posting ini tidak sepenuhnya benar. jika ternyata Anda memiliki lebih dari satu maksimum (katakanlah Anda memiliki dua Aplikasi dengan 5 bintang) menggunakan "get" akan menimbulkan kesalahan.
Raydel Miranda