diff --git a/backend/core/settings/unfold_conf.py b/backend/core/settings/unfold_conf.py index 15b4dbf..3829318 100644 --- a/backend/core/settings/unfold_conf.py +++ b/backend/core/settings/unfold_conf.py @@ -222,6 +222,30 @@ UNFOLD = { ], }, + { + "title": _("بخش تنوع محصول"), + "separator": True, + "collapsible": True, + "items": [ + + { + "title": _("نوع صفت"), + "icon": "format_list_bulleted_add", + "link": reverse_lazy("admin:product_attributetype_changelist"), + }, + { + "title": _("مقدار صفت"), + "icon": "variable_add", + "link": reverse_lazy("admin:product_attributevalue_changelist"), + }, + { + "title": _("تنوع محصول"), + "icon": "photo_prints", + "link": reverse_lazy("admin:product_productvariant_changelist"), + }, + + ], + }, ], diff --git a/backend/home/migrations/0005_alter_showcaseslider_description_and_more.py b/backend/home/migrations/0005_alter_showcaseslider_description_and_more.py new file mode 100644 index 0000000..fa50ae4 --- /dev/null +++ b/backend/home/migrations/0005_alter_showcaseslider_description_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 5.1.2 on 2025-02-08 17:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0004_showcaseslider'), + ] + + operations = [ + migrations.AlterField( + model_name='showcaseslider', + name='description', + field=models.CharField(max_length=150, verbose_name='عنوان'), + ), + migrations.AlterField( + model_name='showcaseslider', + name='image', + field=models.ImageField(upload_to='show_case/', verbose_name='عکس'), + ), + migrations.AlterField( + model_name='showcaseslider', + name='link', + field=models.URLField(verbose_name='لینک'), + ), + migrations.AlterField( + model_name='showcaseslider', + name='title', + field=models.CharField(max_length=30, verbose_name='عنوان'), + ), + ] diff --git a/backend/product/admin.py b/backend/product/admin.py index e795ad0..96aaceb 100644 --- a/backend/product/admin.py +++ b/backend/product/admin.py @@ -11,6 +11,15 @@ from unfold.widgets import ( ) from unfold.decorators import action, display +@admin.register(ProductVariant) +class ProductVariantAdmin(ModelAdmin, ImportExportModelAdmin): + import_form_class = ImportForm + export_form_class = ExportForm + autocomplete_fields = ['attributes'] + warn_unsaved_form = True + + + @admin.register(ProductDetailCategory) class ProductDetailCategoryAdmin(ModelAdmin, ImportExportModelAdmin): import_form_class = ImportForm @@ -156,20 +165,22 @@ class ProductModelAdmin(ModelAdmin, ImportExportModelAdmin): @display(description='محصول', header=True) def display_image(self, instance): - if instance.image1: - return [ - instance.name, - None, - None, - { - "path": instance.image1.url, - "height": 30, - "width": 30, - "borderless": True, - # "squared": True, - }, - ] - return ('خالی',) + if instance and instance.variants.first() and instance.variants.first().attributes.first(): + image = instance.variants.first().attributes.first().image.url if instance.variants.first().attributes.first().image else None + else: + image = None + return [ + instance.name, + None, + None, + { + "path": image, + "height": 30, + "width": 30, + "borderless": True, + # "squared": True, + }, + ] # @display( # description=("نمایش در صفحه ی اصلی"), # label={ diff --git a/backend/product/migrations/0019_remove_productmodel_image1_and_more.py b/backend/product/migrations/0019_remove_productmodel_image1_and_more.py new file mode 100644 index 0000000..8b8e74f --- /dev/null +++ b/backend/product/migrations/0019_remove_productmodel_image1_and_more.py @@ -0,0 +1,40 @@ +# Generated by Django 5.1.2 on 2025-02-08 17:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0018_alter_commentmodel_review_status'), + ] + + operations = [ + migrations.RemoveField( + model_name='productmodel', + name='image1', + ), + migrations.RemoveField( + model_name='productmodel', + name='image2', + ), + migrations.RemoveField( + model_name='productmodel', + name='image3', + ), + migrations.RemoveField( + model_name='productmodel', + name='video', + ), + migrations.AddField( + model_name='attributevalue', + name='image', + field=models.ImageField(default='', upload_to='product_images/'), + preserve_default=False, + ), + migrations.AddField( + model_name='attributevalue', + name='video', + field=models.FileField(blank=True, null=True, upload_to='product_videos/', verbose_name='ویدیو'), + ), + ] diff --git a/backend/product/migrations/0020_productvariant_max_price_and_more.py b/backend/product/migrations/0020_productvariant_max_price_and_more.py new file mode 100644 index 0000000..4c85c4f --- /dev/null +++ b/backend/product/migrations/0020_productvariant_max_price_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.2 on 2025-02-08 19:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0019_remove_productmodel_image1_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='productvariant', + name='max_price', + field=models.PositiveIntegerField(default=0, help_text='این قیمت برای سقف قیمتی محصول در نظر گرفته میشود', verbose_name='قیمت سقف'), + preserve_default=False, + ), + migrations.AlterField( + model_name='productvariant', + name='attributes', + field=models.ManyToManyField(related_name='variant', to='product.attributevalue', verbose_name='ویژگی\u200cها'), + ), + ] diff --git a/backend/product/models.py b/backend/product/models.py index 12b3db9..b148823 100644 --- a/backend/product/models.py +++ b/backend/product/models.py @@ -100,10 +100,6 @@ class InPackItems(models.Model): class ProductModel(models.Model): name = models.CharField(max_length=255, verbose_name='نام') description = models.TextField(verbose_name='توضیحات') - image1 = models.ImageField(upload_to='product_images/', verbose_name='عکس اول') - image2 = models.ImageField(upload_to='product_images/', blank=True, null=True, verbose_name='عکس دوم') - image3 = models.ImageField(upload_to='product_images/', blank=True, null=True, verbose_name='عکس سوم') - video = models.FileField(upload_to='product_videos/', blank=True, null=True, verbose_name='ویدیو') rating = models.PositiveIntegerField(default=0, verbose_name='امتیاز') show = models.BooleanField(default=False, verbose_name='نمایش در خانه') view = models.IntegerField(default=0, verbose_name='بازدید') @@ -203,7 +199,8 @@ class AttributeValue(models.Model): attribute_type = models.ForeignKey(AttributeType, on_delete=models.CASCADE, blank=True, null=True) value = models.CharField(verbose_name='مقدار نوع اتربیوت', max_length=100, blank=True, null=True) color = models.CharField(verbose_name='رنک', max_length=7, blank=True, null=True) - + image = models.ImageField(upload_to='product_images/') + video = models.FileField(upload_to='product_videos/', blank=True, null=True, verbose_name='ویدیو') class Meta: unique_together = ('attribute_type', 'value') @@ -212,10 +209,11 @@ class AttributeValue(models.Model): class ProductVariant(models.Model): product = models.ForeignKey(ProductModel, on_delete=models.CASCADE, related_name='variants', verbose_name='محصول') - attributes = models.ManyToManyField(AttributeValue, verbose_name='ویژگی‌ها') + attributes = models.ManyToManyField(AttributeValue, verbose_name='ویژگی‌ها', related_name='variant') in_stock = models.PositiveIntegerField(default=0, verbose_name='تعداد موجود') price = models.PositiveIntegerField(default=0, verbose_name='قیمت') min_price = models.PositiveIntegerField(verbose_name='قیمت کف', help_text='این قیمت برای کف قیمتی محصول در نظر گرفته میشود') + max_price = models.PositiveIntegerField(verbose_name='قیمت سقف', help_text='این قیمت برای سقف قیمتی محصول در نظر گرفته میشود') currency_type = ( ('dollor', 'دلار'), ('toman', 'تومان'), diff --git a/backend/product/serializers.py b/backend/product/serializers.py index f94fcdb..11fc5fc 100644 --- a/backend/product/serializers.py +++ b/backend/product/serializers.py @@ -35,7 +35,7 @@ class ProductVariantSerialzier(serializers.ModelSerializer): price = serializers.SerializerMethodField() class Meta: model = ProductVariant - fields = "__all__" + exclude = ('min_price', 'sell', 'currency', 'product') def get_price(self, obj): dollor_price = self.context.get('dollor_price') @@ -43,8 +43,14 @@ class ProductVariantSerialzier(serializers.ModelSerializer): return "{:,.0f} تومان".format(toman_price) + + + + + class DynamicProductSerializer(serializers.ModelSerializer): variants = ProductVariantSerialzier(many=True) + # variants_colors = serializers.SerializerMethodField() is_new = serializers.SerializerMethodField() related_products = serializers.SerializerMethodField() details = ProductDetailSerializer(many=True, read_only=True) @@ -64,12 +70,18 @@ class DynamicProductSerializer(serializers.ModelSerializer): model = ProductModel fields = "__all__" view_type = { - 'list': ['name', 'image1', 'video', 'rating', 'slug', 'category', 'variants'], - 'instance': ['name', 'description', 'image1', 'image2', 'image3', 'video', 'rating', 'slug', 'meta_description', 'meta_keywords', 'meta_rating', 'category', 'related_products', 'details', 'in_pack_items', 'variants'], - 'chat': ['name', 'description', ] + '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'], + 'chat': ['name', 'description', 'variants'] } + # def get_variants_colors(self, obj): + # varients = obj.variants.all() + # attributes = AttributeValue.objects.filter(variant__in=varients) + # return AttributeValueForProductListSerialzier(instance=attributes, many=True, context=self.context).data + + def get_is_new(self, obj): return timezone.now() < obj.created_at + timedelta(days=7)