Features: 1) Ensure querysets return distinct results in filters;

Fixes: 1) Address potential duplication issue in querysets;

Extra:
This commit is contained in:
Egor Pavlovich Gorbunov 2025-06-19 09:42:02 +03:00
parent d40d7d3c28
commit d72c3947d4

View file

@ -48,19 +48,31 @@ class CaseInsensitiveListFilter(BaseInFilter, CharFilter):
class ProductFilter(FilterSet):
uuid = UUIDFilter(field_name="uuid", lookup_expr="exact", label=_("UUID"))
name = CharFilter(field_name="name", lookup_expr="icontains", label=_("Name"))
categories = CaseInsensitiveListFilter(field_name="category__name", label=_("Categories"))
categories = CaseInsensitiveListFilter(
field_name="category__name", label=_("Categories")
)
category_uuid = CharFilter(method="filter_category", label="Category (UUID)")
categories_slugs = CaseInsensitiveListFilter(field_name="category__slug", label=_("Categories Slugs"))
categories_slugs = CaseInsensitiveListFilter(
field_name="category__slug", label=_("Categories Slugs")
)
tags = CaseInsensitiveListFilter(field_name="tags__tag_name", label=_("Tags"))
min_price = NumberFilter(field_name="stocks__price", lookup_expr="gte", label=_("Min Price"))
max_price = NumberFilter(field_name="stocks__price", lookup_expr="lte", label=_("Max Price"))
min_price = NumberFilter(
field_name="stocks__price", lookup_expr="gte", label=_("Min Price")
)
max_price = NumberFilter(
field_name="stocks__price", lookup_expr="lte", label=_("Max Price")
)
is_active = BooleanFilter(field_name="is_active", label=_("Is Active"))
brand = CharFilter(field_name="brand__name", lookup_expr="iexact", label=_("Brand"))
attributes = CharFilter(method="filter_attributes", label=_("Attributes"))
quantity = NumberFilter(field_name="stocks__quantity", lookup_expr="gt", label=_("Quantity"))
quantity = NumberFilter(
field_name="stocks__quantity", lookup_expr="gt", label=_("Quantity")
)
slug = CharFilter(field_name="slug", lookup_expr="exact", label=_("Slug"))
is_digital = BooleanFilter(field_name="is_digital", label=_("Is Digital"))
include_subcategories = BooleanFilter(method="filter_include_flag", label=_("Include sub-categories"))
include_subcategories = BooleanFilter(
method="filter_include_flag", label=_("Include sub-categories")
)
order_by = OrderingFilter(
fields=(
@ -111,13 +123,16 @@ class ProductFilter(FilterSet):
)
self.queryset = self.queryset.annotate(
rating=Coalesce(
Subquery(feedback_qs, output_field=FloatField()), Value(0, output_field=FloatField())
Subquery(feedback_qs, output_field=FloatField()),
Value(0, output_field=FloatField()),
)
)
def filter_include_flag(self, queryset, **_kwargs):
if not self.data.get("category_uuid"):
raise BadRequest(_("there must be a category_uuid to use include_subcategories flag"))
raise BadRequest(
_("there must be a category_uuid to use include_subcategories flag")
)
return queryset
def filter_attributes(self, queryset, _name, value):
@ -240,7 +255,8 @@ class ProductFilter(FilterSet):
)
qs = qs.annotate(
rating=Coalesce(
Subquery(feedback_qs, output_field=FloatField()), Value(0, output_field=FloatField())
Subquery(feedback_qs, output_field=FloatField()),
Value(0, output_field=FloatField()),
)
)
return qs.distinct()
@ -252,14 +268,26 @@ class OrderFilter(FilterSet):
label=_("Search (ID, product name or part number)"),
)
min_buy_time = DateTimeFilter(field_name="buy_time", lookup_expr="gte", label=_("Bought after (inclusive)"))
max_buy_time = DateTimeFilter(field_name="buy_time", lookup_expr="lte", label=_("Bought before (inclusive)"))
min_buy_time = DateTimeFilter(
field_name="buy_time", lookup_expr="gte", label=_("Bought after (inclusive)")
)
max_buy_time = DateTimeFilter(
field_name="buy_time", lookup_expr="lte", label=_("Bought before (inclusive)")
)
uuid = UUIDFilter(field_name="uuid", lookup_expr="exact")
user_email = CharFilter(field_name="user__email", lookup_expr="iexact", label=_("User email"))
user = UUIDFilter(field_name="user__uuid", lookup_expr="exact", label=_("User UUID"))
user_email = CharFilter(
field_name="user__email", lookup_expr="iexact", label=_("User email")
)
user = UUIDFilter(
field_name="user__uuid", lookup_expr="exact", label=_("User UUID")
)
status = CharFilter(field_name="status", lookup_expr="icontains", label=_("Status"))
human_readable_id = CharFilter(field_name="human_readable_id", lookup_expr="exact", label=_("Human Readable ID"))
human_readable_id = CharFilter(
field_name="human_readable_id",
lookup_expr="exact",
label=_("Human Readable ID"),
)
order_by = OrderingFilter(
fields=(
@ -299,11 +327,20 @@ class OrderFilter(FilterSet):
class WishlistFilter(FilterSet):
uuid = UUIDFilter(field_name="uuid", lookup_expr="exact")
user_email = CharFilter(field_name="user__email", lookup_expr="iexact", label=_("User email"))
user = UUIDFilter(field_name="user__uuid", lookup_expr="exact", label=_("User UUID"))
user_email = CharFilter(
field_name="user__email", lookup_expr="iexact", label=_("User email")
)
user = UUIDFilter(
field_name="user__uuid", lookup_expr="exact", label=_("User UUID")
)
order_by = OrderingFilter(
fields=(("uuid", "uuid"), ("created", "created"), ("modified", "modified"), ("?", "random"))
fields=(
("uuid", "uuid"),
("created", "created"),
("modified", "modified"),
("?", "random"),
)
)
class Meta:
@ -317,7 +354,9 @@ class CategoryFilter(FilterSet):
parent_uuid = CharFilter(method="filter_parent_uuid", label=_("Parent"))
slug = CharFilter(field_name="slug", lookup_expr="exact", label=_("Slug"))
whole = BooleanFilter(
field_name="whole", label=_("Whole category(has at least 1 product or not)"), method="filter_whole_categories"
field_name="whole",
label=_("Whole category(has at least 1 product or not)"),
method="filter_whole_categories",
)
tags = CaseInsensitiveListFilter(field_name="tags__tag_name", label=_("Tags"))
level = NumberFilter(field_name="level", lookup_expr="exact", label=_("Level"))
@ -332,7 +371,16 @@ class CategoryFilter(FilterSet):
class Meta:
model = Category
fields = ["uuid", "name", "parent_uuid", "slug", "tags", "level", "order_by", "whole"]
fields = [
"uuid",
"name",
"parent_uuid",
"slug",
"tags",
"level",
"order_by",
"whole",
]
def filter_whole_categories(self, queryset, _name, value):
has_own_products = Exists(Product.objects.filter(category=OuterRef("pk")))
@ -364,7 +412,9 @@ class CategoryFilter(FilterSet):
class BrandFilter(FilterSet):
uuid = UUIDFilter(field_name="uuid", lookup_expr="exact")
name = CharFilter(field_name="name", lookup_expr="icontains", label=_("Name"))
categories = CaseInsensitiveListFilter(field_name="categories__uuid", lookup_expr="exact", label=_("Categories"))
categories = CaseInsensitiveListFilter(
field_name="categories__uuid", lookup_expr="exact", label=_("Categories")
)
order_by = OrderingFilter(
fields=(
@ -381,8 +431,16 @@ class BrandFilter(FilterSet):
class FeedbackFilter(FilterSet):
uuid = UUIDFilter(field_name="uuid", lookup_expr="exact", label=_("UUID"))
product_uuid = UUIDFilter(field_name="order_product__product__uuid", lookup_expr="exact", label=_("Product UUID"))
user_uuid = UUIDFilter(field_name="order_product__order__user__uuid", lookup_expr="exact", label=_("User UUID"))
product_uuid = UUIDFilter(
field_name="order_product__product__uuid",
lookup_expr="exact",
label=_("Product UUID"),
)
user_uuid = UUIDFilter(
field_name="order_product__order__user__uuid",
lookup_expr="exact",
label=_("User UUID"),
)
order_by = OrderingFilter(
fields=(
@ -402,8 +460,12 @@ class FeedbackFilter(FilterSet):
class AddressFilter(FilterSet):
uuid = UUIDFilter(field_name="uuid", lookup_expr="exact", label=_("UUID"))
user_uuid = UUIDFilter(field_name="user__uuid", lookup_expr="exact", label=_("User UUID"))
user_email = CharFilter(field_name="user__email", lookup_expr="iexact", label=_("User email"))
user_uuid = UUIDFilter(
field_name="user__uuid", lookup_expr="exact", label=_("User UUID")
)
user_email = CharFilter(
field_name="user__email", lookup_expr="iexact", label=_("User email")
)
order_by = OrderingFilter(
fields=(