如何在Django中重命名values()中的项?

178

我想做的和这个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}]

有没有其他更内置的方法,还是我必须手动完成这个过程?

6个回答

282

django>=1.8 开始,您可以使用 annotate 和 F 对象

from django.db.models import F

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

另外,extra()也将被弃用,来自于Django文档:

这是一个旧的API,我们计划在未来的某个时候将其弃用。仅在无法使用其他查询集方法表达查询时使用它。如果确实需要使用它,请使用QuerySet.extra关键字文件一个包含您的用例的票(请先检查现有票列表),以便我们可以增强QuerySet API以允许删除extra()。我们不再改进或修复此方法的错误。


3
这个答案只适用于django>=1.8。查询表达式使annotate能够接受聚合以外的表达式。参考链接:https://docs.djangoproject.com/en/1.9/releases/1.8/#query-expressions-conditional-expressions-and-database-functions。 - Yeo
3
它可以工作,但是原问题中提出的语法难道不是更加简洁明了吗?MyModel.objects.values(renamed_value='cryptic_value_name') - wim
28
MyModel.objects.values(renamed_value=models.F('cryptic_value_name')) 的作用是获取 MyModel 对象中 cryptic_value_name 字段的值,并将其重命名为 renamed_value - jarcoal
4
这个答案是可行的,但是有些冗余。传递给values()的关键字参数会通过annotate()自动传递(请参阅https://docs.djangoproject.com/en/2.2/ref/models/querysets/#django.db.models.query.QuerySet.values)。在我看来,使用`MyModel.objects.values(renamed_value=F('cryptic_value_name'))`更简单明了。 - tobias.mcnulty

160

不使用任何其他管理方法(已在v3.0.4上测试):

from django.db.models import F

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

摘自Django文档

F()对象表示模型字段或注释列的值。它使得可以在不必将它们从数据库中提取到Python内存中的情况下,引用模型字段值并使用它们执行数据库操作。


9
你可以使用以下代码来获取多个值: MyModel.objects.values(renamed_value=F('cryptic_value_name'),renamed_value2=F('cryptic_value_name2')) 其中,"renamed_value" 和 "renamed_value2" 是你为 "cryptic_value_name" 和 "cryptic_value_name2" 设置的易于理解的新名称。 - alpere
1
但是如果您想要更改一个值并保留另一个值怎么办?使用annotate就可以实现:inv.annotate(name=F('stock__name')).values('name', "price"),但我不知道这种方法在这种情况下应该如何工作。 inv.values(name=F("stock__name"), price=F("price")) -> 注释“price”与模型上的字段冲突。 - DeemonRider
7
@Malte,我认为没必要把这件事情复杂化。只需尝试使用inv.values('price', name=F("stock__name"))即可。在Python中,只有在不具名的参数之后才能放置命名参数。 - Arpit Singh
在这个方法中,是否可以设置一个带空格的新列名?例如,“Renamed value”而不是renamed_value。无法将字符串作为参数名称传递。 - Azzonith
2
@Azzonith,你可以使用关键字参数,例如MyModel.objects.values(**{'重命名的值': F('加密值名称')}) - Arpit Singh

84

虽然有点巧妙,但你可以使用extra方法:

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

这个 SQL 查询语句实际上是将 cryptic_value_name 重命名为 renamed_value

如果你希望始终使用新的命名版本,但数据库中使用了神秘名称,则可以用新名称命名字段,但使用 db_column 指向数据库中的原始名称。


7
显然在1.7.*版本中,您将能够将别名名称直接作为命名参数写入values()。来源: https://code.djangoproject.com/ticket/16735 - gregoltsov
26
-1 是因为 .values(supports__through_tables) 这个方法不能完成此操作(顺便提一下,这很可能是想要重命名 ValuesQuerySet 字典键的最明显用例)。 - wim
1
有人找到了通过表格(.values(supports__through_tables))实现这一点的方法吗? - François Constant
15
很遗憾,该解决方案不支持像 .values('foreignkeymodel__id', 'foreignkeymodel__name') 中的相关名称。 - Risadinha
16
Model.objects.annotate(my_alias=F('some__long__name__to__alias')).values('my_alias'),来自票号16735。 - eugene
显示剩余5条评论

1
我正在使用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

请注意,添加同时使用objects.filter().extra().values()与以上操作相同。

你可以看到,.extra(select={cryptic_value:ranamed_value}) 的键值对与被接受的答案相反。 - Sudip Bhattarai

0

尝试将其作为kwargs传递:

MyModel.objects.annotate(**{'A B C':F('profile_id')}).values('A B C')

在我的情况下,结果集中每个值的键中都包含空格和其他特殊字符,所以这个方法行得通。

-6

如果您想重命名模型中的几个字段,这并不难。

试试看

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

在这个表中我没有一个name字段,但是稍微调整一下就可以得到它。


16
它评估查询集,这是个不能接受的问题。 - wim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接