![](/img/trans.png)
[英]Django Model.objects.all() returns unexpected empty QuerySet
[英]Model.objects.all() returns integers for ForeignKeys and choice fields - how to force it to return names instead?
我正在嘗試使用djqscsv導出.csv文件。 我知道模塊支持外鍵 ,但是我模型的一半是選擇鍵和外鍵 ,我真的不想像鏈接中所示那樣對其進行硬編碼。
它只是按原樣從數據庫中獲取表。 有沒有一種創建動態查詢的理想方法,該查詢可以檢查字段類型並返回適當的實際數據,而不僅僅是ID?
編輯,因為顯然這太難以可視化了:
class Bonus(models.Model):
name = models.CharField(max_length=30, unique=True)
def __unicode__(self):
return self.name
class Contact(models.Model):
url = models.URLField()
email = models.EmailField()
person = models.CharField(max_length=128)
bonus_field = models.ForeignKey(Bonus)
status = models.IntegerField(choices=STATUS_CHOICES, default=1)
choices.py
STATUS_CHOICES = (
(1, ("Sent")),
(2, ("Requested")),
(3, ("In progress")),
)
Contact.objects.all()返回Bonus中的 url,電子郵件,個人和row ID 。 我希望它返回相應的名稱,但是我不想寫
Contact.objects.values('url', 'email', 'person', 'bonus_field__name')
因為完整的模型太大了,我不想硬編碼。
我假設您要遍歷各個字段,並選擇ForeignKeys來專門處理:
from django.db import models
# Get field_name, is_foreign_key pairs from model meta
fields_foreign = [(
field.name,
isinstance(field, models.ForeignKey)
) for field in Contact._meta.fields]
# Assume every related model has a 'name' field,
# if you want to get 'name' fields of these models,
# construct the list of field names to pass to values():
# something like ['url', 'email', 'person', 'bonus_field__name']
value_fields = [
f[0] if not f[1] else "{}__name".format(f[0])
for f in fields_foreign
]
# Now get the values
Contact.objects.values(*value_fields)
如果是“實際數據”,則表示__str__
返回的字符串:
contacts = Contact.objects.all()
for contact in contacts:
values = []
for f in fields_foreign:
value = getattr(contact, f[0])
if f[1]: # Foreignkey, call str() on the model object
value = str(value) # this calls __str__ in model
values.append(value)
print values
如果要獲得更好的性能,則在查詢所有Contact對象時,最好對相關模型使用select_related()。
關於選擇,Django為每個帶有選擇項的字段提供了一個便捷的方法get_field_display()。 要在值列表中使用此選項,首先必須知道哪些字段已設置選項:
# Now we have tuples (field_name, is_field_foreign, field_has_choices)
fields_foreign_choices = [(
field.name,
isinstance(field, models.ForeignKey),
bool(field.choices)
) for field in Contact._meta.fields]
# We can use the magic get_field_display() method
contacts = Contact.objects.all()
for contact in contacts:
values = []
for f in fields_foreign_choices:
value = getattr(contact, f[0])
if f[1]: # Foreignkey, call str() on the model object
value = str(value) # this calls __str__ in model
elif f[2]: # The field has choices
display_method = getattr(contact, "get_{}_display".format(f[0])
value = display_method()
values.append(value)
print values
對於其他字段(例如Datetime字段),您可以執行以下操作:提取元數據,然后進行相應的轉換。
這是我解決問題的方法:
@NeoWang的最后一段代碼的確起作用(在稍作修改后以相同的Model.objects.values()形式返回字典列表),但是在我的特定情況下它沒有用,因為djqscsv模塊需要查詢集。 相反,我要做的是將代碼的第一位用於ForeignKey字段,並且在編寫CSV之前就進行選擇,我只是將選擇字段整數值替換為選擇元組的對應名稱。 硬編碼部分僅用於三個字段,因此我猜想加上一個適當的注釋並繼續進行。
所以這是這樣的:
models.py中的自定義管理器
class ContactManager(models.Manager):
def get_values(self):
fields_foreign = [(
field.name,
isinstance(field, models.ForeignKey)
) for field in Contact._meta.fields]
value_fields = [
f[0] if not f[1] else "{}__name".format(f[0])
for f in fields_foreign
]
return Contact.objects.values(*value_fields)
djqscsv.py中的修改(很遺憾,我們必須合並修改,因此我們將不得不在項目中使用修改后的版本):
在djqscsv.py中
## import your choices.py!
## add this method (hardcoded, so modify accordingly)
def replace_choice(record):
for key in record:
if key == 'status':
## very readable line incoming (might remove unicode call if you don't need it)
record[key] = unicode([v[1] for i, v in enumerate(STATUS_CHOICES) if v[0] == record[key]][0], 'utf-8')
return record
## modify this forloop
for record in values_qs:
fixed_record = replace_choice(record)
record = _sanitize_unicode_record(field_serializer_map, fixed_record)
writer.writerow(record)
replace_choice()方法已針對我的模型進行了硬編碼,但是您明白了。 在上面和@NeoWang答案中提供的代碼段上構建動態解決方案非常容易。
您可以為Contact
創建一個自定義管理器,覆蓋values()
函數,並使其根據需要處理ForeignKey
字段。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.