Filter menurut properti

98

Apakah mungkin untuk memfilter queryset Django berdasarkan properti model?

saya memiliki metode dalam model saya:

@property
def myproperty(self):
    [..]

dan sekarang saya ingin memfilter berdasarkan properti ini seperti:

MyModel.objects.filter(myproperty=[..])

apakah ini mungkin?

schneck
sumber
Ada di SQLAlchemy: docs.sqlalchemy.org/en/latest/orm/extensions/hybrid.html dan Anda dapat menghubungkan django dengan SQLAlchemy melalui pypi.python.org/pypi/aldjemy tetapi saya ragu keduanya dapat dihubungkan seperti yang Anda inginkan.
rattray

Jawaban:

79

Nggak. Filter Django beroperasi pada tingkat basis data, menghasilkan SQL. Untuk memfilter berdasarkan properti Python, Anda harus memuat objek ke Python untuk mengevaluasi properti - dan pada titik itu, Anda sudah melakukan semua pekerjaan untuk memuatnya.

Glenn Maynard
sumber
5
nasib buruk bahwa fitur ini tidak diterapkan, akan menjadi ekstensi yang menarik untuk setidaknya menyaring objek yang cocok setelah kumpulan hasil dibuat.
Schneck
1
bagaimana mengatasinya di admin? Apakah ada solusi?
andilabs
41

Saya mungkin salah memahami pertanyaan awal Anda, tetapi ada filter bawaan python.

filtered = filter(myproperty, MyModel.objects)

Tetapi lebih baik menggunakan pemahaman daftar :

filtered = [x for x in MyModel.objects if x.myproperty()]

atau bahkan lebih baik, ekspresi generator :

filtered = (x for x in MyModel.objects if x.myproperty())
Clint
sumber
16
Itu berfungsi untuk memfilternya setelah Anda memiliki objek Python, tetapi dia bertanya tentang Django QuerySet.filter, yang membangun kueri SQL.
Glenn Maynard
1
benar, tetapi seperti yang dijelaskan di atas, saya ingin menambahkan properti ke filter database saya. pemfilteran setelah kueri dilakukan adalah hal yang ingin saya hindari.
Schneck
20

Berdasarkan solusi yang disarankan @ TheGrimmScientist, Anda dapat membuat "properti sql" ini dengan menentukannya di Manajer atau QuerySet, dan menggunakan kembali / rantai / buat mereka:

Dengan Manajer:

class CompanyManager(models.Manager):
    def with_chairs_needed(self):
        return self.annotate(chairs_needed=F('num_employees') - F('num_chairs'))

class Company(models.Model):
    # ...
    objects = CompanyManager()

Company.objects.with_chairs_needed().filter(chairs_needed__lt=4)

Dengan QuerySet:

class CompanyQuerySet(models.QuerySet):
    def many_employees(self, n=50):
        return self.filter(num_employees__gte=n)

    def needs_fewer_chairs_than(self, n=5):
        return self.with_chairs_needed().filter(chairs_needed__lt=n)

    def with_chairs_needed(self):
        return self.annotate(chairs_needed=F('num_employees') - F('num_chairs'))

class Company(models.Model):
    # ...
    objects = CompanyQuerySet.as_manager()

Company.objects.needs_fewer_chairs_than(4).many_employees()

Lihat https://docs.djangoproject.com/en/1.9/topics/db/managers/ untuk selengkapnya. Perhatikan bahwa saya akan menonaktifkan dokumentasi dan belum menguji yang di atas.

rattray
sumber
14

Sepertinya menggunakan F () dengan penjelasan akan menjadi solusi saya untuk ini.

Ini tidak akan difilter @property, karena Fberbicara ke databse sebelum objek dibawa ke python. Tapi tetap meletakkannya di sini sebagai jawaban karena alasan saya ingin memfilter berdasarkan properti sebenarnya ingin memfilter objek berdasarkan hasil aritmatika sederhana pada dua bidang berbeda.

jadi, sesuatu di sepanjang baris:

companies = Company.objects\
    .annotate(chairs_needed=F('num_employees') - F('num_chairs'))\
    .filter(chairs_needed__lt=4)

daripada mendefinisikan properti menjadi:

@property
def chairs_needed(self):
    return self.num_employees - self.num_chairs

lalu melakukan pemahaman daftar di semua objek.

TheGrimmScientist
sumber
5

Saya memiliki masalah yang sama, dan saya mengembangkan solusi sederhana ini:

objects_id = [x.id for x in MyModel.objects.all() if x.myProperty == [...]]
MyModel.objects.filter(id__in=objects_id)

Saya tahu ini bukan solusi yang paling berkinerja, tetapi dapat membantu dalam kasus sederhana seperti saya

Vitalate
sumber
3

TOLONG seseorang mengoreksi saya, tetapi saya rasa saya telah menemukan solusi, setidaknya untuk kasus saya sendiri.

Saya ingin mengerjakan semua elemen yang propertinya sama persis dengan ... apa pun.

Tapi saya punya beberapa model, dan rutinitas ini seharusnya bekerja untuk semua model. Dan memang:

def selectByProperties(modelType, specify):
    clause = "SELECT * from %s" % modelType._meta.db_table

    if len(specify) > 0:
        clause += " WHERE "
        for field, eqvalue in specify.items():
            clause += "%s = '%s' AND " % (field, eqvalue)
        clause = clause [:-5]  # remove last AND

    print clause
    return modelType.objects.raw(clause)

Dengan subrutin universal ini, saya dapat memilih semua elemen yang sama persis dengan kamus saya tentang kombinasi 'spesifik' (nama properti, nilai properti).

Parameter pertama mengambil (model.Model),

kamus kedua seperti: {"property1": "77", "property2": "12"}

Dan itu menciptakan pernyataan SQL seperti

SELECT * from appname_modelname WHERE property1 = '77' AND property2 = '12'

dan mengembalikan QuerySet pada elemen tersebut.

Ini adalah fungsi uji:

from myApp.models import myModel

def testSelectByProperties ():

    specify = {"property1" : "77" , "property2" : "12"}
    subset = selectByProperties(myModel, specify)

    nameField = "property0"
    ## checking if that is what I expected:
    for i in subset:
        print i.__dict__[nameField], 
        for j in specify.keys():
             print i.__dict__[j], 
        print 

Dan? Bagaimana menurut anda?

akrueger
sumber
Secara umum sepertinya pekerjaan yang layak. Saya tidak akan mengatakan itu ideal, tetapi itu mengalahkan harus membagi repositori untuk memodifikasi model untuk paket yang Anda instal dari PyPI setiap kali Anda membutuhkan sesuatu seperti ini.
hlongmore
Dan sekarang saya punya waktu untuk bermain-main dengannya: kelemahan sebenarnya dari pendekatan ini adalah bahwa queryset yang dikembalikan oleh .raw () bukanlah queryset lengkap, maksud saya ada metode queryset yang hilang:AttributeError: 'RawQuerySet' object has no attribute 'values'
hlongmore
1

Saya tahu ini adalah pertanyaan lama, tetapi demi mereka yang melompat ke sini saya pikir ada gunanya membaca pertanyaan di bawah ini dan jawaban relatifnya:

Bagaimana menyesuaikan filter admin di Django 1.4

FSp
sumber
1
Bagi mereka yang membaca sekilas jawaban ini - tautan ini adalah informasi tentang mengimplementasikan Filter Daftar di Admin Django menggunakan "SimpleListFilter". Berguna, tetapi bukan jawaban atas pertanyaan kecuali dalam kasus yang sangat spesifik.
jenniwren
0

Hal ini juga mungkin untuk menggunakan anotasi queryset yang menduplikasi mendapatkan properti / set-logika, seperti yang disarankan misalnya oleh @rattray dan @thegrimmscientist , dalam hubungannya dengan yang property. Ini bisa menghasilkan sesuatu yang berfungsi baik pada level Python maupun pada level database.

Tidak yakin tentang kekurangannya, bagaimanapun: lihat pertanyaan SO ini sebagai contoh.

djvg
sumber
Tautan pertanyaan tinjauan kode Anda memberikan pemberitahuan bahwa itu telah dihapus secara sukarela oleh pembuatnya. Maukah Anda memperbarui jawaban Anda di sini, baik dengan tautan ke kode, atau dengan penjelasan, atau hanya menghapus jawaban Anda?
hlongmore
@hlongmore: Maaf untuk itu. Pertanyaan itu dipindahkan ke SO. Saya memperbaiki tautan di atas.
djvg