Как отсортировать список (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/ - таким образом данные сохраняются как поле символов, разделенное запятыми, которое можно сортировать как любое другое поле.