จะเปลี่ยนชื่อรายการในค่า () ใน Django ได้อย่างไร?


102

ฉันต้องการทำเหมือนในตั๋วนี้ที่ djangoproject.comแต่มีการจัดรูปแบบเพิ่มเติมบางอย่าง จากแบบสอบถามนี้

>>> MyModel.objects.values('cryptic_value_name')
[{'cryptic_value_name': 1}, {'cryptic_value_name': 2}]

ฉันอยากได้อะไรแบบนั้น:

>>> MyModel.objects.values(renamed_value='cryptic_value_name')
[{'renamed_value': 1}, {'renamed_value': 2}]

มีวิธีอื่นในตัวมากกว่านี้หรือฉันต้องทำด้วยตนเองหรือไม่?

คำตอบ:


72

มันค่อนข้างแฮ็ค แต่คุณสามารถใช้extraวิธีการ:

MyModel.objects.extra(
  select={
    'renamed_value': 'cryptic_value_name'
  }
).values(
  'renamed_value'
)

สิ่งนี้โดยทั่วไปSELECT cryptic_value_name AS renamed_valueใน SQL

อีกทางเลือกหนึ่งหากคุณต้องการให้เวอร์ชันที่เปลี่ยนชื่อเสมอ แต่ db มีชื่อที่เป็นความลับคือการตั้งชื่อฟิลด์ของคุณด้วยชื่อใหม่ แต่ใช้db_columnเพื่ออ้างถึงชื่อเดิมในฐานข้อมูล


6
เห็นได้ชัดใน 1.7 * คุณจะสามารถเขียนชื่อนามแฝงตรงตามอาร์กิวเมนต์ที่ตั้งชื่อvalues()ได้ ที่มา: code.djangoproject.com/ticket/16735
gregoltsov

19
-1 เพราะ.values(supports__through_tables)และแฮ็คนี้ไม่ได้ (โดยบังเอิญอาจเป็นกรณีการใช้งานที่ชัดเจนที่สุดสำหรับการต้องการเปลี่ยนชื่อValuesQuerySetคีย์ dict)
wim

1
มีใครพบวิธีทำผ่านตาราง (.values ​​(supports__through_tables)) บ้าง
François Constant

12
ขออภัยโซลูชันนี้ไม่รองรับชื่อที่เกี่ยวข้องเช่นใน.values('foreignkeymodel__id', 'foreignkeymodel__name').
Risadinha

13
Model.objects.annotate (my_alias = F ( 'some__long__name__to__alias')) ค่า ( 'my_alias') จากตั๋ว 16735. .
ยู

189

จากdjango>=1.8คุณสามารถใช้คำอธิบายประกอบและวัตถุ F

from django.db.models import F

MyModel.objects.annotate(renamed_value=F('cryptic_value_name')).values('renamed_value')

และextra()กำลังจะเลิกใช้งานจากdjango docs:

นี่คือ API เก่าที่เราตั้งเป้าว่าจะเลิกใช้งานในอนาคต ใช้เฉพาะในกรณีที่คุณไม่สามารถแสดงแบบสอบถามของคุณโดยใช้วิธีการสืบค้นอื่น ๆ หากคุณจำเป็นต้องใช้โปรดยื่นตั๋วโดยใช้คีย์เวิร์ด QuerySet.extra พร้อมกับกรณีการใช้งานของคุณ (โปรดตรวจสอบรายการตั๋วที่มีอยู่ก่อน) เพื่อให้เราสามารถปรับปรุง QuerySet API เพื่ออนุญาตให้ลบ extra () ได้ เราไม่ได้ปรับปรุงหรือแก้ไขข้อบกพร่องสำหรับวิธีนี้อีกต่อไป


3
django>=1.8คำตอบนี้จะทำงานเฉพาะใน นิพจน์แบบสอบถามเปิดใช้งานannotateเพื่อยอมรับนิพจน์อื่นที่ไม่ใช่การรวม อ้างอิง: docs.djangoproject.com/th/1.9/releases/1.8/…
Yeo

3
มันใช้งานได้ แต่ไวยากรณ์ที่เสนอในคำถามดั้งเดิมนั้นดีกว่ามากไม่ใช่หรือ? MyModel.objects.values(renamed_value='cryptic_value_name')
Wim

11
MyModel.objects.values(renamed_value=models.F('cryptic_value_name'))works
jarcoal

1
คำตอบนี้ใช้ได้ แต่ซ้ำซ้อน อาร์กิวเมนต์คำหลักที่ส่งถึงvalues()จะถูกส่งผ่านannotate()ในนามของคุณ (ดูdocs.djangoproject.com/en/2.2/ref/models/querysets/… ) คำตอบMyModel.objects.values(renamed_value=F('cryptic_value_name'))คือง่ายและชัดเจนมากขึ้น IMO
tobias.mcnulty

77

โดยไม่ต้องใช้วิธีการจัดการอื่น ๆ (ทดสอบบนv3.0.4):

from django.db.models import F

MyModel.objects.values(renamed_value=F('cryptic_value_name'))

ข้อความที่ตัดตอนมาจากเอกสาร Django :

F ()วัตถุแทนค่าของสนามรูปแบบหรือคอลัมน์ข้อเขียน ทำให้สามารถอ้างถึงค่าฟิลด์โมเดลและดำเนินการฐานข้อมูลโดยใช้โดยไม่ต้องดึงออกจากฐานข้อมูลไปยังหน่วยความจำ Python


5
คุณสามารถใช้ได้มากกว่าหนึ่งค่า:MyModel.objects.values(renamed_value=F('cryptic_value_name'),renamed_value2=F('cryptic_value_name2'))
alpere

1
แต่ถ้าคุณต้องการเปลี่ยนค่าหนึ่งและเก็บค่าอื่นไว้จะเป็นอย่างไร ด้วยคำอธิบายประกอบเป็นไปได้: inv.annotate(name=F('stock__name')).values('name', "price")แต่ฉันไม่เห็นว่าวิธีนี้ควรทำงานอย่างไรในกรณีนั้น inv.values(name=F("stock__name"), price=F("price"))-> คำอธิบายประกอบ "ราคา" ขัดแย้งกับฟิลด์ในรุ่น
Malte

1
@Malte ฉันคิดว่าไม่จำเป็นต้องซับซ้อนนี้ inv.values('price', name=F("stock__name"))เพียงแค่พยายาม ใน python คุณสามารถใส่อาร์กิวเมนต์ที่ตั้งชื่อไว้หลังอาร์กิวเมนต์ที่ไม่มีชื่อเท่านั้น
Arpit Singh

0

ฉันกำลังทำงานกับ django 1.11.6

(และคู่คีย์: ค่าตรงข้ามกับคำตอบที่ยอมรับ)

นี่คือวิธีที่ฉันทำให้มันได้ผลสำหรับโครงการของฉัน

def json(university):
    address = UniversityAddress.objects.filter(university=university)
    address = address.extra(select={'city__state__country__name': 'country', 'city__state__name': 'state', 'city__name': 'city'})
    address = address.values('country', 'state', "city", 'street', "postal_code").get()
    return address

โปรดทราบว่าการเพิ่ม simultanous objects.filter (). extra (). values ​​() จะเหมือนกับด้านบน


คุณจะเห็นว่า.extra(select={cryptic_value:ranamed_value})มีคีย์ตรงข้าม: ค่าเมื่อเทียบกับคำตอบที่ยอมรับ
Sudip Bhattarai

-6

มันง่ายกว่าถ้าคุณต้องการเปลี่ยนชื่อฟิลด์บางส่วนของโหมด

ลอง

projects = Project.objects.filter()
projects = [{'id': p.id, 'name': '%s (ID:%s)' % (p.department, p.id)} for p in projects]

ที่นี่ฉันไม่มีnameฟิลด์ในตาราง แต่ฉันสามารถทำได้หลังจากปรับแต่งเล็กน้อยแล้ว


10
–1 มันจะประเมินการสืบค้นซึ่งเป็นตัวทำลายข้อตกลง
wim
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.