กรองตามคุณสมบัติ


98

เป็นไปได้ไหมที่จะกรองชุดแบบสอบถาม Django ตามคุณสมบัติของโมเดล

ฉันมีวิธีการในแบบจำลองของฉัน:

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

และตอนนี้ฉันต้องการกรองตามคุณสมบัตินี้เช่น:

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

เป็นไปได้ไหม


อยู่ใน SQLAlchemy: docs.sqlalchemy.org/en/latest/orm/extensions/hybrid.htmlและคุณสามารถเชื่อมต่อ django กับ SQLAlchemy ผ่านpypi.python.org/pypi/aldjemyแต่ฉันสงสัยว่าทั้งสองสามารถเชื่อมต่อกันได้ ในแบบที่คุณต้องการให้เป็น
rattray

คำตอบ:


79

ไม่ ตัวกรอง Django ทำงานที่ระดับฐานข้อมูลสร้าง SQL ในการกรองตามคุณสมบัติของ Python คุณต้องโหลดออบเจ็กต์ลงใน Python เพื่อประเมินคุณสมบัติ - และเมื่อถึงจุดนั้นคุณได้ทำงานทั้งหมดเพื่อโหลดแล้ว


5
โชคร้ายที่ไม่มีการใช้งานคุณลักษณะนี้น่าจะเป็นส่วนขยายที่น่าสนใจอย่างน้อยก็กรองวัตถุที่ตรงกันออกหลังจากสร้างชุดผลลัพธ์แล้ว
schneck

1
วิธีจัดการกับแอดมิน มีวิธีแก้ปัญหาบางอย่างหรือไม่?
andilabs

41

ฉันอาจเข้าใจผิดในคำถามเดิมของคุณ แต่มีตัวกรองใน python

filtered = filter(myproperty, MyModel.objects)

แต่จะดีกว่าถ้าใช้ความเข้าใจในรายการ :

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

หรือดีกว่านั้นคือการแสดงออกของเครื่องกำเนิดไฟฟ้า :

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

16
วิธีนี้ใช้เพื่อกรองเมื่อคุณมีวัตถุ Python แต่เขาถามเกี่ยวกับ Django QuerySet.filter ซึ่งสร้างแบบสอบถาม SQL
Glenn Maynard

1
ถูกต้อง แต่ตามที่อธิบายไว้ข้างต้นฉันต้องการเพิ่มคุณสมบัติในตัวกรองฐานข้อมูลของฉัน การกรองหลังจากแบบสอบถามเสร็จสิ้นเป็นสิ่งที่ฉันต้องการหลีกเลี่ยง
schneck

20

วิธีแก้ปัญหาที่แนะนำของ @ TheGrimmScientist คุณสามารถสร้าง "คุณสมบัติ sql" เหล่านี้ได้โดยการกำหนดคุณสมบัติเหล่านี้ใน Manager หรือ QuerySet และใช้ซ้ำ / chain / compose:

ด้วยผู้จัดการ:

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:

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

ดูhttps://docs.djangoproject.com/en/1.9/topics/db/managers/สำหรับข้อมูลเพิ่มเติม โปรดทราบว่าฉันกำลังจะปิดเอกสารและไม่ได้ทดสอบข้างต้น


14

ดูเหมือนว่าการใช้ F () กับคำอธิบายประกอบจะเป็นวิธีแก้ปัญหาของฉัน

จะไม่กรองตาม@propertyเนื่องจากการFพูดคุยกับฐานข้อมูลก่อนที่จะนำวัตถุเข้าสู่ python แต่ยังคงใส่ไว้ที่นี่เป็นคำตอบเนื่องจากเหตุผลของฉันที่ต้องการกรองตามคุณสมบัติคือต้องการกรองวัตถุโดยผลของการคำนวณทางคณิตศาสตร์อย่างง่ายในสองฟิลด์ที่แตกต่างกัน

ดังนั้นบางสิ่งบางอย่างตามแนวของ:

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

แทนที่จะกำหนดคุณสมบัติให้เป็น:

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

จากนั้นทำความเข้าใจรายการในวัตถุทั้งหมด


5

ฉันมีปัญหาเดียวกันและฉันได้พัฒนาวิธีง่ายๆนี้:

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

ฉันรู้ว่ามันไม่ใช่วิธีแก้ปัญหาที่มีประสิทธิภาพที่สุด แต่อาจช่วยได้ในกรณีง่ายๆเช่นเดียวกับฉัน


3

โปรดมีคนช่วยแก้ไขให้ฉัน แต่ฉันคิดว่าฉันพบวิธีแก้ปัญหาแล้วอย่างน้อยก็สำหรับกรณีของฉันเอง

ฉันต้องการทำงานกับองค์ประกอบทั้งหมดที่มีคุณสมบัติเท่ากับ ... อะไรก็ได้

แต่ฉันมีหลายรุ่นและกิจวัตรนี้ควรใช้ได้กับทุกรุ่น และมันทำ:

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)

ด้วยรูทีนย่อยสากลนี้ฉันสามารถเลือกองค์ประกอบเหล่านั้นทั้งหมดซึ่งเท่ากับพจนานุกรมของชุดค่าผสม 'ระบุ' (ชื่อคุณสมบัติ, ค่าคุณสมบัติ)

พารามิเตอร์แรกใช้เวลา a (models.Model)

อันที่สองพจนานุกรมเช่น: {"property1": "77", "property2": "12"}

และสร้างคำสั่ง SQL เช่น

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

และส่งคืน QuerySet บนองค์ประกอบเหล่านั้น

นี่คือฟังก์ชั่นทดสอบ:

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 

และ? คุณคิดอย่างไร?


โดยทั่วไปดูเหมือนว่าเป็นวิธีที่ดี ฉันจะไม่บอกว่ามันเหมาะอย่างยิ่ง แต่มันต้องมีการแยกที่เก็บข้อมูลเพื่อแก้ไขโมเดลสำหรับแพ็คเกจที่คุณติดตั้งจาก PyPI ทุกครั้งที่คุณต้องการสิ่งนี้
hlongmore

และตอนนี้ฉันมีเวลาเล่นกับมันเล็กน้อย: ข้อเสียที่แท้จริงของวิธีนี้คือแบบสอบถามที่ส่งคืนโดย .raw () ไม่ใช่ชุดแบบสอบถามแบบเต็มซึ่งฉันหมายความว่ามีวิธีการสืบค้นที่ขาดหายไป:AttributeError: 'RawQuerySet' object has no attribute 'values'
hlongmore

1

ฉันรู้ว่ามันเป็นคำถามเก่า แต่เพื่อประโยชน์ของผู้ที่กระโดดมาที่นี่ฉันคิดว่าการอ่านคำถามด้านล่างและคำตอบที่เกี่ยวข้องมีประโยชน์:

วิธีปรับแต่งตัวกรองผู้ดูแลระบบใน Django 1.4


1
สำหรับผู้ที่อ่านคำตอบนี้ - ลิงก์นี้เป็นข้อมูลเกี่ยวกับการใช้ตัวกรองรายการใน Django Admin โดยใช้ "SimpleListFilter" มีประโยชน์ แต่ไม่ใช่คำตอบสำหรับคำถามยกเว้นในกรณีที่เฉพาะเจาะจงมาก
jenniwren

0

มันก็อาจจะเป็นไปได้ที่จะใช้คำอธิบายประกอบชุดข้อความค้นหาที่ซ้ำกันสถานที่ให้บริการรับ / ชุดตรรกะเป็นเช่นแนะนำโดย@rattrayและ@thegrimmscientist , ร่วมกับ propertyสิ่งนี้อาจให้ผลบางอย่างที่ใช้ได้ทั้งในระดับ Python และในระดับฐานข้อมูล

อย่างไรก็ตามไม่แน่ใจเกี่ยวกับข้อเสียโปรดดูตัวอย่างคำถาม SO นี้


ลิงก์คำถามการตรวจสอบโค้ดของคุณแจ้งให้ทราบว่าผู้เขียนนำออกโดยสมัครใจ คุณช่วยอัปเดตคำตอบของคุณที่นี่ด้วยลิงก์ไปยังรหัสหรือพร้อมคำอธิบายหรือเพียงแค่ลบคำตอบของคุณ
hlongmore

@hlongmore: ขออภัยที่. คำถามนั้นถูกย้ายไปที่ SO ฉันแก้ไขลิงค์ด้านบนแล้ว
djvg
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.