如何使Django Queryset选择组内最大值的记录

时间:2015-09-02 17:51:15

标签: python django django-models orm

这是我的Django课程:

class MyClass(models.Model):
    my_integer = models.IntegerField()
    created_ts = models.DateTimeField(default=datetime.utcnow, editable=False)

我想检索MyClass的每个唯一值为created_ts的最新my_integer的实例。我无法弄明白该怎么做。

有人可以告诉我该怎么做吗?

6 个答案:

答案 0 :(得分:4)

这会帮助您

['Rafael-Debit', 'Daniel-Credit', 'Monica-Debit', 'Monica-Credit']

表格中的数据

from django.db.models import Count, Max
MyClass.objects.values('my_integer').annotate(count=Count("my_integer"),latest_date=Max('created_ts'))

<强>输出

  my_integer      created_ts
    -             -----------
    1 2015-09-08 20:05:51.144321+00:00
    1 2015-09-08 20:08:40.687936+00:00
    3 2015-09-08 20:08:58.472077+00:00
    2 2015-09-08 20:09:08.493748+00:00
    2 2015-09-08 20:10:20.906069+00:00

答案 1 :(得分:0)

未测试

results = MyClass.objects.all().distinct('my_integer').order_by('created_ts')

答案 2 :(得分:0)

MyClass.objects.order_by('my_integer', '-created_ts').distinct('my_integer')

根据distinct,您需要按照order_by中的顺序调用不同的属性。因此,基于整数然后在反向时间戳中对元素进行排序,并在它们上调用distinct,它返回每个整数的最新实例。

答案 3 :(得分:0)

试试这个;

      // create the sharepoint chrome control
        var nav = new SP.UI.Controls.Navigation("chrome_ctrl_container", spChromeControlData);

        // show chrome control
        nav.setVisible(true);

        // hide top app chrome (image & app name)
        nav.setBottomHeaderVisible(false);

        //fix issues with chrome ctrl
        $('body').on('click', '.ms-core-menu-root', function () {
            $(this).attr("href", "javascript:;")
        });

        logger.log("spAppChrome loaded", null, controllerId);
        common.activateController([], controllerId);
    }

答案 4 :(得分:0)

这是一个非常基本的方法。假设您拥有的数据量不是很大,那么这将非常有效。您可以通过覆盖get_queryset函数并返回过滤来在视图中使用它。或者,如果您计划在任何地方使用它,您可以将它用作课程中的静态方法。

values = MyClass.objects.order_by('-created_ts').all()

filtered = []
existing = []
for value in values:
    if value.my_integer not in existing:
        existing.append(value.my_integer) 
        filtered.append(value)  

由于列表是按最近的顺序排序的,因此它们将被添加到该整数的现有列表中。我用它进行了一些基本的测试,但并不多,所以可能存在一两个缺陷。 使用sqlite进行测试。

修改

这是一个更快的版本。

def iter_tools():
    import itertools
    qs = MyClass.objects.all()
    filtered = []
    group_by = itertools.groupby(qs, lambda x: x.my_integer)
    for x in group_by:
        filtered.append(sorted(x[1], key=lambda x: x.created_ts, reverse=True)[0])
    return filtered

本质上,它的工作方式是从数据库中获取所有对象,按整数对它们进行分组,然后根据时间戳对每个组进行排序,并从每个组中获取第一个。超越这个超出我的技能,但我相信有一些方法。

这是前一个timeit与之前的In[]: timeit.timeit(manual, number=1500) Out[]: 0.5577559471130371 In[]: timeit.timeit(iter_tools, number=1500) Out[]: 0.39012885093688965 ----------------------------------------------- In[]: timeit.timeit(manual, number=5000) Out[]: 1.770777940750122 In[]: timeit.timeit(iter_tools, number=5000) Out[]: 1.2411231994628906 只有6个条目:

datetime.now()

编辑2: 我为数据库创建了60000个对象,以便用一些数据进行试验。我使用django-fixtureless生成数据,因此整数是完全随机的,并且所有这些数据的时间戳都是每个对象的新In[]: timeit.timeit(manual, number=1) Out[]: 11.946185827255249 In[]: timeit.timeit(iter_tools, number=1) Out[]: 0.7811920642852783 In[]: timeit.timeit(iter_tools, number=100) Out[]: 77.93837308883667 In[]: MyClass.objects.all().count() Out[]: 60000

In[16]: MyClass.objects.all().count()
Out[16]: 60000
In[17]: timeit.timeit(iter_tools, number=100)
Out[17]: 49.636733055114746
In[18]: timeit.timeit(iter_tools, number=1)
Out[18]: 0.4923059940338135

关于数据库的说明:在上面的示例中,我在本地计算机上使用了sqlite3。我刚刚设置了一个快速的小型mysql服务器作为vm并获得了更好的速度结果。

h1 {
       }

h1{
    color: bisque;
  }

h2{
    color: bisque;
  }

body {
    background-color: aquamarine;
  }

无论哪种方式,都会返回相同的对象。如果性能是一个问题,我建议使用itertools one或自定义sql查询。

答案 5 :(得分:0)

您可以执行原始查询:

MyClass.objects.raw("""
SELECT m1.id, m1.my_integer, m1.created_ts
FROM app_myclass AS m1, (
    SELECT my_integer, MAX(created_ts) AS max_created_ts
    FROM app_myclass
    GROUP BY my_integer
) AS m2
WHERE m1.my_integer = m2.my_integer AND m1.created_ts = m2.max_created_ts
"""))

或者使用Django ORM:

MyClass.objects.filter(
    created_ts__in=MyClass.objects.values(
        "my_integer"
    ).annotate(
        created_ts=models.Max(
            "created_ts"
        )
    ).values_list("created_ts", flat=True)
)

请注意,这只需要一个SQL请求,您可以通过在查询之前和之后打印len(django.db.connection.queries)来查看。

但是,请注意,后一种解决方案仅在保证created_ts属性唯一时才有效,这可能不是您的情况。

如果您不愿意在created_ts上使用原始查询或索引,那么您应该开始使用PostgreSQL及其DISTINCT ON功能,如其他答案所示。