Python: 3.9.10 Django: 4.0.3
I am using Django Paginator.
To sort the data on each page, and only the data on that page, I am passing my queryset to Paginator, converting Paginator to a list and then sorting the list by its key using attrgetter().
My model contains two ManyToManyFields and so I cannot sort it using the standard sorted() method as I receive the error message:
TypeError: '<' not supported between instances of 'ManyRelatedManager' and 'ManyRelatedManager'
I think I understand why this is happening - sorted() cannot compare two lists (manytomanyfields) against each other and determine which is greater than or less than - so therefore the list(Paginator) cannot be sorted in this way.
I imagine I probably have to iterate through the list(Paginator), get the first value of the manytomanyfield and then somehow sort everything by that. I am not what the best way is to go about doing this.
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
Paginator helper function: 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
CodePudding user response:
The answer is to use annotation. Thanks to: Order a django queryset by ManyToManyField = Value
And the docs: https://docs.djangoproject.com/en/4.0/ref/models/conditional-expressions/
CodePudding user response:
Using annotation did solve the problem initially, though it created additional rows which caused pagination num_pages to be an invalid number. The best way to solve this problem is not to use manytomanyfield but rather to use https://pypi.org/project/django-multiselectfield/ - this way the data is saved as a charfield, comma-separated - which can be sorted like any other field.