diff --git a/backend/product/admin.py b/backend/product/admin.py index 3cfe104..11359e1 100644 --- a/backend/product/admin.py +++ b/backend/product/admin.py @@ -10,12 +10,6 @@ from unfold.widgets import UnfoldAdminColorInputWidget from unfold.decorators import action, display from utils.admin import ModelAdmin -@admin.register(ProductVariant) -class ProductVariantAdmin(ModelAdmin, ImportExportModelAdmin): - import_form_class = ImportForm - export_form_class = ExportForm - autocomplete_fields = ['product_attributes', 'images', 'in_pack_items'] - warn_unsaved_form = True @@ -48,6 +42,15 @@ class InPackItemsAdmin(ModelAdmin, ImportExportModelAdmin): } +class AttributeValueInLine(StackedInline): + model = AttributeValue + extra = 0 + show_change_link = True + min_num = 1 + # autocomplete_fields = ['product_attributes', 'in_pack_items', 'images'] + # search_fields = [''] + + @admin.register(AttributeType) class AttributeTypeAdmin(ModelAdmin, ImportExportModelAdmin): import_form_class = ImportForm @@ -55,7 +58,7 @@ class AttributeTypeAdmin(ModelAdmin, ImportExportModelAdmin): search_fields = ['name'] compressed_fields = True warn_unsaved_form = True - + inlines = [AttributeValueInLine] formfield_overrides = { ArrayField: { "widget": ArrayWidget, @@ -98,20 +101,6 @@ class ProductImagesAdmin(ModelAdmin): -class ProductVariantInLine(StackedInline): - model = ProductVariant - extra = 0 - show_change_link = True - tab = True - min_num = 1 - autocomplete_fields = ['product_attributes', 'in_pack_items', 'images'] - # search_fields = [''] - - - def formfield_for_dbfield(self, db_field, request, **kwargs): - if db_field.name == 'color': - kwargs['widget'] = UnfoldAdminColorInputWidget() - return super().formfield_for_dbfield(db_field, request, **kwargs) @@ -129,24 +118,51 @@ class DetailModelAdmin(ModelAdmin, ImportExportModelAdmin): "widget": ArrayWidget, } } +@admin.register(ProductDetailModel) +class ProductDetailModel1Admin(ModelAdmin, ImportExportModelAdmin): + import_form_class = ImportForm + export_form_class = ExportForm + search_fields = ['detail_category__title'] + compressed_fields = True + warn_unsaved_form = True + + formfield_overrides = { + ArrayField: { + "widget": ArrayWidget, + } + } - -class DetailModelInLine(TabularInline): - model = ProductDetailModel +class ProductVariantInLine(StackedInline): + model = ProductVariant extra = 0 - fields = ['detail', 'detail_category'] show_change_link = True - autocomplete_fields = ['detail', 'detail_category'] - + tab = True + min_num = 1 + # inlines = [DetailModelInLine] + autocomplete_fields = ['product_attributes', 'in_pack_items', 'images', 'details'] + # search_fields = [''] + + + def formfield_for_dbfield(self, db_field, request, **kwargs): + if db_field.name == 'color': + kwargs['widget'] = UnfoldAdminColorInputWidget() + return super().formfield_for_dbfield(db_field, request, **kwargs) +@admin.register(ProductVariant) +class ProductVariantAdmin(ModelAdmin, ImportExportModelAdmin): + import_form_class = ImportForm + export_form_class = ExportForm + autocomplete_fields = ['product_attributes', 'images', 'in_pack_items', 'details'] + warn_unsaved_form = True + # inlines = [DetailModelInLine] @admin.register(ProductModel) class ProductModelAdmin(ModelAdmin, ImportExportModelAdmin): import_form_class = ImportForm export_form_class = ExportForm - inlines = [ProductVariantInLine, DetailModelInLine] + inlines = [ProductVariantInLine] readonly_fields = ('slug', ) search_fields = ['name', 'description', ] list_filter = ['show', 'category'] diff --git a/backend/product/migrations/0031_alter_productdetailmodel_product.py b/backend/product/migrations/0031_alter_productdetailmodel_product.py new file mode 100644 index 0000000..55fb8cd --- /dev/null +++ b/backend/product/migrations/0031_alter_productdetailmodel_product.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.2 on 2025-02-18 19:43 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0030_rename_attributes_productvariant_product_attributes_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='productdetailmodel', + name='product', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='details', to='product.productvariant', verbose_name='محصول مرتبط'), + ), + ] diff --git a/backend/product/migrations/0032_alter_productdetailmodel_unique_together_and_more.py b/backend/product/migrations/0032_alter_productdetailmodel_unique_together_and_more.py new file mode 100644 index 0000000..824bc49 --- /dev/null +++ b/backend/product/migrations/0032_alter_productdetailmodel_unique_together_and_more.py @@ -0,0 +1,26 @@ +# Generated by Django 5.1.2 on 2025-02-18 20:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0031_alter_productdetailmodel_product'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='productdetailmodel', + unique_together=set(), + ), + migrations.AddField( + model_name='productvariant', + name='details', + field=models.ManyToManyField(related_name='product', to='product.productdetailmodel', verbose_name='محصول مرتبط'), + ), + migrations.RemoveField( + model_name='productdetailmodel', + name='product', + ), + ] diff --git a/backend/product/migrations/0033_alter_productvariant_details.py b/backend/product/migrations/0033_alter_productvariant_details.py new file mode 100644 index 0000000..3080f15 --- /dev/null +++ b/backend/product/migrations/0033_alter_productvariant_details.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.2 on 2025-02-18 20:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0032_alter_productdetailmodel_unique_together_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='productvariant', + name='details', + field=models.ManyToManyField(related_name='product', to='product.productdetailmodel', verbose_name='جزيیات محصول'), + ), + ] diff --git a/backend/product/models.py b/backend/product/models.py index 1cf00fa..de8aca0 100644 --- a/backend/product/models.py +++ b/backend/product/models.py @@ -152,17 +152,6 @@ class ProductDetailCategory(models.Model): verbose_name = 'دسته بندی جزيات' verbose_name_plural = 'دسته بندی های جزيیات' -class ProductDetailModel(models.Model): - product = models.ForeignKey(ProductModel, on_delete=models.CASCADE, verbose_name='محصول مرتبط', related_name='details') - detail_category = models.ForeignKey(ProductDetailCategory, on_delete=models.CASCADE, verbose_name='دسته بندی جزيات', blank=True, null=True) - detail = models.ManyToManyField(DetailModel, verbose_name='جزيات ها') - - class Meta: - verbose_name = 'جزیات محصول' - verbose_name_plural = 'جزیات محصول ها' - unique_together = ('product', 'detail_category') - def __str__(self): - return f'جزيیات محصول {self.product}' @@ -216,6 +205,15 @@ class ProductImageModel(models.Model): verbose_name_plural = 'عکس های محصولات' +class ProductDetailModel(models.Model): + detail_category = models.ForeignKey(ProductDetailCategory, on_delete=models.CASCADE, verbose_name='دسته بندی جزيات', blank=True, null=True) + detail = models.ManyToManyField(DetailModel, verbose_name='جزيات ها') + + class Meta: + verbose_name = 'جزیات محصول' + verbose_name_plural = 'جزیات محصول ها' + # def __str__(self): + # return f'جزيیات محصول {self.product}' class ProductVariant(models.Model): @@ -237,6 +235,7 @@ class ProductVariant(models.Model): color = models.CharField(verbose_name='رنک', max_length=7, blank=True, null=True) images = models.ManyToManyField(ProductImageModel, verbose_name='عکس ها') video = models.FileField(upload_to='product_videos/', blank=True, null=True, verbose_name='ویدیو') + details = models.ManyToManyField(ProductDetailModel, verbose_name='جزيیات محصول', related_name='product') class Meta: verbose_name = 'تنوع محصول' verbose_name_plural = 'تنوع‌های محصول' @@ -262,4 +261,4 @@ class ProductVariant(models.Model): return toman_price def get_toman_price_after_discount(self): - return self.get_toman_price() * ((100 - self.discount) / 100) \ No newline at end of file + return self.get_toman_price() * ((100 - self.discount) / 100) diff --git a/backend/product/serializers.py b/backend/product/serializers.py index 3794822..d1e011a 100644 --- a/backend/product/serializers.py +++ b/backend/product/serializers.py @@ -48,6 +48,7 @@ class ProductVariantSerialzier(serializers.ModelSerializer): price = serializers.SerializerMethodField() in_pack_items = InPackItemsSerialzier(many=True) images = ProductImageSerailizer(many=True) + details = ProductDetailSerializer(many=True, read_only=True) class Meta: model = ProductVariant exclude = ('min_price', 'max_price','sell', 'currency', 'product') @@ -73,7 +74,6 @@ class DynamicProductSerializer(serializers.ModelSerializer): # variants_colors = serializers.SerializerMethodField() is_new = serializers.SerializerMethodField() related_products = serializers.SerializerMethodField() - details = ProductDetailSerializer(many=True, read_only=True) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -92,7 +92,7 @@ class DynamicProductSerializer(serializers.ModelSerializer): fields = "__all__" view_type = { 'list': ['name', 'rating', 'slug', 'category', 'variants'], - 'instance': ['name', 'description', 'rating', 'slug', 'meta_description', 'meta_keywords', 'meta_rating', 'category', 'related_products', 'details', 'in_pack_items', 'variants'], + 'instance': ['name', 'description', 'rating', 'slug', 'meta_description', 'meta_keywords', 'meta_rating', 'category', 'related_products', 'in_pack_items', 'variants'], 'chat': ['name', 'description', 'variants'] }