【问题标题】:Django ORM - Get distinct objects based on stringDjango ORM - 根据字符串获取不同的对象
【发布时间】:2021-03-03 12:13:49
【问题描述】:

我的对象在 mariaDB 上有这个布局(这里没有 postgres):

+------+------------------------------+----------+---------+--------------------------------------------+
|  id  |           created            | message  |  from   |                  guest_id                  |
+------+------------------------------+----------+---------+--------------------------------------------+
| '45' | '2021-03-02 21:32:56.756966' | 'Hello ' | 'host'  | 'e5b15b726e1224ca1d70ad9cf6a6e2a485de5478' |
| '46' | '2021-03-02 23:32:56.756966' | 'bye'    | 'guest' | 'e5b15b726e1224ca1d70ad9cf6a6e2a485de5478' |
| '47' | '2021-03-02 24:32:56.756966' | 'still ' | 'guest' | 'a7c3bdf1adc0e9f821f8727e83e76ae3821cf47e' |
| '48' | '2021-03-03 00:32:56.756966' | 'here '  | 'host'  | 'a7c3bdf1adc0e9f821f8727e83e76ae3821cf47e' |
+------+------------------------------+----------+---------+--------------------------------------------+

如您所见,我对每个 guest_id 有两个结果,我希望只有最新“创建”的最后一个结果:

+------+------------------------------+---------+---------+--------------------------------------------+
|  id  |           created            | message |  from   |                  guest_id                  |
+------+------------------------------+---------+---------+--------------------------------------------+
| '46' | '2021-03-02 23:32:56.756966' | 'bye'   | 'guest' | 'e5b15b726e1224ca1d70ad9cf6a6e2a485de5478' |
| '48' | '2021-03-03 00:32:56.756966' | 'here ' | 'host'  | 'a7c3bdf1adc0e9f821f8727e83e76ae3821cf47e' |
+------+------------------------------+---------+---------+--------------------------------------------+

这是我想出的:

queryaux    = Message.objects.filter(guest__reservation__home=home).values('guest_id').distinct().order_by()
queryset    = Message.objects.filter(guest__in=queryaux).latest('created')

queryaux 包含我需要的元素集(没有重复),但只有 guest_id,而不是完整对象(因为 values())

queryset应该有对应的对象,但它只获取一个对象,集合中最新的一个

我的解决方案(使用迭代器和查询集的串联 - 可能不快):

pseudo-code:

for elem in queryset: 
    final_queryset += Message.objects.filter(guest=elem).latest('created')

你有什么建议以正确的方式做到这一点?

谢谢。

编辑: 为了完整起见,这是消息模型:

class Message(models.Model):
    guest         = models.ForeignKey(Guest, related_name='messages', on_delete=models.CASCADE)
    created       = models.DateTimeField(default=timezone.now)
    message       = models.CharField(max_length=512)
    msgfrom       = models.CharField(max_length=1,choices=MSGFROM)
    class Meta:
        ordering = ['created'] 

【问题讨论】:

    标签: django django-rest-framework


    【解决方案1】:

    您可能想在这里使用subquery

    >>> from django.db.models import Subquery, OuterRef
    >>> messages_ordered = Message.objects.filter(guest__id=OuterRef('id')).order_by('-created')
    >>> for g in Guest.objects.values('id').annotate(latest_message_id=Subquery(messages_ordered.values('id')[:1])):
    ...     print(g['id'], g['latest_message_id'])
    ...
    a7c3bdf1adc0e9f821f8727e83e76ae3821cf47e 48
    e5b15b726e1224ca1d70ad9cf6a6e2a485de5478 46
    

    messages_ordered 为您提供由客人过滤并按最新创建日期排序的QuerySet

    然后您可以使用messages_ordered QuerySet 作为子查询来注释另一个QuerySet,您可以在其中使用Guest.objects.values('id') 迭代来宾。

    【讨论】:

    • 感谢您的回答。我理解原理,但最后,它确实给了我所有消息的列表:pastebin.com/8hDqxNNL。有趣的是,这些 ID 与消息不匹配,但 45 是来自该 guest_id 的最新消息。
    • 啊,guest 是另一个模型的正确ForeignKey,这改变了一切!然后你需要遍历Guest 模型并注解QuerySet。此外,您不需要distinct(),因为我希望您在Guest 的客人是独一无二的。我修改了代码,当我在更改后的模型上运行它时对我有用。
    • 不是这样,因为 fk 是一个字符串(令牌),我仍然可以使用它。在您的帮助下,我达到了最终目标:pastebin.com/9v0eu18R。它就像一个魅力。必须说,此时我已经接近宁愿使用原始 sql 了。由于您已帮助我找到解决方案,我会将您的答案标记为正确答案。
    【解决方案2】:

    试试:

     Message.objects.filter(guest__reservation__home=home).distinct('guest_id').order_by()
    

    【讨论】:

    • distinct('keyword') 不能在 postgres 以外的数据库引擎中工作,但感谢您的输入 Sergey
    猜你喜欢
    • 2013-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-16
    • 2019-01-24
    • 1970-01-01
    • 2021-03-12
    • 1970-01-01
    相关资源
    最近更新 更多