ใน Django หนึ่งกรอง QuerySet ด้วยการค้นหาเขตข้อมูลแบบไดนามิกได้อย่างไร


160

รับคลาส:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=20)

เป็นไปได้หรือไม่และถ้าเป็นเช่นนั้นจะมี QuerySet ที่กรองตามอาร์กิวเมนต์แบบไดนามิกหรือไม่ ตัวอย่างเช่น:

 # Instead of:
 Person.objects.filter(name__startswith='B')
 # ... and:
 Person.objects.filter(name__endswith='B')

 # ... is there some way, given:
 filter_by = '{0}__{1}'.format('name', 'startswith')
 filter_value = 'B'

 # ... that you can run the equivalent of this?
 Person.objects.filter(filter_by=filter_value)
 # ... which will throw an exception, since `filter_by` is not
 # an attribute of `Person`.

คำตอบ:


310

อาจใช้การขยายอาร์กิวเมนต์ของ Python เพื่อแก้ปัญหานี้:

kwargs = {
    '{0}__{1}'.format('name', 'startswith'): 'A',
    '{0}__{1}'.format('name', 'endswith'): 'Z'
}

Person.objects.filter(**kwargs)

นี่เป็นสำนวน Python ที่ใช้กันทั่วไปและมีประโยชน์


6
เพียงแค่ gotcha heads-up อย่างรวดเร็ว: ตรวจสอบให้แน่ใจว่าสตริงใน kwargs เป็นประเภท str ไม่ใช่ยูนิโค้ดแล้วตัวกรองอื่น () จะบ่น
Steve Jalim

1
@santiagobasulto มันยังอ้างถึงพารามิเตอร์การบรรจุ / เปิดออกและการเปลี่ยนแปลงของมัน
Daniel Naab

7
ดีดีและดี !
Oscar Mederos

5
@DanielNaab แต่จะใช้งานได้เฉพาะกับ kwargs ที่ทำงานกับการกรอง AND เงื่อนไขทางเลือกใด ๆ สำหรับเงื่อนไข OR
Prateek099

3
@prateek คุณสามารถใช้วัตถุ Q: stackoverflow.com/questions/13076822/…
deecodameeko

6

ตัวอย่างง่าย ๆ :

ในแอพการสำรวจ Django ฉันต้องการรายการ HTML เลือกแสดงผู้ใช้ที่ลงทะเบียน แต่เนื่องจากเรามีผู้ใช้งานที่ลงทะเบียนแล้ว 5,000 คนฉันจึงต้องการวิธีกรองรายการตามเกณฑ์การสืบค้น (เช่นเฉพาะผู้ที่จบเวิร์กช็อปเฉพาะ) เพื่อให้องค์ประกอบการสำรวจสามารถใช้งานได้อีกครั้งฉันต้องการให้ผู้ที่สร้างคำถามแบบสำรวจเพื่อให้สามารถแนบเกณฑ์เหล่านั้นกับคำถามนั้น (ไม่ต้องการเข้ารหัสโค้ดแบบสอบถามลงในแอป)

วิธีแก้ปัญหาที่ฉันพบนั้นไม่ได้เป็นมิตรกับผู้ใช้ 100% (ต้องการความช่วยเหลือจากบุคลากรด้านเทคโนโลยีเพื่อสร้างคิวรี) แต่มันจะช่วยแก้ปัญหาได้ เมื่อสร้างคำถามผู้แก้ไขสามารถป้อนพจนานุกรมลงในฟิลด์ที่กำหนดเองเช่น:

{'is_staff':True,'last_name__startswith':'A',}

สตริงนั้นถูกเก็บไว้ในฐานข้อมูล self.question.custom_queryในรหัสดูก็กลับมาในขณะที่มา ค่าของนั่นคือสตริงที่ดูเหมือนพจนานุกรม เราเปลี่ยนกลับเป็นพจนานุกรมจริงด้วย eval () จากนั้นใส่ลงในคิวรีด้วย ** kwargs:

kwargs = eval(self.question.custom_query)
user_list = User.objects.filter(**kwargs).order_by("last_name")   

ฉันสงสัยว่าจะต้องใช้อะไรในการสร้าง ModelField / FormField / WidgetField แบบกำหนดเองที่ใช้งานพฤติกรรมเพื่ออนุญาตให้ผู้ใช้บนฝั่ง GUI โดยทั่วไปแล้ว "สร้าง" แบบสอบถามไม่เคยเห็นข้อความจริง แต่ใช้อินเทอร์เฟซ ทำเช่นนั้น เสียงเหมือนโครงการเรียบร้อย ...
T. หิน

1
T. Stone - ฉันคิดว่ามันจะง่ายในการสร้างเครื่องมือเช่นนี้ในแบบง่าย ๆ ถ้าแบบจำลองที่ต้องการการสืบค้นนั้นง่าย แต่ยากมากที่จะทำในวิธีที่ละเอียดถี่ถ้วนที่สัมผัสกับตัวเลือกที่เป็นไปได้ทั้งหมด ซับซ้อน
shacker

5
-1 การโทรหาeval()การนำเข้าของผู้ใช้เป็นความคิดที่ไม่ดีแม้ว่าคุณจะเชื่อใจผู้ใช้ของคุณอย่างสมบูรณ์ ฟิลด์ JSON จะเป็นแนวคิดที่ดีกว่าที่นี่
John Carter

5

Django.db.models.Qเป็นสิ่งที่คุณต้องการอย่างยิ่งใน Django


7
คุณ (หรือบางคน) ให้ตัวอย่างของวิธีใช้วัตถุ Q ในการใช้ชื่อเขตข้อมูลแบบไดนามิกได้หรือไม่
jackdbernier

3
มันเหมือนกับคำตอบของ Daniel Naabความแตกต่างเพียงอย่างเดียวคือคุณส่งข้อโต้แย้งไปยังตัวสร้างวัตถุ Q Q(**filters)หากคุณต้องการสร้างวัตถุ Q แบบไดนามิกคุณสามารถใส่วัตถุเหล่านั้นลงในรายการและใช้งาน.filter(*q_objects)หรือใช้ตัวดำเนินการระดับบิตเพื่อรวมวัตถุ Q
Will S

5
คำตอบนี้ควรรวมถึงตัวอย่างของการใช้ Q เพื่อแก้ปัญหาของ OP
pdoherty926

-2

แบบฟอร์มการค้นหาที่ซับซ้อนจริงๆมักจะระบุว่าแบบจำลองที่ง่ายกว่ากำลังพยายามที่จะขุดมันออกมา

คุณคาดหวังว่าจะได้รับค่าสำหรับชื่อคอลัมน์และการดำเนินการอย่างไร คุณจะได้รับค่าที่?'name''startswith'

 filter_by = '%s__%s' % ('name', 'startswith')
  1. แบบฟอร์ม "ค้นหา"? คุณกำลังจะ - อะไร - เลือกชื่อจากรายการชื่อ? เลือกการดำเนินการจากรายการการดำเนินการหรือไม่ ในขณะที่ปลายเปิดคนส่วนใหญ่พบความสับสนและยากต่อการใช้งาน

    มีตัวกรองดังกล่าวกี่คอลัมน์ 6? 12? 18?

    • จำนวนน้อย? รายการเลือกที่ซับซ้อนไม่สมเหตุสมผล มีฟิลด์น้อยและมีบางประโยคที่เหมาะสม
    • จำนวนมาก? แบบจำลองของคุณไม่ถูกต้อง ดูเหมือนว่า "ฟิลด์" เป็นจริงคีย์ไปยังแถวในตารางอื่นไม่ใช่คอลัมน์
  2. ปุ่มตัวกรองเฉพาะ เดี๋ยวก่อนนั่นคือวิธีที่ผู้ดูแลระบบ Django ทำงาน ตัวกรองเฉพาะถูกเปลี่ยนเป็นปุ่ม และการวิเคราะห์เช่นเดียวกับข้างต้นนำไปใช้ ตัวกรองบางอย่างสมเหตุสมผล ตัวกรองจำนวนมากมักจะหมายถึงการละเมิดฟอร์มปกติประเภทแรก

บ่อยครั้งที่ฟิลด์ที่คล้ายกันจำนวนมากหมายความว่าควรมีแถวมากกว่านี้และมีฟิลด์น้อยกว่า


9
ด้วยความเคารพมันอาจจะให้คำแนะนำโดยไม่ต้องรู้อะไรเกี่ยวกับการออกแบบ หากต้องการ "เพียงใช้งาน" แอปพลิเคชันนี้จะทำให้เกิดฟังก์ชั่นทางดาราศาสตร์ (> 200 แอป ^ 21 foos) เพื่อตอบสนองความต้องการ คุณกำลังอ่านวัตถุประสงค์และเจตนาในตัวอย่าง; คุณไม่ควร :)
Brian M. Hunt

2
ฉันได้พบกับผู้คนจำนวนมากที่รู้สึกว่าปัญหาของพวกเขาจะแก้ปัญหาได้เล็กน้อยหากมีเพียงสิ่งเดียวที่ (ก) เป็นเรื่องทั่วไปมากขึ้นและ (ข) ทำงานในแบบที่พวกเขาจินตนาการ วิธีนี้จะทำให้หงุดหงิดไม่รู้จบเพราะทุกสิ่งไม่ใช่วิธีที่พวกเขาจินตนาการ ฉันเห็นความล้มเหลวมากเกินไปเกิดจาก "การแก้ไขเฟรมเวิร์ก"
S.Lott

2
สิ่งต่าง ๆ ทำงานได้ตามที่คาดหวังและต้องการตามคำตอบของ Daniel คำถามของฉันเกี่ยวกับไวยากรณ์ไม่ใช่การออกแบบ ถ้าฉันมีเวลาเขียนแบบฉันจะทำอย่างนั้น ฉันแน่ใจว่าข้อมูลของคุณจะมีประโยชน์ แต่ก็ไม่ใช่ตัวเลือกที่ใช้งานได้จริง
Brian M. Hunt

8
S.Lott คำตอบของคุณไม่ได้ตอบคำถามนี้จากระยะไกล หากคุณไม่ทราบคำตอบโปรดทิ้งคำถามไว้คนเดียว อย่าตอบสนองด้วยคำแนะนำการออกแบบที่ไม่พึงประสงค์เมื่อคุณไม่มีความรู้เกี่ยวกับการออกแบบ!
slypete

2
@slypete: หากการเปลี่ยนแปลงการออกแบบลบปัญหาแสดงว่าปัญหานั้นได้รับการแก้ไข ต่อเนื่องไปตามเส้นทางบนพื้นฐานของการออกแบบที่ไม่ดีมีราคาแพงและซับซ้อนเกินความจำเป็น การแก้ไขปัญหาที่เกิดจากสาเหตุที่ดีกว่าการแก้ปัญหาอื่น ๆ ที่เกิดจากการตัดสินใจออกแบบที่ไม่ดี ฉันขอโทษที่คุณไม่ชอบการวิเคราะห์สาเหตุ แต่เมื่อมีบางสิ่งที่ยากจริงๆมันมักจะหมายความว่าคุณกำลังพยายามทำสิ่งผิดเริ่มต้นด้วย
S.Lott
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.