Natural sort on Django Queryset

﹥>﹥吖頭↗ 提交于 2019-12-01 01:58:51

问题


I am working on a system that lists out a range of products sorted by their product code. The product codes are made up of two letters for the followed by a number, for example EG1.

I currently sort these products by doing a simple

Product.objects.order_by('product_code'),

however as there can be multiple digit product codes (for example EG12), these will come out above ahead of the single digit codes. i.e EG1, EG11, EG12, EG13 ... EG19, EG2, EG20 etc

I know that adding leading zeros to the product codes will fix this (i.e EG01 rather than EG1) but as there is already printed literature and an existing site using EG1 this is not an option.

Is there a way to fix this to show these products in the correct order?


回答1:


I think the implementation here (https://github.com/nathforge/django-naturalsortfield) should work. The main advantage of this method is that it doesn't do the sorting in python but in the database so it'll perform well even on large datasets, at the cost of some additional storage.

You have to change your model to include a product_code__sort field

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

where the NaturalSortField is defined as

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:


I don't currently have enough reputation to comment. Wanted to provide an update for those of you using Django version 2 or above who are having problems.

    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




回答3:


Try this

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)


来源:https://stackoverflow.com/questions/42535451/natural-sort-on-django-queryset

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!