Mülke göre filtrele


100

Bir Django sorgu kümesini model özelliğine göre filtrelemek mümkün müdür?

modelimde bir yöntemim var:

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

ve şimdi bu özelliğe göre filtrelemek istiyorum:

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

bu bir şekilde mümkün mü?


SQLAlchemy'de: docs.sqlalchemy.org/en/latest/orm/extensions/hybrid.html ve django'yu pypi.python.org/pypi/aldjemy aracılığıyla SQLAlchemy ile bağlayabilirsiniz, ancak ikisinin birbirine bağlanabileceğinden şüpheliyim onların olmasını istediğiniz şekilde.
rattray

Yanıtlar:


81

Hayır! Django filtreleri veritabanı düzeyinde çalışır ve SQL oluşturur. Python özelliklerine göre filtre uygulamak için, özelliği değerlendirmek için nesneyi Python'a yüklemeniz gerekir - ve bu noktada, onu yüklemek için tüm işi zaten yapmışsınızdır.


5
Bu özelliğin uygulanmaması kötü şans, sonuç kümesi oluşturulduktan sonra en azından eşleşen nesneleri filtrelemek için ilginç bir uzantı olacaktır .
schneck

1
yönetimde bununla nasıl başa çıkılır? Çözüm var mı?
andilabs

42

Orijinal sorunuzu yanlış anlıyor olabilirim, ancak python'da yerleşik bir filtre var .

filtered = filter(myproperty, MyModel.objects)

Ancak bir liste anlayışı kullanmak daha iyidir :

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

hatta daha iyisi, bir üreteç ifadesi :

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

16
Bu, bir Python nesnesine sahip olduğunuzda onu filtrelemek için çalışır, ancak SQL sorguları oluşturan Django QuerySet.filter'ı sorar.
Glenn Maynard

1
doğru, ancak yukarıda açıklandığı gibi özelliği veritabanı filtreme eklemek istiyorum. Sorgu yapıldıktan sonra filtreleme tam olarak kaçınmak istediğim şeydir.
schneck

20

@ TheGrimmScientist'in önerdiği geçici çözümden yararlanarak, bu "sql özelliklerini" Yönetici veya Sorgu Kümesinde tanımlayarak yapabilir ve yeniden kullanabilir / zincirleyebilir / oluşturabilirsiniz:

Bir Yönetici ile:

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)

QuerySet ile:

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()

Daha fazla bilgi için https://docs.djangoproject.com/en/1.9/topics/db/managers/ adresine bakın . Belgelerden çıktığımı ve yukarıdakileri test etmediğimi unutmayın.


14

Ek açıklamalarla F () kullanmak gibi görünüyor buna çözüm olacak.

Şuna göre filtrelemeyecek @property, çünküF nesnelerin piton getirilmeden önce databse için görüşmelere. Ama yine de bunu bir cevap olarak buraya koyarsak, özelliğe göre filtre isteme sebebim, nesneleri iki farklı alanda basit aritmetik sonucu filtrelemek istemekti.

yani, şu satırlarda bir şey:

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

mülkü şu şekilde tanımlamak yerine:

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

sonra tüm nesneler arasında bir liste kavrama yapmak.


6

Aynı sorunu yaşadım ve bu basit çözümü geliştirdim:

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

Bunun en performanslı çözüm olmadığını biliyorum, ancak benimki gibi basit durumlarda yardımcı olabilir


3

LÜTFEN birisi beni düzeltir, ama sanırım en azından kendi durumum için bir çözüm buldum.

Özellikleri tam olarak eşit olan tüm unsurlar üzerinde çalışmak istiyorum ... her neyse.

Ama birkaç modelim var ve bu rutin tüm modeller için çalışmalı. Ve yapar:

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)

Bu evrensel alt yordamla, 'belirt' (özellik adı, özellik değeri) kombinasyonları sözlüğüme tam olarak eşit olan tüm öğeleri seçebilirim.

İlk parametre bir (models.Model) alır,

ikincisi şuna benzer bir sözlük: {"özellik1": "77", "özellik2": "12"}

Ve aşağıdaki gibi bir SQL ifadesi oluşturur

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

ve bu öğeler üzerinde bir QuerySet döndürür.

Bu bir test işlevidir:

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 

Ve? Ne düşünüyorsun?


Genelde etrafta iyi bir iş gibi görünüyor. İdeal olduğunu söyleyemem, ancak böyle bir şeye her ihtiyaç duyduğunuzda PyPI'den yüklediğiniz paketlerin modelini değiştirmek için bir depo çatallaşmak zorunda kalmaktan daha iyidir.
hlongmore

Ve şimdi onunla oynamak için biraz zamanım oldu: Bu yaklaşımın gerçek dezavantajı, .raw () tarafından döndürülen sorgu kümelerinin tam teşekküllü sorgu kümeleri olmamasıdır, yani eksik olan sorgu kümesi yöntemleri vardır:AttributeError: 'RawQuerySet' object has no attribute 'values'
hlongmore

1

Bunun eski bir soru olduğunu biliyorum, ancak buraya atlayanlar için aşağıdaki soruyu ve göreceli cevabı okumanın faydalı olacağını düşünüyorum:

Django 1.4'te yönetici filtresi nasıl özelleştirilir


1
Bu yanıtı gözden geçirenler için - bu bağlantı, "SimpleListFilter" kullanarak Django Yöneticisinde Liste Filtrelerinin uygulanmasına ilişkin bilgilere yöneliktir. Kullanışlı, ancak çok özel bir durum dışında sorunun cevabı değil.
jenniwren

0

Ayrıca önerdiği ör olarak emlak get / set mantığı çoğaltmak QuerySet ek açıklamaları kullanmasına mümkün olabilir @rattray ve @thegrimmscientist , birlikteproperty . Bu, hem Python seviyesinde hem de veritabanı seviyesinde çalışan bir şey sağlayabilir .

Ancak dezavantajlardan emin değilim: örnek için bu SO sorusuna bakın .


Kod incelemesi soru bağlantınız, yazarı tarafından gönüllü olarak kaldırıldığına dair bir bildirim verir. Cevabınızı burada bir kod bağlantısı ile veya bir açıklama ile günceller misiniz, yoksa sadece cevabınızı kaldırır mısınız?
hlongmore

@hlongmore: Bunun için üzgünüm. Bu soru SO'ya taşındı. Yukarıdaki bağlantıyı düzelttim.
djvg
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.