mirror of
https://github.com/inventree/InvenTree.git
synced 2025-12-16 09:18:10 +00:00
Implement generic "ordering" by parameter
This commit is contained in:
@@ -3,7 +3,18 @@
|
||||
import re
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db.models import Q
|
||||
from django.db.models import (
|
||||
Case,
|
||||
CharField,
|
||||
Exists,
|
||||
FloatField,
|
||||
Model,
|
||||
OuterRef,
|
||||
Q,
|
||||
Subquery,
|
||||
Value,
|
||||
When,
|
||||
)
|
||||
from django.db.models.query import QuerySet
|
||||
|
||||
import InvenTree.conversion
|
||||
@@ -207,3 +218,81 @@ def filter_parametric_data(queryset: QuerySet, parameters: dict[str, str]) -> Qu
|
||||
)
|
||||
|
||||
return queryset
|
||||
|
||||
|
||||
def order_by_parameter(
|
||||
queryset: QuerySet, model_type: Model, ordering: str | None
|
||||
) -> QuerySet:
|
||||
"""Order the provided queryset by a parameter value.
|
||||
|
||||
Arguments:
|
||||
queryset: The initial queryset to order.
|
||||
model_type: The model type of the items in the queryset.
|
||||
ordering: The ordering string provided by the user.
|
||||
|
||||
Returns:
|
||||
Ordered queryset.
|
||||
|
||||
Used to order returned parts based on their parameter values.
|
||||
|
||||
To order based on parameter value, supply an ordering string like:
|
||||
- parameter_<x>
|
||||
- -parameter_<x>
|
||||
|
||||
where:
|
||||
- <x> is the ID of the PartParameterTemplate.
|
||||
- A leading '-' indicates descending order.
|
||||
"""
|
||||
import common.models
|
||||
|
||||
if not ordering:
|
||||
# No ordering provided - return the original queryset
|
||||
return queryset
|
||||
|
||||
result = re.match(r'^-?parameter_(\d+)$', ordering)
|
||||
|
||||
if not result:
|
||||
# Ordering does not match the expected pattern - return the original queryset
|
||||
return queryset
|
||||
|
||||
template_id = result.group(1)
|
||||
ascending = not ordering.startswith('-')
|
||||
|
||||
template_exists_filter = common.models.Parameter.objects.filter(
|
||||
template__id=template_id,
|
||||
model_type=ContentType.objects.get_for_model(model_type),
|
||||
model_id=OuterRef('id'),
|
||||
)
|
||||
|
||||
queryset = queryset.annotate(parameter_exists=Exists(template_exists_filter))
|
||||
|
||||
# Annotate the queryset with the parameter value for the provided template
|
||||
queryset = queryset.annotate(
|
||||
parameter_value=Case(
|
||||
When(
|
||||
parameter_exists=True,
|
||||
then=Subquery(
|
||||
template_exists_filter.values('data')[:1], output_field=CharField()
|
||||
),
|
||||
),
|
||||
default=Value('', output_field=CharField()),
|
||||
),
|
||||
parameter_value_numeric=Case(
|
||||
When(
|
||||
parameter_exists=True,
|
||||
then=Subquery(
|
||||
template_exists_filter.values('data_numeric')[:1],
|
||||
output_field=FloatField(),
|
||||
),
|
||||
),
|
||||
default=Value(0, output_field=FloatField()),
|
||||
),
|
||||
)
|
||||
|
||||
prefix = '' if ascending else '-'
|
||||
|
||||
return queryset.order_by(
|
||||
'-parameter_exists',
|
||||
f'{prefix}parameter_value_numeric',
|
||||
f'{prefix}parameter_value',
|
||||
)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
"""Provides a JSON API for the Part app."""
|
||||
|
||||
import re
|
||||
|
||||
from django.db.models import Count, F, Q
|
||||
from django.urls import include, path
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
@@ -14,7 +12,6 @@ from drf_spectacular.utils import extend_schema_field
|
||||
from rest_framework import serializers
|
||||
from rest_framework.response import Response
|
||||
|
||||
import part.filters
|
||||
import part.tasks as part_tasks
|
||||
from data_exporter.mixins import DataExportViewMixin
|
||||
from InvenTree.api import (
|
||||
@@ -1088,31 +1085,9 @@ class PartList(
|
||||
queryset, self.request.query_params
|
||||
)
|
||||
|
||||
# queryset = self.filter_parametric_data(queryset)
|
||||
queryset = self.order_by_parameter(queryset)
|
||||
|
||||
return queryset
|
||||
|
||||
def order_by_parameter(self, queryset):
|
||||
"""Perform queryset ordering based on parameter value.
|
||||
|
||||
- Used if the 'ordering' query param points to a parameter
|
||||
- e.g. '&ordering=param_<id>' where <id> specifies the PartParameterTemplate
|
||||
- Only parts which have a matching parameter are returned
|
||||
- Queryset is ordered based on parameter value
|
||||
"""
|
||||
# Extract "ordering" parameter from query args
|
||||
ordering = self.request.query_params.get('ordering', None)
|
||||
|
||||
if ordering:
|
||||
# Ordering value must match required regex pattern
|
||||
result = re.match(r'^\-?parameter_(\d+)$', ordering)
|
||||
|
||||
if result:
|
||||
template_id = result.group(1)
|
||||
ascending = not ordering.startswith('-')
|
||||
queryset = part.filters.order_by_parameter(
|
||||
queryset, template_id, ascending
|
||||
# Apply ordering based on query parameter
|
||||
queryset = common.filters.order_by_parameter(
|
||||
queryset, Part, self.request.query_params.get('ordering', None)
|
||||
)
|
||||
|
||||
return queryset
|
||||
|
||||
Reference in New Issue
Block a user