【问题标题】:How do I do a Django query on a Model, where I order by field A, but filter distinct on field B?如何对模型进行 Django 查询,我按字段 A 排序,但在字段 B 上过滤不同?
【发布时间】:2020-05-17 15:59:12
【问题描述】:

假设我有一个 Book 表,其中每本书都有一个作者字段和一个出版日期字段。

我想获得每位作者的最新书籍。我使用 PostgreSQL 作为后端。

明显(和错误)的解决方案是:

Book.objects.order_by("author", "-published_on").distinct("author").all()

问题在于,虽然结果只包含每位作者的一本书,但不能保证它是最新一本书。这可能是因为我使用随机 UUID 作为 PK。我无法改变这一点。这是一个要求。

下一个明显(也是错误的)解决方案是:

Book.objects.order_by("author", "-published_on").distinct("author", "published_on").all()

这里书籍的顺序是正确的,但是我们从同一个作者那里得到了多本书。

我也尝试过翻转论点:

Book.objects.order_by("-published_on", "author").distinct("published_on", "author").all()

这里书籍的顺序是正确的,但是我们从同一个作者那里得到了多本书。

如何进行 Django ORM 查询,从哪里获取每位作者的最新书籍?

编辑:这是我实际在我们的实时数据库上运行的查询,然后将其转换为书本式示例:

from db.models import User, EventVisibility
user = User.objects.get(username="7g8jltdzbz46ak7nhuz8tzfuu7y9mdym7tiy7klfxjnn")
evs = EventVisibility.objects.filter(user=user).order_by("room", "-created_on").distinct("room")[:20]
for ev in evs:
    print(f"book_id={ev.room.room_id}, published_on={ev.created_on}")

结果如下:

book_id=2mcnhajfwf5jsgyzpqix36ytbjfucn9u6derkyurlfff, published_on=2020-05-16 00:54:05.083477+00:00
book_id=4rp9ffxqr5marnphbtlahqtwnkzozupyb8ht532ffxl6, published_on=2020-05-12 20:29:31.286095+00:00
book_id=5dqygkksrzq6ay49xxcspagma5cbz8p59sjcavf6pepm, published_on=2020-05-08 09:28:53.508563+00:00
book_id=9mz85qcxreaczcnenebcywqqm3scehjhpwlkso7g4jbd, published_on=2020-05-04 10:52:06.396995+00:00
book_id=9sgiiasbvbtat4iahx7bd7ammzwatgfipe8wmzl9snz5, published_on=2020-05-15 09:00:52.602512+00:00
book_id=b8uvcxuhgjhmvkjjnwkcr5zzj7hrushz2e9mpzkosg8k, published_on=2020-05-08 09:36:47.148885+00:00
book_id=bxif8aal2v4fb3p8wsdvdard5p65ygw8j92tnleqqza4, published_on=2020-04-19 02:43:23.819854+00:00
book_id=cgoad7xuwjhxz6hcxctbl5arnnsrjt5osuwmzunmppra, published_on=2020-05-08 09:36:06.944614+00:00
book_id=cztb84akqqde6fvpj2nneqezvmor5gdjh3hpcjnxcz2x, published_on=2020-05-15 10:06:53.054862+00:00
book_id=czxizxptbvxz7jybkxevk2mkmaxykhgakfluud7ffa2b, published_on=2020-05-17 14:54:43.245325+00:00
book_id=dgtze2ri5snrr7nmurvdechydxjd2ph3dd8rugibn2me, published_on=2020-05-05 19:16:45.254928+00:00
book_id=dp9wu8qmdw6prsvx2zwvrnw5akcxv6llcwa2skeadcpx, published_on=2020-04-27 10:58:32.555542+00:00
book_id=duelfazwfiek8jhr4ew7wa9vrzzuyhznzxcrpybmbuww, published_on=2020-05-15 10:06:45.001961+00:00
book_id=dwhqxqfyolggdf5wwwm3su3yq6ffsh5kwwjxj7wtkdbj, published_on=2020-05-15 05:53:01.153492+00:00
book_id=edakxxhqv7w99lukxr23dfugcarddpwj5ea8wx7r5bmd, published_on=2020-04-27 19:49:29.673872+00:00
book_id=evz9biehu88eds7hgcutw6jfktt4fkjznfgozxsu8jtk, published_on=2020-04-20 21:13:01.693752+00:00
book_id=fqnxa3j4vbbaw7fc5hgrumabtfh2phmd3hg7cgm5ayfa, published_on=2020-05-15 10:04:22.322094+00:00
book_id=gkxahh8y7eqtqzxsnjtdpnghxnipi8vx3qugjcrs6t3m, published_on=2020-04-17 02:14:31.219950+00:00
book_id=hdgoxpnmqde8siwdbgfwwtodqk4hzhefyz8pw3esdmem, published_on=2020-05-17 14:46:49.437289+00:00
book_id=jrg6uae5kyvfvjgjhmwvzf45lbtqmgspawbuqzfewnhc, published_on=2020-05-05 09:11:59.334099+00:00

这是queryset.query

SELECT DISTINCT ON ("db_eventvisibility"."room_id") "db_eventvisibility"."id", "db_eventvisibility"."event_id", "db_eventvisibility"."user_id", "db_eventvisibility"."room_id", "db_eventvisibility"."unit_id", "db_eventvisibility"."case_id", "db_eventvisibility"."team_id", "db_eventvisibility"."created_on" FROM "db_eventvisibility" WHERE "db_eventvisibility"."user_id" = 7g8jltdzbz46ak7nhuz8tzfuu7y9mdym7tiy7klfxjnn ORDER BY "db_eventvisibility"."room_id" ASC, "db_eventvisibility"."created_on" DESC LIMIT 20

【问题讨论】:

  • 其实有保证的,因为如果你.order_by()也包括published_on,那么它总是会取第一个。
  • 我也是这么想的,但是不行。
  • 文档说:“例如,SELECT DISTINCT ON (a) 为您提供了 a 列中每个值的 第一行。如果您没有指定订单,你会得到一些任意的行。”。这是 PostgreSQL 特有的,所以我希望,鉴于文档,并且 PgSQL 的行为没有改变,这会起作用。
  • 您可以看到 Django 使用print(queryset.query) 生成的查询。例如,也许您制作了一些对象管理器,它在查询中引入了一些问题。
  • 是否可以edit该问题并显示Django ORM生成的查询?

标签: django postgresql django-models django-orm


【解决方案1】:

问题在于,虽然结果只包含每位作者的一本书,但不能保证它是最新一本书。这可能是因为我使用随机 UUID 作为 PK。我无法改变这一点。这是一个要求。

据我所知,从 per Room 的意义上来说,结果是正确的,您确实得到了最新的 EventVisibility,但很可能这不是您想要的。如果你想按照最新的EventVisibilityRooms 进行排序,那么你可以这样做:

from django.db.models import Max

Room.objects.filter(
    eventvisibility__user=user
).order_by(
    Max('eventvisibitility__created_on').desc()
)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-11
    • 2015-03-25
    • 2021-08-05
    • 2019-07-15
    • 1970-01-01
    • 2012-09-30
    • 2015-08-31
    相关资源
    最近更新 更多