首页 > 代码库 > 关于Django中的数据库操作API之distinct去重的一个误传

关于Django中的数据库操作API之distinct去重的一个误传

关于Django中的数据库操作API之distinct去重的一个误传

最近在做一个Server的项目,后台框架是Apache mod_wsgi + django。
django是一个基于Python的Web开发框架,功能十分强大,至于有多强大,还是读者们自己去体验吧。
我在这里要说的一个问题是关于Python的ORM功能的。
问题就在django提供的数据库操作API中的distinct()函数,了解SQL语句的读者都应该知道,DISTINCT关键字可以在select操作时去重。django里的这个distinct()函数也是这个功能,通常的用法是我们要取出一张表中的某一列的所有值,并且只取出不重复的,如果有重复的就只取出来一次,网络上几乎所有的对这个函数的使用例子对应的Python代码都如下:

1
xxxx.objects.values("field_name").distinct()

#或者

 
1
xxxx.objects.distinct().values("field_name")

 

但是这样的用法往往达不到使用者的意图,可以来看一下网络上流传甚广的一个问答:
比如这个:http://bbs.csdn.net/topics/330006477
再比如这个:http://www.douban.com/group/topic/17856879/
这里其实说的都是一个例子,仔细看这个解答

—–
需要注意的是,这样返回的是一个字典的列表,而不是通常的 QuerySet

这样是过滤不掉的,这是个问题,还是有5个对象在里面,对结果使用len(obj)就知道没有任何变化,但是如果只作统计不重复数据的个数的话,还是准确的3个
obj=ClassName.objects.values(‘name’).distinct()
len(obj)=5
obj.count()=3
但是结果集中没有起到任何的过滤作用,此问题有待解决!!!!!

有理有据,让人很信服的认为distinct()函数没有实现其所描述的功能。真的是这样么?

在这里负责的告诉读者,上面的解释是错误的,distinct()具有去重功能是没有必要怀疑的,那为什么会出现上面的解释呢?因为他不知道distinct()函数有一个隐藏特性,当使用distinct()函数的时候,如果不使用order_by()函数做跟随,那么该函数会自动把当前表中的默认排序字段作为DISTINCT的一个列,所以上述问题出现的原因是因为他没有使用order_by(‘name’)来屏蔽distinct()的那个特性,从而得出的distinct结果并不是仅仅以name一个列作为参数的,而是以id+name两列的值作为distinct参数,可以看到id列里面的值全部都是唯一值,所以得出来的结果必然是以id列的值为标准的,这也就是

 
1
len(obj)=5

的原因,而如果想要实现上述例子中楼主的需求,正确的代码写法是:

1
ClassName.objects.values(‘name‘).distinct() .order_by(‘name‘)

在1.4+版本的django中distinct()函数可以传入一个参数,从而省去了后面跟加一个order_by()函数的必要,但是这也需要你的数据库支持Postgresql,否则还是无效的。

附上,django官方介绍:

Note

Any fields used in an order_by() call are included in the SQL SELECT columns. This can sometimes lead to unexpected results when used in conjunction with distinct(). If you order by fields from a related model, those fields will be added to the selected columns and they may make otherwise duplicate rows appear to be distinct. Since the extra columns don’t appear in the returned results (they are only there to support ordering), it sometimes looks like non-distinct results are being returned.

Similarly, if you use a values() query to restrict the columns selected, the columns used in any order_by() (or default model ordering) will still be involved and may affect uniqueness of the results.

The moral here is that if you are using distinct() be careful about ordering by related models. Similarly, when using distinct() and values() together, be careful when ordering by fields not in the values()call.

关于Django中的数据库操作API之distinct去重的一个误传