Katakanlah saya memiliki model berikut
class Photo(models.Model):
tags = models.ManyToManyField(Tag)
class Tag(models.Model):
name = models.CharField(max_length=50)
Dalam tampilan saya memiliki daftar dengan filter aktif yang disebut kategori . Saya ingin memfilter objek Foto yang semua tagnya ada dalam kategori .
Saya mencoba:
Photo.objects.filter(tags__name__in=categories)
Tapi ini cocok dengan semua item dalam kategori, tidak semua item.
Jadi jika kategorinya adalah ['liburan', 'musim panas'], saya ingin Foto dengan tag liburan dan musim panas.
Bisakah ini dicapai?
python
django
filter
django-queryset
Sander van Leeuwen
sumber
sumber
Photo.objects.filter(tags__name='holiday').filter(tags__name='summer')
adalah cara yang tepat. (Ini sama dengan contoh jpic). Masingfilter
- masing harus menambahkan lebih banyakJOIN
ke kueri, sehingga Anda dapat mengambil pendekatan anotasi jika terlalu banyak.Jawaban:
Ringkasan:
Salah satu opsinya adalah, seperti yang disarankan oleh jpic dan sgallen di komentar, menambahkan
.filter()
untuk setiap kategori. Setiap tambahanfilter
menambahkan lebih banyak gabungan, yang seharusnya tidak menjadi masalah untuk sekumpulan kecil kategori.Ada pendekatan agregasi . Kueri ini akan lebih pendek dan mungkin lebih cepat untuk sekumpulan besar kategori.
Anda juga memiliki opsi untuk menggunakan kueri khusus .
Beberapa contoh
Pengaturan tes:
Menggunakan pendekatan filter berantai :
Kueri yang dihasilkan:
Perhatikan bahwa masing-masing
filter
menambahkan lebih banyakJOINS
ke kueri.Menggunakan pendekatan anotasi :
Kueri yang dihasilkan:
AND
Q
objek ed tidak akan berfungsi:Kueri yang dihasilkan:
sumber
t3
, dan foto memiliki tagt2
dant3
. Maka foto ini akan tetap cocok dengan kueri yang diberikan.Photo.objects.filter(tags__in=tags)
cocok dengan foto yang memiliki salah satu tag, tidak hanya yang memiliki semua. Beberapa dari mereka yang hanya memiliki salah satu tag yang diinginkan, mungkin memiliki jumlah yang tepat dari tag yang Anda cari, dan beberapa yang memiliki semua tag yang diinginkan, mungkin juga memiliki tag tambahan.Pendekatan lain yang berhasil, meskipun hanya PostgreSQL, adalah menggunakan
django.contrib.postgres.fields.ArrayField
:Contoh disalin dari dokumen :
ArrayField
memiliki beberapa fitur yang lebih kuat seperti tumpang tindih dan transformasi indeks .sumber
Ini juga dapat dilakukan dengan pembuatan kueri dinamis menggunakan Django ORM dan beberapa sihir Python :)
Idenya adalah untuk menghasilkan objek Q yang sesuai untuk setiap kategori dan kemudian menggabungkannya menggunakan operator AND ke dalam satu QuerySet. Misalnya untuk contoh Anda itu akan sama dengan
sumber
filter
akan sama dengan menggunakanand
objek Q dalam satu filter ... Kesalahan saya.filter
keexclude
dan menggunakan operator negate. Seperti:res = Photo.exclude(~reduce(and_, [Q(tags__name=c) for c in categories]))
Saya menggunakan fungsi kecil yang mengulang filter di atas daftar untuk operator tertentu dengan nama kolom:
dan fungsi ini bisa disebut seperti itu:
itu juga bekerja dengan kelas apa pun dan lebih banyak tag dalam daftar; operator bisa siapa saja seperti 'iexact', 'in', 'contains', 'ne', ...
sumber
sumber
Jika kita ingin melakukannya secara dinamis, ikuti contoh:
sumber