Flask-Mongoengine 自定义排序比较器
Flask-Mongoengine custom order-by comparator
我正在使用 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
中使用的比较器。问题是,我不确定如何正确执行此操作。我是否必须定义自定义属性类型,或者是否有一些我应该覆盖的挂钩?
无论如何,在我开始重写轮子之前,我想我会像 Whosebug 上的好人一样。
在深入研究 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
。
我正在使用 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
中使用的比较器。问题是,我不确定如何正确执行此操作。我是否必须定义自定义属性类型,或者是否有一些我应该覆盖的挂钩?
无论如何,在我开始重写轮子之前,我想我会像 Whosebug 上的好人一样。
在深入研究 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
。