feat(category): add brands relationship and resolve method

enable brands association with categories and allow querying of active brands within a category. Updated GraphQL schema, models, and serializers to include this relationship while deprecating redundant category-to-brand ManyToManyField.
This commit is contained in:
Egor Pavlovich Gorbunov 2026-02-21 18:27:10 +03:00
parent 10f5c798d4
commit 87ed875fe6
4 changed files with 19 additions and 6 deletions

View file

@ -421,10 +421,7 @@ class BrandAdmin(
"priority",
"is_active",
)
list_filter = (
"categories",
"is_active",
)
list_filter = ("is_active",)
search_fields = (
"uuid",
"name",

View file

@ -230,6 +230,7 @@ class CategoryType(DjangoObjectType):
"minimum and maximum prices for products in this category, if available."
),
)
brands = List(lambda: BrandType, description=_("brands in this category"))
tags = DjangoFilterConnectionField(
lambda: CategoryTagType, description=_("tags for this category")
)
@ -249,6 +250,7 @@ class CategoryType(DjangoObjectType):
"slug",
"description",
"image",
"brands",
"min_max_prices",
)
filter_fields = ["uuid"]
@ -294,6 +296,9 @@ class CategoryType(DjangoObjectType):
"max_price": min_max_prices["max_price"],
}
def resolve_brands(self: Category, info) -> QuerySet[Brand]:
return self.brands
def resolve_seo_meta(self: Category, info):
lang = graphene_current_lang()
base = f"https://{settings.BASE_DOMAIN}"

View file

@ -443,6 +443,14 @@ class Category(ExportModelOperationsMixin("category"), NiceModel, MPTTModel):
# Fallback to favicon.png from static files
return static("favicon.png")
@cached_property
def brands(self) -> QuerySet["Brand"]:
return Brand.objects.filter(
products__category=self,
products__is_active=True,
is_active=True,
).distinct()
class Meta:
verbose_name = _("category")
verbose_name_plural = _("categories")
@ -490,8 +498,8 @@ class Brand(ExportModelOperationsMixin("brand"), NiceModel):
categories = ManyToManyField(
"core.Category",
blank=True,
help_text=_("optional categories that this brand is associated with"),
verbose_name=_("associated categories"),
help_text=_("DEPRECATED"),
verbose_name=_("DEPRECATED"),
)
slug = AutoSlugField(
populate_from=("name",),

View file

@ -26,6 +26,7 @@ from engine.core.models import (
Wishlist,
)
from engine.core.serializers.simple import (
BrandSimpleSerializer,
CategorySimpleSerializer,
ProductSimpleSerializer,
)
@ -60,6 +61,7 @@ class CategoryDetailListSerializer(ListSerializer):
class CategoryDetailSerializer(ModelSerializer):
children = SerializerMethodField()
filterable_attributes = SerializerMethodField()
brands = BrandSimpleSerializer(many=True, read_only=True)
class Meta:
model = Category
@ -71,6 +73,7 @@ class CategoryDetailSerializer(ModelSerializer):
"image",
"markup_percent",
"filterable_attributes",
"brands",
"children",
"slug",
"created",