You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django中如何按@property定义的关联数量对多对多关系排序?

Django后台多对多字段按关联数量排序解决方案

嗨,我来帮你搞定这个Django后台的排序问题!先理清楚你的场景:你有Site和Product的多对多关系,Product里定义了sites_count属性统计关联站点数量,现在想在SiteAdmin的多对多选择框里,让Product按这个数量排序对吧?

你的现有代码

先把你给出的模型和Admin类片段整理出来:

模型代码

from django.db import models

class Site(models.Model):
    articles = models.ManyToManyField(Article, blank=True, null=True)
    products = models.ManyToManyField(Product, blank=True, null=True)

class Product(models.Model):
    @property
    def sites_count(self):
        return self.site_set.count()

现有Admin类片段

class SiteAdmin(admin.ModelAdmin):
    def formfield_for_manytomany(self, db_field, request, **kwargs):
        if db_field.name == "articles":
            # 你原来的代码片段
            ...

实现排序的关键修改

这里要注意:模型里的sites_count是Python层面的属性,没法直接用于数据库级别的排序。我们需要用Django ORM的annotate方法,在数据库层面计算每个Product关联的Site数量,再基于这个结果排序。

完整的SiteAdmin代码如下:

from django.contrib import admin
from django.db.models import Count
from .models import Site, Article, Product

class SiteAdmin(admin.ModelAdmin):
    def formfield_for_manytomany(self, db_field, request, **kwargs):
        # 处理products多对多字段的排序逻辑
        if db_field.name == "products":
            # 注释出每个Product关联的站点数量,按数量倒序排序
            kwargs["queryset"] = Product.objects.annotate(
                sites_count=Count('site')
            ).order_by('-sites_count')
        # 如果articles字段也需要类似排序,可以在这里添加对应逻辑
        elif db_field.name == "articles":
            # 比如若Article也有统计需求,同样用annotate+order_by实现
            pass
        # 调用父类方法返回处理后的字段
        return super().formfield_for_manytomany(db_field, request, **kwargs)

补充说明

  • Count('site')用于统计每个Product关联的Site数量,这里的site是反向关联的默认名称(对应Site模型的小写形式)。
  • order_by('-sites_count')表示按站点数量倒序排列,若要正序排序,去掉负号改成'sites_count'即可。
  • 这个修改会覆盖products多对多字段的默认查询集,让后台选择框里的产品按关联站点数量排序展示。

内容的提问来源于stack exchange,提问作者Tinker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:20:18