diff --git a/engine/core/admin.py b/engine/core/admin.py index 94df948e..d6f06de4 100644 --- a/engine/core/admin.py +++ b/engine/core/admin.py @@ -421,10 +421,7 @@ class BrandAdmin( "priority", "is_active", ) - list_filter = ( - "categories", - "is_active", - ) + list_filter = ("is_active",) search_fields = ( "uuid", "name", diff --git a/engine/core/graphene/object_types.py b/engine/core/graphene/object_types.py index 44ffffc3..bc2b323a 100644 --- a/engine/core/graphene/object_types.py +++ b/engine/core/graphene/object_types.py @@ -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}" diff --git a/engine/core/models.py b/engine/core/models.py index 55218d23..5e0b9e3d 100644 --- a/engine/core/models.py +++ b/engine/core/models.py @@ -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",), diff --git a/engine/core/serializers/detail.py b/engine/core/serializers/detail.py index ee3878a0..535704ac 100644 --- a/engine/core/serializers/detail.py +++ b/engine/core/serializers/detail.py @@ -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",