unit category added

This commit is contained in:
Parsa Nazer
2025-12-04 13:37:55 +03:30
parent fd6aad8be5
commit 68368fc531
8 changed files with 161 additions and 3 deletions
+6
View File
@@ -182,6 +182,12 @@ UNFOLD = {
"collapsible": True, "collapsible": True,
"items": [ "items": [
{
"title": _("دسته بندی واحد"),
"icon": "category",
"link": reverse_lazy("admin:product_unitcategorymodel_changelist"),
"permission": lambda request: request.user.is_superuser,
},
{ {
"title": _("دسته بندی"), "title": _("دسته بندی"),
"icon": "category", "icon": "category",
+6
View File
@@ -30,6 +30,12 @@ class ProductDetailCategoryAdmin(ModelAdmin, ImportExportModelAdmin):
return request.user.is_superuser return request.user.is_superuser
@admin.register(UnitCategoryModel)
class UnitCategoryAdmin(ModelAdmin):
pass
@admin.register(InPackItems) @admin.register(InPackItems)
class InPackItemsAdmin(ModelAdmin, ImportExportModelAdmin): class InPackItemsAdmin(ModelAdmin, ImportExportModelAdmin):
import_form_class = ImportForm import_form_class = ImportForm
@@ -0,0 +1,44 @@
# Generated by Django 5.1.2 on 2025-12-04 09:48
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('product', '0057_productvariant_profit_and_more'),
]
operations = [
migrations.CreateModel(
name='UnitCategoryModel',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50, verbose_name='نام دسته بندی')),
('slug', models.SlugField(help_text='اسم دسته را برای مسیر به انگلیسی و بدون فاصله وارد کنید', unique=True)),
('icon', models.ImageField(blank=True, null=True, upload_to='category_model/', verbose_name='آیکون')),
('image', models.ImageField(blank=True, null=True, upload_to='category_model/', verbose_name='عکس')),
('meta_title', models.CharField(blank=True, help_text='عنوان متا برای SEO', max_length=60, null=True, verbose_name='عنوان متا')),
('meta_description', models.TextField(blank=True, help_text='توضیحات متا برای SEO', max_length=160, null=True, verbose_name='توضیحات متا')),
],
options={
'verbose_name': 'دسته\u200cبندی اصلی',
'verbose_name_plural': 'دسته\u200cبندی\u200cهااصلی',
},
),
migrations.RenameIndex(
model_name='maincategorymodel',
new_name='unit_category_slug_idx',
old_name='main_category_slug_idx',
),
migrations.AddIndex(
model_name='unitcategorymodel',
index=models.Index(fields=['slug'], name='main_category_slug_idx'),
),
migrations.AddField(
model_name='maincategorymodel',
name='parent',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='unitcategorys', to='product.unitcategorymodel', verbose_name='دسته\u200cبندی والد'),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 5.1.2 on 2025-12-04 09:56
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('product', '0058_unitcategorymodel_and_more'),
]
operations = [
migrations.AlterField(
model_name='maincategorymodel',
name='parent',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='maincategorys', to='product.unitcategorymodel', verbose_name='دسته\u200cبندی والد'),
),
]
+35 -2
View File
@@ -8,6 +8,38 @@ from django.core.exceptions import ValidationError
from home.models import ShowCaseSlider from home.models import ShowCaseSlider
class UnitCategoryModel(models.Model):
name = models.CharField(max_length=50, verbose_name='نام دسته بندی')
slug = models.SlugField(max_length=50, unique=True,
help_text="اسم دسته را برای مسیر به انگلیسی و بدون فاصله وارد کنید")
icon = models.ImageField(upload_to='category_model/',
verbose_name='آیکون', blank=True, null=True)
image = models.ImageField(
upload_to='category_model/', verbose_name='عکس', blank=True, null=True)
meta_title = models.CharField(
max_length=60, verbose_name="عنوان متا", help_text="عنوان متا برای SEO", blank=True, null=True)
meta_description = models.TextField(
max_length=160, verbose_name="توضیحات متا", help_text="توضیحات متا برای SEO", blank=True, null=True)
class Meta:
verbose_name = "دسته‌بندی اصلی"
verbose_name_plural = "دسته‌بندی‌هااصلی"
indexes = [
models.Index(fields=['slug'], name='main_category_slug_idx'),
]
def __str__(self):
return self.name
def save(self, *args, **kwargs):
if not self.slug:
self.slug = 'unit-category-' + slugify(self.name, allow_unicode=True)
super().save(*args, **kwargs)
class MainCategoryModel(models.Model): class MainCategoryModel(models.Model):
name = models.CharField(max_length=50, verbose_name='نام دسته بندی') name = models.CharField(max_length=50, verbose_name='نام دسته بندی')
slug = models.SlugField(max_length=50, unique=True, slug = models.SlugField(max_length=50, unique=True,
@@ -22,12 +54,13 @@ class MainCategoryModel(models.Model):
max_length=160, verbose_name="توضیحات متا", help_text="توضیحات متا برای SEO", blank=True, null=True) max_length=160, verbose_name="توضیحات متا", help_text="توضیحات متا برای SEO", blank=True, null=True)
video = models.FileField(upload_to='category_videos/', video = models.FileField(upload_to='category_videos/',
blank=True, null=True, verbose_name='ویدیو') blank=True, null=True, verbose_name='ویدیو')
parent = models.ForeignKey(UnitCategoryModel, on_delete=models.CASCADE,
related_name='maincategorys', verbose_name='دسته‌بندی والد', null=True)
class Meta: class Meta:
verbose_name = "دسته‌بندی اصلی" verbose_name = "دسته‌بندی اصلی"
verbose_name_plural = "دسته‌بندی‌هااصلی" verbose_name_plural = "دسته‌بندی‌هااصلی"
indexes = [ indexes = [
models.Index(fields=['slug'], name='main_category_slug_idx'), models.Index(fields=['slug'], name='unit_category_slug_idx'),
] ]
def __str__(self): def __str__(self):
+7
View File
@@ -110,6 +110,13 @@ class SubCategorySerializer(serializers.ModelSerializer):
return obj.parent.name return obj.parent.name
class UnitCategorySerializer(serializers.ModelSerializer):
class Meta:
model = UnitCategoryModel
fields = ['id', 'name', 'slug', 'icon', 'meta_title',
'meta_description', 'image']
class MainCategorySerializer(serializers.ModelSerializer): class MainCategorySerializer(serializers.ModelSerializer):
subcategorys = SubCategorySerializer(many=True) subcategorys = SubCategorySerializer(many=True)
+2 -1
View File
@@ -1,11 +1,12 @@
from django.urls import path, re_path from django.urls import path, re_path
from .views import AllCategories, ProductView, AllProductsView, CommentView, ShowCaseProductsView, ShowCaseCategoryListView, BotProductsView,BotProductDetailView,BotCategoryView from .views import AllCategories, ProductView, AllProductsView, CommentView, ShowCaseProductsView, ShowCaseCategoryListView, BotProductsView,BotProductDetailView,BotCategoryView ,AllCategoriesV2
urlpatterns = [ urlpatterns = [
path('slider_category', ShowCaseProductsView.as_view(), name='category-products'), path('slider_category', ShowCaseProductsView.as_view(), name='category-products'),
path('bot', BotProductsView.as_view(), name='bot-products'), path('bot', BotProductsView.as_view(), name='bot-products'),
path('bot/<int:pk>/', BotProductDetailView.as_view(), name='bot-product-detail'), path('bot/<int:pk>/', BotProductDetailView.as_view(), name='bot-product-detail'),
path('categories', AllCategories.as_view(), name='all-categories'), path('categories', AllCategories.as_view(), name='all-categories'),
path('categories/v2', AllCategoriesV2.as_view(), name='all-categories'),
path('categories/bot', BotCategoryView.as_view(), name='bot-categories'), path('categories/bot', BotCategoryView.as_view(), name='bot-categories'),
path('slider_categories', ShowCaseCategoryListView.as_view(), name='all-categories'), path('slider_categories', ShowCaseCategoryListView.as_view(), name='all-categories'),
re_path(r'^comments/(?P<slug>[\w\u0600-\u06FF\-]+)$', CommentView.as_view(), name='comment-views'), re_path(r'^comments/(?P<slug>[\w\u0600-\u06FF\-]+)$', CommentView.as_view(), name='comment-views'),
+42
View File
@@ -65,6 +65,48 @@ class AllCategories(APIView):
categories_ser = self.serializer_class( categories_ser = self.serializer_class(
instance=categories, many=True, context={'request': request}) instance=categories, many=True, context={'request': request})
return Response(categories_ser.data, status=status.HTTP_200_OK) return Response(categories_ser.data, status=status.HTTP_200_OK)
class UnitCategorySerializerV2(serializers.ModelSerializer):
maincategorys = serializers.SerializerMethodField()
class Meta:
model = UnitCategoryModel
fields = ['id', 'name', 'slug', 'icon', 'meta_title',
'meta_description', 'image', 'maincategorys']
def get_maincategorys(self, obj):
main_categories = obj.maincategorys.all()
return MainCategorySerializer(main_categories, many=True, context=self.context).data
class AllCategoriesV2(APIView):
serializer_class = UnitCategorySerializerV2
authentication_classes = []
@extend_schema(
responses={
200: UnitCategorySerializerV2(many=True),
404: OpenApiTypes.OBJECT,
},
)
def get(self, request):
# Optimize query with prefetch_related to avoid N+1 queries
unit_categories = UnitCategoryModel.objects.prefetch_related(
Prefetch(
'maincategorys',
queryset=MainCategoryModel.objects.prefetch_related(
Prefetch(
'subcategorys',
queryset=SubCategoryModel.objects.annotate(
product_count=Count('products')
)
)
)
)
).all()
categories_ser = self.serializer_class(
instance=unit_categories, many=True, context={'request': request})
return Response(categories_ser.data, status=status.HTTP_200_OK)
class ProductView(APIView): class ProductView(APIView):