เลือกค่าที่แตกต่างจากเขตข้อมูลตาราง


105

ฉันดิ้นรนที่จะเอาหัวไปรอบ ORM ของ Django สิ่งที่ฉันต้องการทำคือรับรายการค่าที่แตกต่างกันภายในฟิลด์บนตารางของฉัน .... เทียบเท่ากับค่าใดค่าหนึ่งต่อไปนี้:

SELECT DISTINCT myfieldname FROM mytable

(หรืออีกทางเลือกหนึ่ง)

SELECT myfieldname FROM mytable GROUP BY myfieldname

อย่างน้อยฉันก็อยากจะทำแบบ Django ก่อนที่จะหันมาใช้ Raw sql ตัวอย่างเช่นมีตาราง:

id ถนนเมือง

1 ถนนสายหลักฮัลล์

2, ถนนอื่น ๆ , ฮัลล์

3, Bibble Way, เลสเตอร์

4 อีกทางหนึ่งเลสเตอร์

5, High Street, Londidium

ฉันต้องการรับ:

ฮัลล์เลสเตอร์ Londidium

คำตอบ:


205

บอกว่าโมเดลของคุณคือ "ร้านค้า"

class Shop(models.Model):
    street = models.CharField(max_length=150)
    city = models.CharField(max_length=150)

    # some of your models may have explicit ordering
    class Meta:
        ordering = ('city')

เนื่องจากคุณอาจจะมีMetaระดับorderingชุดแอตทริบิวต์คุณสามารถใช้order_by()โดยไม่มีพารามิเตอร์เพื่อยกเลิกการสั่งซื้อใด ๆ distinct()เมื่อใช้ ดูเอกสารภายใต้order_by()

หากคุณไม่ต้องการให้ใช้คำสั่งใด ๆ กับแบบสอบถามไม่ใช่แม้แต่การสั่งซื้อเริ่มต้นให้โทรสั่ง order_by () โดยไม่มีพารามิเตอร์

และdistinct()ในบันทึกที่กล่าวถึงปัญหาเกี่ยวกับการใช้distinct()กับการสั่งซื้อ

ในการสืบค้นฐานข้อมูลของคุณคุณต้องโทร:

models.Shop.objects.order_by().values('city').distinct()

มันส่งคืนพจนานุกรม

หรือ

models.Shop.objects.order_by().values_list('city').distinct()

อันนี้ส่งคืนค่าValuesListQuerySetที่คุณสามารถส่งเป็นlistไฟล์. นอกจากนี้คุณยังสามารถเพิ่มflat=Trueการvalues_listให้เรียบผล

ดูเพิ่มเติม: รับค่าที่แตกต่างกันของ Queryset ตามฟิลด์


29
ใช้งานได้จริง อย่างไรก็ตาม! ฉันไม่สามารถใช้งานได้กับโมเดลทั้งหมดของฉัน Weidly มันใช้ได้กับบางคน แต่ไม่ใช่คนอื่น สำหรับผู้ที่มีการสั่งซื้อ Meta จะไม่ทำงาน ดังนั้นคุณต้องล้างการสั่งซื้อในชุดแบบสอบถามก่อน Models.Shop.objects.order_by (). values ​​('city').
unique

2
สิ่งสำคัญคือต้องทราบvalues_listว่าไม่ได้ส่งคืนรายการจริง มันส่งคืนบางอย่างเช่น queryset ฉันพบว่ามีประโยชน์ในการใช้ list () รอบ ๆ การโทร values_list เสมอ
dheerosaur

8
values_listส่งคืนค่า ValuesListQuerySet ซึ่งเป็นตัววนซ้ำ การแคสต์ไปยังรายการอาจมีประโยชน์ แต่ยังสามารถขีดฆ่าประสิทธิภาพเมื่อต้องประเมินแถวทั้งหมดพร้อมกันโดยเฉพาะกับชุดข้อมูลขนาดใหญ่
Peter Kilczuk

3
Meta: ordering = ()"คุณสมบัติ" ของออม Django และobjects.distinct()เทียบกับobjects.ordering().distinct()ทำให้เราชั่วโมงของความสับสน ควรมีสติกเกอร์คำเตือนเกี่ยวกับความปลอดภัยของผู้บริโภคบนผลิตภัณฑ์นั้น) เราอาจกำหนดนโยบาย no-Meta-orders-attribute เพื่อป้องกันไม่ให้ศีรษะเกิดรอยขีดข่วนในอนาคต
เตาปรุงอาหาร

คุณสามารถปิดMetaคลาสorderingและแก้ไขปัญหาได้distinctโดยใช้โดยorder_by()ไม่มีพารามิเตอร์ อยู่ในเอกสาร QuerySet API ภายใต้order_by()" ถ้าคุณไม่ต้องการให้ใช้คำสั่งใด ๆ กับข้อความค้นหาไม่ใช่แม้แต่คำสั่งเริ่มต้นให้เรียกorder_by()โดยไม่มีพารามิเตอร์ "
Mark Mikofski

11

นอกจากนี้จะยังคงมีความเกี่ยวข้องมากคำตอบของ jujuleฉันคิดว่ามันสำคัญมากทีเดียวที่จะยังต้องตระหนักถึงผลกระทบของorder_by()ในdistinct("field_name")คำสั่ง อย่างไรก็ตามนี่เป็นคุณลักษณะเฉพาะของ Postgres เท่านั้น!

หากคุณกำลังใช้ Postgres และหากคุณกำหนดชื่อฟิลด์ที่คิวรีควรแตกต่างกันคุณจะorder_by()ต้องเริ่มต้นด้วยชื่อฟิลด์เดียวกัน (หรือชื่อฟิลด์) ในลำดับเดียวกัน (อาจมีฟิลด์เพิ่มเติมในภายหลัง)

บันทึก

เมื่อคุณระบุชื่อฟิลด์คุณต้องระบุ order_by () ใน QuerySet และฟิลด์ใน order_by () ต้องขึ้นต้นด้วยฟิลด์ที่แตกต่างกัน () ตามลำดับเดียวกัน

ตัวอย่างเช่น SELECT DISTINCT ON (a) ให้แถวแรกสำหรับแต่ละค่าในคอลัมน์ a หากคุณไม่ระบุคำสั่งซื้อคุณจะได้รับแถวตามอำเภอใจ

หากคุณต้องการเช่น - ดึงรายชื่อเมืองที่คุณรู้จักร้านค้าในตัวอย่างของ jujule จะต้องปรับให้เข้ากับสิ่งนี้:

# returns an iterable Queryset of cities.
models.Shop.objects.order_by('city').values_list('city', flat=True).distinct('city')  

2

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

# select distinct code from Platform where id in ( select platform__id from Build where product=p)
pl_ids = Build.objects.values('platform__id').filter(product=p)
platforms = Platform.objects.values_list('code', flat=True).filter(id__in=pl_ids).distinct('code')
platforms = list(platforms) if platforms else []
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.