以多对多关系对相关集进行排序
Posted
技术标签:
【中文标题】以多对多关系对相关集进行排序【英文标题】:Order the related set in a many-to-many relation 【发布时间】:2014-03-08 10:06:12 【问题描述】:假设我有这样的设置:
class Product(models.Model):
name = models.CharField()
category = models.CharField()
class Shop(models.Model):
name = models.CharField()
website = models.URLField()
class ProductPrices(model.Model):
product = models.ForeignKey(Product)
shop = models.ForeignKey(Shop)
price = models.FloatField()
updated = models.DateTimeField()
然后我有一个页面,我想在上面列出所有产品、可以在哪些商店找到它们以及价格。是否可以对所有(或仅部分)产品的价格进行排序,而不使用:
class Meta:
ordering = ['-price']
关于产品价格?
也就是说,在我看来,我想做这样的事情(在价格列上对 productprice_set 进行排序):
def get_queryset(self):
queryset = Product.objects.all()
for product in queryset:
product.productprice_set.order_by('-price')
# Have also tried the following, without any result
product.productprice_set = product.productprice_set.order_by('-price')
return queryset
澄清一下:我想对每种产品的价格进行排序,而不是根据价格对产品进行排序。但我想通过“通过”产品的方式来做到这一点。 我要订购“productprice_set”,而不是“product”。
【问题讨论】:
呃,你想要做的“类似这样的事情”正是你会怎么做(除非你会使用反向关系的实际名称,product.productprices_set
.
@DanielRoseman 现在将我的帖子编辑为正确的。但它仍然不起作用。对于具有多个价格的产品,无论我有“价格”还是“-价格”,价格都保持不变。
嗯,我不太确定你想做什么。您实际上需要对您在那里订购的查询集做一些事情:当前该代码检索一个有序的查询集,然后将其丢弃。通常你会以某种方式遍历它。
嗯,它在视图中,我希望它以这种方式发送到模板。我再次更新了帖子并在最后添加了说明。这不是我要订购的查询集,而是查询集中的“productprice_set”部分。
这可能有用:***.com/questions/5437335
【参考方案1】:
该语法不会以某种方式修改关系,以便后续调用返回一个有序的查询集:它当时和那里返回有序的 qs。后续调用将采用默认顺序。
执行此操作的方法是在检索对象时使用 order_by 子句。由于您不能将参数传递给模板中的方法,因此可以在 Product 上定义一个返回订购价格的方法:
class Product(models.Model):
...
def ordered_prices (self):
return self.productprices_set.order_by('-price')
并在模板中调用它:
% for product in products %
product.name
% for price in product.ordered_prices %
% price.price %
% endfor %
% endfor %
【讨论】:
谢谢!我想你让我明白我正在做的事情是行不通的,以及为什么行不通。也是一个解决方案。但我还是不明白一件事。如果我将所有数据加载到查询集中,修改一些东西,然后返回它。这个修改后的版本不应该是发送到模板的版本吗?我尝试在 for 循环中为每个产品添加一个属性,然后我可以在模板中访问它。还是在我的模板中,当我从产品中获取价格时会生成一个新的查询集? (我以为之前所有的东西都是在 'get_queryset' 方法中加载的。) 如果您修改了产品就是这种情况。但是对 product.productprice_set 的每次调用都是对数据库的新调用,因此是一个新的查询集。我想解决这个问题的另一种方法是在视图中坚持原来的循环,但将订购的价格存储在每个产品的新属性中。这可行,但我认为我原来的解决方案更干净。 好吧,我明白了。我喜欢你的解决方案,但我有一个小问题。这是您将价格信息添加到产品模型中。我将它们放在两个单独的应用程序中,将这些信息添加到产品模型中会使将来重新使用该应用程序变得更加困难。但也许只有我完全过分/误解了如何将项目分成应用程序。【参考方案2】:我现在找到了一个解决方案,我不需要修改任何模型,也不需要进行任何数据库查询。
在我看来,我现在有:
def get_queryset(self):
queryset = Product.objects.all().prefetch_related('price_set')
for product in queryset:
product.prices = sorted(product.price_set.all(), key=lambda x: x.price, reverse=true)
return queryset
然后在我的模板中:
% for product in products %
product.name
% for price in product.prices %
% price.price %
% endfor %
% endfor %
由于我是使用 Django 的新手,我想知道这样解决它是否有什么“错误”或不好的地方?
【讨论】:
嗯,这几乎就是我之前提到的“替代方案”。我唯一的意见是最好依靠数据库进行排序:所以像以前一样使用product.prices = product.price_set.order_by('-price')
。
是的,我刚刚意识到这一点。但是,让数据库执行此操作将为每个产品创建一个新查询。在 Python 代码中使用它,将不会有额外的查询(使用 prefetch_related)。你还是说最好有这些额外的查询并让数据库工作吗?
刚刚对它们都进行了测试,调试工具栏显示让 Python 进行排序时 CPU 时间和查询时间都要低得多。我只有 5 种产品,其中一种有价格。以上是关于以多对多关系对相关集进行排序的主要内容,如果未能解决你的问题,请参考以下文章
如何让所有学生以多对多关系注册到特定课程列表中(1 和 2)
iPhone:无法对多对关系进行 NSSortDescriptor。如何排序?