【问题标题】:Flask-Mongoengine custom order-by comparatorFlask-Mongoengine 自定义排序比较器
【发布时间】:2020-07-03 17:41:19
【问题描述】:

我正在使用 Flask-Mongoengine 作为我的烧瓶应用程序的 ORM。我定义了一个 Document 来将唯一版本存储在一个字符串中:

MyDocument(db.Document):
    version = db.StringField(primary_key=True, required=True)

StringField中存储的值格式如下:

a.b.c.d

在使用order_by 进行查询时遇到问题:

MyDocument.objects.order_by('version')

更准确地说,如果版本的第一部分(上述格式示例中的a)包含多个数字,则无法正确排序(例如:15.20.142 1.7.9)。虽然我显然可以做类似的事情:

tmp = Document.objects.all()
result = some_merge_sort(tmp)

但是,这需要编写、测试和维护排序算法。这让我想到了下一个解决方案,即覆盖order_by 中使用的比较器。问题是,我不确定如何正确执行此操作。我必须定义一个自定义属性类型,还是有一些我应该覆盖的钩子?

无论如何,在我重新编写轮子之前,我想我会成为 stackoverflow 上的好人。

【问题讨论】:

  • 嘿,谢谢你的详细问题。您是否还可以添加您现在拥有的代码的 sn-p,理想情况下易于重现?这将有助于其他人为您提供量身定制的答案。
  • @NickShebanov 我已经添加了相关代码sn-ps。

标签: python-3.x mongoengine flask-mongoengine


【解决方案1】:

在深入研究Mongoengine 源代码后,我发现它使用了PyMongo cursor sort,它本身包装了对MongoDB 的查询。换句话说,我的Document 定义在链中太高了,甚至不会影响order_by 结果。

因此,我采用的解决方案是写一个classmethod。这只是接受mongoengine.queryset.QuerySet 实例并对version 属性应用排序。

from mongoengine.queryset import QuerySet
from packaging import version

class MyDocument(db.Document):
    version = db.StringField(primary_key=True, required=True)

    @classmethod
    def order_by_version(_, queryset, reverse=False):
        """
            Wrapper function to order a given queryset of this classes
            version attribute.

            :params:
                - `QuerySet` :queryset: - The queryset to order the elements.
                - `bool`     :reverse:  - If the results should be reversed.

            :raises:
                - `TypeError` if the instance of the given queryset is not a `QuerySet`.

            :returns:
                - `List` containing the elements sorted by the version attribute.
       """
       # Instance check the queryset.
       if not isinstance(queryset, QuerySet):
           raise TypeError("order_by_version requires a QuerySet instance!")

       # Sort by the version attribute.
       return sorted(queryset, key=lambda obj: version.parse(obj.version), reverse=reverse)

那么要利用这个方法,我可以简单地做如下:

MyDocument.order_by_version(MyDocument.objects)

至于我采用QuerySet 实例而不是从类方法中进行查询的原因,只是为了在进行排序之前有机会调用其他Mongoengine 方法。因为我使用了sorted,所以它会维护任何现有的顺序,所以我仍然可以使用内置的order_by来处理其他属性。

【讨论】:

    猜你喜欢
    • 2013-11-04
    • 1970-01-01
    • 1970-01-01
    • 2012-05-29
    • 2021-03-23
    • 1970-01-01
    • 1970-01-01
    • 2011-11-01
    • 2011-05-10
    相关资源
    最近更新 更多