Django Queryset上的自然排序

时间:2017-03-01 15:16:37

标签: python django natural-sort

我正在开发一个系统,列出按产品代码排序的一系列产品。产品代码由两个字母组成,后跟一个数字,例如EG1。

我目前通过简单的

对这些产品进行排序

Product.objects.order_by('product_code')

然而,由于可以存在多个数字的产品代码(例如EG12),因此这些代码将出现在单个数字代码之前。 即EG1,EG11,EG12,EG13 ...... EG19,EG2,EG20等

我知道在产品代码中添加前导零会解决这个问题(即EG01而不是EG1),但由于已经有印刷文献和使用EG1的现有网站,因此这不是一个选项。

有没有办法解决这个问题,以正确的顺序显示这些产品?

3 个答案:

答案 0 :(得分:1)

我目前没有足够的声誉来发表评论。希望为使用Django 2或更高版本的有问题的用户提供更新。

    def __init__(self, for_field, *args, **kwargs):
        self.for_field = for_field
        kwargs.setdefault('db_index', True)
        kwargs.setdefault('editable', False)
        kwargs.setdefault('max_length', 255)
        super().__init__(*args, **kwargs)

    def deconstruct(self):
        name, path, args, kwargs = super().deconstruct()
        kwargs['for_field'] = self.for_field
        return name, path, args, kwargs

    def pre_save(self, model_instance, add):
        return self.naturalize(getattr(model_instance, self.for_field))

    def naturalize(self, string):
        def naturalize_int_match(match):
            return '%08d' % (int(match.group(0)),)

        string = string.lower()
        string = string.strip()
        string = re.sub(r'^the\s+', '', string)
        string = re.sub(r'\d+', naturalize_int_match, string)

        return string

答案 1 :(得分:0)

我认为这里的实施(https://github.com/nathforge/django-naturalsortfield)应该有效。这种方法的主要优点是它不会在python中进行排序,而是在数据库中进行排序,因此即使在大型数据集上也会表现良好,但需要额外的存储。

您必须更改模型以包含product_code__sort字段

class MyModel(models.Model):
    title = models.CharField(max_length=255)
    title_sort = NaturalSortField('title')

其中NaturalSortField定义为

class NaturalSortField(models.CharField):
    def __init__(self, for_field, **kwargs):
        self.for_field = for_field
        kwargs.setdefault('db_index', True)
        kwargs.setdefault('editable', False)
        kwargs.setdefault('max_length', 255)
        super(NaturalSortField, self).__init__(**kwargs)

    def pre_save(self, model_instance, add):
        return self.naturalize(getattr(model_instance, self.for_field))

    def naturalize(self, string):
        def naturalize_int_match(match):
            return '%08d' % (int(match.group(0)),)

        string = string.lower()
        string = string.strip()
        string = re.sub(r'^the\s+', '', string)
        string = re.sub(r'\d+', naturalize_int_match, string)

        return string

答案 2 :(得分:0)

尝试一下

def alphanumeric_sort(objects_list, sort_key):
    """ Sort a list of objects by a given key
    This function sort a list of objects by a given
    key common across the objects
    Sorting can be implemented on keys that are either
    alphabets, integers or both
    """
    convert = lambda text: int(text) if text.isdigit() else text
    alphanum_key = lambda key: [
        convert(c) for c in re.split("([0-9]+)", getattr(key, sort_key))
    ]
    return sorted(objects_list, key=alphanum_key)