Как отсортировать список (Paginator), содержащий множество полей

Python: 3.9.10 Django: 4.0.3

Я использую Django Paginator.

Для сортировки данных на каждой странице, и только данных на этой странице, я передаю свой набор запросов в Paginator, преобразую Paginator в список, а затем сортирую список по ключу с помощью attrgetter().

Моя модель содержит два поля ManyToManyFields, поэтому я не могу отсортировать ее с помощью стандартного метода sorted(), так как получаю сообщение об ошибке:

TypeError: '<' не поддерживается между экземплярами 'ManyRelatedManager' и 'ManyRelatedManager'

Кажется, я понимаю, почему это происходит - sorted() не может сравнить два списка (manytomanyfields) друг с другом и определить, какой из них больше или меньше - поэтому список(Paginator) не может быть отсортирован таким образом.

Я представляю, что мне, вероятно, нужно выполнить итерацию по списку(Paginator), получить первое значение поля manytomanyfield и затем как-то отсортировать все по нему. Я не знаю, как лучше всего это сделать.

models.py

class DataExchange(models.Model):
    name = models.CharField(unique=True, max_length=15, null=True)

    def __str__(self):
        return self.name

class DataSource(models.Model):
    name = models.CharField(unique=True, max_length=10, null=True)

    def __str__(self):
        return self.name

class Provider(models.Model):
    name = models.CharField(unique=True, max_length=15, null=True)
    exchange = models.ManyToManyField(DataExchange, related_name="providers_exchange")
    source = models.ManyToManyField(DataSource, related_name='providers_source')
    score = models.IntegerField(null=True)

    def __str__(self):
        return self.name

Вспомогательная функция пагинатора: order_by = "exchange"

def paginator_helper(request, paginator):

    page = request.GET.get("page")
    
    if page is None:
        page = 1
    try:
        page_obj = paginator.get_page(page)
    except PageNotAnInteger:
        page_obj = paginator.page(1)
        page = 1
    except EmptyPage:
        page_obj = paginator.page(paginator.num_pages)
        page = page_obj.number
    
    order_by = request.GET.get("orderby")
    
    if order_by is None:
        order_by = "name"
    
    ascending = request.GET.get("ascending")
    if ascending is None:
        ascending = "0"
    
    if ascending == "1":
        ascending = True
    else:
        ascending = False
     
    page_obj = list(page_obj)
            
    if ascending:
        page_obj = sorted(page_obj, key=attrgetter(order_by))
        
    else:
        page_obj = sorted(page_obj, key=attrgetter(order_by), reverse=True)
    
    return page_obj, paginator_obj, order_by, ascending

Ответ - использовать аннотацию. Благодаря: Order a django queryset by ManyToManyField = Value

И документация: https://docs.djangoproject.com/en/4.0/ref/models/conditional-expressions/

Использование аннотации первоначально решило проблему, хотя и создало дополнительные строки, которые привели к тому, что число страниц num_pages стало недопустимым. Лучший способ решить эту проблему - не использовать manytomanyfield, а использовать https://pypi.org/project/django-multiselectfield/ - таким образом данные сохраняются как поле символов, разделенное запятыми, которое можно сортировать как любое другое поле.

Вернуться на верх