在Django中选择不同的单独列?

134

我很好奇在Django中是否有不用"SELECT * FROM..."的方式进行查询。我想要执行"SELECT DISTINCT columnName FROM ..."。

具体来说,我有一个模型看起来像:

class ProductOrder(models.Model):
   Product  = models.CharField(max_length=20, promary_key=True)
   Category = models.CharField(max_length=30)
   Rank = models.IntegerField()

其中,RankCategory内的排名。我想能够遍历所有类别,在每个类别中对每个排名执行某些操作。

首先,我想要获取系统中所有类别的列表,然后查询该类别中的所有产品,并重复此过程,直到处理完所有类别。

我宁愿避免使用原始SQL,但如果必须使用,也可以。尽管我以前没有在Django/Python中编写过原始SQL。

4个回答

240

从数据库获取不同列名的列表的一种方法是使用distinct()values()结合使用。

在您的情况下,您可以执行以下操作以获取不同类别的名称:

q = ProductOrder.objects.values('Category').distinct()
print q.query # See for yourself.

# The query would look something like
# SELECT DISTINCT "app_productorder"."category" FROM "app_productorder"

这里有几件事情需要记住。首先,这将返回一个ValuesQuerySet,它的行为与QuerySet不同。当你访问q的第一个元素(上面)时,你将得到一个字典,而不是ProductOrder的实例。
其次,最好阅读文档中关于使用distinct()warning note。上面的示例将起作用,但可能不适用于所有distinct()values()的组合。 PS:在模型中使用小写名称是个好主意。在你的情况下,这意味着重写你的模型如下所示:
class ProductOrder(models.Model):
    product  = models.CharField(max_length=20, primary_key=True)
    category = models.CharField(max_length=30)
    rank = models.IntegerField()

1
下面描述的方法现在在django 1.4中可用,如果您需要具有字段感知去重的ProductOrder实例,这个方法非常好用;-) - Jonathan Liuti
我想知道如何修改这段代码,使其也能在“where”语句中工作。例如:select distinct(app_productorder.category) from app_productorder where app_productorder.rank <30; - Prakash Dahal
1
你如何将这个转换成普通的查询集? - Josh
SQL中select distinct on的好技巧:https://www.geekytidbits.com/postgres-distinct-on/ - John Jiang

98

如果您正在使用PostgreSQL,那么其实非常简单,只需使用distinct(columns)文档)。

Productorder.objects.all().distinct('category')

请注意,自Django 1.4版以来已经包含了此功能。


3
这个功能现在已经被添加到 Django SVN 中,并将包含在 Django 1.4 版本中。 - Will Hardy
22
注意:除非您正在使用PostgreSQL,否则不能给distinct()提供参数。最好使用上面接受的解决方案。 - Mark Chackerian
如果那些点踩的人能解释一下他们的行为就好了,原始答案确实是错误的,但那已经在4年前被纠正了 ;) - Wolph
5
加1,但这里不需要使用all() - Antony Hatchkins
1
在Postgres中找不到此查询。 - Ricardo D. Quiroga
显示剩余2条评论

28

按照那个字段对用户进行排序,然后去重。

ProductOrder.objects.order_by('category').values_list('category', flat=True).distinct()

1
被低估的答案!如果你能更好地解释一下就更好了。我遇到了一个与排序有关的问题。 - Shahriar Rahman Zahin
1
我很高兴偶然发现了这个答案,它起作用了,但为什么会这样呢?我不明白为什么排序会有所不同。 - Steven Gillies

19
其他回答都可以,但这个稍微更简洁一些,因为它只提供了像DISTINCT查询一样的值,没有来自Django的任何冗余信息。
>>> set(ProductOrder.objects.values_list('category', flat=True))
{u'category1', u'category2', u'category3', u'category4'}
或者
>>> list(set(ProductOrder.objects.values_list('category', flat=True)))
[u'category1', u'category2', u'category3', u'category4']

此外,它可以在没有PostgreSQL的情况下使用。

与使用 .distinct() 相比,这种方法效率较低。假设您的数据库中的 DISTINCT 操作比 python 的 set 更快,但是在shell中进行试验非常棒。

更新:: 这个答案非常适用于在Django shell中进行开发查询。 除非您绝对确定在应用set之前始终只有很少的结果,否则不要在生产环境中使用此解决方案,否则从性能角度来看,这是个糟糕的想法。


1
values_list 不会在 SQL 查询中加入 DISTINCT,因此如果有多个值,它将带来多个值。 - mehmet
1
从性能角度来说,这是一个非常糟糕的想法! - boatcoder
1
如果您的表不是非常小的话,请不要在生产环境中这样做! - Mark Chackerian

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