从多对多关系返回一组随机排序的不同对象

Posted

技术标签:

【中文标题】从多对多关系返回一组随机排序的不同对象【英文标题】:Returning a randomly ordered set of distinct objects from a ManyToMany relationship 【发布时间】:2011-11-06 19:29:53 【问题描述】:

我正在使用带有 Postgresql 8.4 的 Django 1.3,并且我有如下模型(删除了不相关的东西):

class Service(models.Model):
    name = models.CharField(max_length=80)

class Location(models.Model):
    name = models.CharField(max_length=80)
    services = models.ManyToManyField(Service, through='LocalService')

class LocalService(models.Model):
    location = models.ForeignKey(Location)
    service = models.ForeignKey(Service)

我正在尝试获取一组不同的 Service 对象,这些对象按链接的 Location 对象的属性进行过滤,并随机排序。我首先尝试了这个:

Service.objects.filter(location__name__icontains='o').distinct().order_by('?')

...但这会引发此异常:

DatabaseError:对于 SELECT DISTINCT,必须出现 ORDER BY 表达式 在选择列表中

google了一下,发现要在SQL层面实现这种结果,需要将DISTINCT和ORDER BY放在不同的查询级别,即:使用子查询。如果我对一组 DISTINCT 的结果进行子查询,我可以像这样随机排序它们:

SELECT * 
FROM (
    SELECT DISTINCT s.*
    FROM profile_service s
    JOIN profile_localservice ls
    ON ls.service_id = s.id
    JOIN profile_location l
    ON ls.location_id = l.id
    WHERE l.name LIKE '%o%'
) as temptable
ORDER BY RANDOM()

我是否需要在此 SQL 查询中使用 Manager.raw() 方法来获取我的模型实例集,或者是否有更简单的方法可以从 Django API 中执行此操作?

【问题讨论】:

您是否尝试交换“distinct()”和“order_by()”? @akonsu:是的,我试过了。它导致相同的异常 如果您尝试创建子查询,请使用extra() @Pannu:我尝试使用extra() 方法的tables 参数。当我简单地指定一个表名时它就可以工作,但是每当我提供一个子查询时,我都会得到一个DatabaseError 异常。即使子查询正在运行,我也知道如何在 SELECT 和 WHERE 子句中使用它,但我不确定如何将它用作模型实例的源。 【参考方案1】:

根据您的确切要求,以下可能会起作用(并且可能比ORDER BY Random() 表现更好)。我不确定 Postgresql,但 mysql 对任何数据集进行随机排序确实很慢。

services = list(Service.objects.filter(location__name__icontains='o').distinct())
random.shuffle(services)

【讨论】:

谢谢@Alex,我想这会实现我所追求的。

以上是关于从多对多关系返回一组随机排序的不同对象的主要内容,如果未能解决你的问题,请参考以下文章

从多对多关系中获取核心数据

从多对多关系中获取结果

使用实体框架从多对多关系中选择数据

从多对多关系中获取数据

核心数据 - 从多对多关系构建 NSPredicate

如何从多对多关系中只加载 1 个结果 Laravel