我查询一个模型:

Members.objects.all()

它返回:

Eric, Salesman, X-Shop
Freddie, Manager, X2-Shop
Teddy, Salesman, X2-Shop
Sean, Manager, X2-Shop

我想要的是知道Django最好的点火方式 对我的数据库进行group_by查询,如:

Members.objects.all().group_by('designation')

当然,这是行不通的。 我知道我们可以在django/db/models/query.py上做一些技巧,但我只是好奇如何不打补丁就能做到。


当前回答

如果你想要模型对象,而不仅仅是简单的值或字典,你可以这样做:

members = Member.objects.filter(foobar=True)
designations = Designation.objects.filter(member__in=members).order_by('pk').distinct()

将member__in替换为模型名的小写版本,后跟__in。例如,如果您的模型名称是Car,则使用car__in。

其他回答

你需要做自定义SQL的例子在这段代码:

自定义SQL通过子查询

或者在Django在线文档中所示的自定义管理器中:

添加额外的Manager方法

下面的模块允许你对Django模型进行分组,并且仍然使用结果中的QuerySet: https://github.com/kako-nawao/django-group-by

例如:

from django_group_by import GroupByMixin

class BookQuerySet(QuerySet, GroupByMixin):
    pass

class Book(Model):
    title = TextField(...)
    author = ForeignKey(User, ...)
    shop = ForeignKey(Shop, ...)
    price = DecimalField(...)

class GroupedBookListView(PaginationMixin, ListView):
    template_name = 'book/books.html'
    model = Book
    paginate_by = 100

    def get_queryset(self):
        return Book.objects.group_by('title', 'author').annotate(
            shop_count=Count('shop'), price_avg=Avg('price')).order_by(
            'name', 'author').distinct()

    def get_context_data(self, **kwargs):
        return super().get_context_data(total_count=self.get_queryset().count(), **kwargs)

“本书/ books.html”

<ul>
{% for book in object_list %}
    <li>
        <h2>{{ book.title }}</td>
        <p>{{ book.author.last_name }}, {{ book.author.first_name }}</p>
        <p>{{ book.shop_count }}</p>
        <p>{{ book.price_avg }}</p>
    </li>
{% endfor %}
</ul>

与注释/聚合基本Django查询的区别在于使用了相关字段的属性,例如book.author.last_name。

如果您需要已经分组在一起的实例的pk,请添加以下注释:

.annotate(pks=ArrayAgg('id'))

注意:ArrayAgg是Postgres特定的函数,从Django 1.9开始可用:https://docs.djangoproject.com/en/3.2/ref/contrib/postgres/aggregates/#arrayagg

一个简单的解决方案,但不是正确的方法是使用原始SQL:

results = Members.objects.raw('SELECT * FROM myapp_members GROUP BY designation')

另一个解决方案是使用group_by属性:

query = Members.objects.all().query
query.group_by = ['designation']
results = QuerySet(query=query, model=Members)

现在可以遍历结果变量以检索结果。请注意,group_by没有文档,在Django的未来版本中可能会被更改。

和…为什么要使用group_by?如果不使用聚合,可以使用order_by来实现类似的结果。

Django不支持自由分组查询。我是用一种非常糟糕的方式学会的。ORM不是设计来支持你想做的事情的,不使用自定义SQL。你被限制在:

RAW sql(即MyModel.objects.raw()) cr.执行句子(以及对结果的手工解析)。 .注解()(按句子分组在.注解()的子模型中执行,例如aggregating lines_count=Count('lines')))。

对于一个queryset,你可以调用qs.query。Group_by = ['field1', 'field2',…],但如果你不知道你在编辑什么查询,也不能保证它会工作,不会破坏QuerySet对象的内部结构,这是有风险的。此外,它是一个内部的(未被记录的)API,你不应该直接访问,否则会有代码与未来的Django版本不再兼容的风险。

这有点复杂,但让提问者他/她期望只有一个DB命中。

from django.db.models import Subquery, OuterRef

member_qs = Members.objects.filter(
    pk__in = Members.objects.values('designation').distinct().annotate(
        pk = Subquery(
          Members.objects.filter(
            designation= OuterRef("designation")
        )
        .order_by("pk") # you can set other column, e.g. -pk, create_date...
        .values("pk")[:1]
        ) 
    )
   .values_list("pk", flat=True)
)