diff --git a/backend/account/admin.py b/backend/account/admin.py index 588da46..3e81445 100644 --- a/backend/account/admin.py +++ b/backend/account/admin.py @@ -28,7 +28,7 @@ class UserAdmin(BaseUserAdmin, ModelAdmin, ImportExportModelAdmin): list_filter = ['is_superuser'] search_fields = ['phone', 'first_name', 'last_name', 'email'] list_display = ['full_name_display', 'phone', 'email', 'is_superuser', ] - readonly_fields = [] + # readonly_fields = ['phone', 'email', 'otp_expiry', 'otp_hash', 'date_joined', 'profile_photo'] exclude = ('otp_hash', 'otp_expiry', 'is_active', 'is_staff', 'password', 'last_login') import_form_class = ImportForm @@ -73,6 +73,7 @@ class AddressAdmin(ModelAdmin, ImportExportModelAdmin): export_form_class = ExportForm search_fields = ['address', 'name', 'city', 'province'] list_display = ['user', 'name', 'address_display', 'postal_code', 'city', 'province', 'for_me'] + #readonly_fields = ['user', 'name', 'address', 'postal_code', 'phone', 'city', 'province', 'for_me'] compressed_fields = True warn_unsaved_form = True formfield_overrides = { @@ -84,5 +85,5 @@ class AddressAdmin(ModelAdmin, ImportExportModelAdmin): } } def address_display(self, obj): - return obj.address[0:20] + return obj.address[0:35] + '...' address_display.short_description = 'ادرس' \ No newline at end of file diff --git a/backend/account/migrations/0005_alter_useraddressmodel_options_alter_user_is_active_and_more.py b/backend/account/migrations/0005_alter_useraddressmodel_options_alter_user_is_active_and_more.py new file mode 100644 index 0000000..47fbb53 --- /dev/null +++ b/backend/account/migrations/0005_alter_useraddressmodel_options_alter_user_is_active_and_more.py @@ -0,0 +1,69 @@ +# Generated by Django 5.1.2 on 2025-02-04 19:55 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0004_useraddressmodel_for_me'), + ] + + operations = [ + migrations.AlterModelOptions( + name='useraddressmodel', + options={'verbose_name': 'ادرس کاربر', 'verbose_name_plural': 'ادرس های کاربر'}, + ), + migrations.AlterField( + model_name='user', + name='is_active', + field=models.BooleanField(default=True, verbose_name='فعال بودن کاربر'), + ), + migrations.AlterField( + model_name='user', + name='is_staff', + field=models.BooleanField(default=False, verbose_name='کارمند'), + ), + migrations.AlterField( + model_name='useraddressmodel', + name='address', + field=models.TextField(verbose_name='ادرس کامل'), + ), + migrations.AlterField( + model_name='useraddressmodel', + name='city', + field=models.CharField(max_length=30, verbose_name='شهر'), + ), + migrations.AlterField( + model_name='useraddressmodel', + name='for_me', + field=models.BooleanField(default=False, verbose_name='برای خود کاربر'), + ), + migrations.AlterField( + model_name='useraddressmodel', + name='name', + field=models.CharField(max_length=30, verbose_name='نام ادرس'), + ), + migrations.AlterField( + model_name='useraddressmodel', + name='phone', + field=models.CharField(help_text='شماره تماس کاربر و شماره ی ارسالی میتواند متفاوت باشد', max_length=11, verbose_name='شماره تماس برای ارسال'), + ), + migrations.AlterField( + model_name='useraddressmodel', + name='postal_code', + field=models.CharField(max_length=10, verbose_name='کد پستی'), + ), + migrations.AlterField( + model_name='useraddressmodel', + name='province', + field=models.CharField(max_length=30, verbose_name='استان'), + ), + migrations.AlterField( + model_name='useraddressmodel', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='address', to=settings.AUTH_USER_MODEL, verbose_name='کاربر'), + ), + ] diff --git a/backend/blog/migrations/0003_alter_blogmodel_author_alter_blogmodel_category_and_more.py b/backend/blog/migrations/0003_alter_blogmodel_author_alter_blogmodel_category_and_more.py new file mode 100644 index 0000000..013aa64 --- /dev/null +++ b/backend/blog/migrations/0003_alter_blogmodel_author_alter_blogmodel_category_and_more.py @@ -0,0 +1,78 @@ +# Generated by Django 5.1.2 on 2025-02-04 19:55 + +import django.db.models.deletion +import django.utils.timezone +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('blog', '0002_alter_blogmodel_options_alter_blogmodel_cover_image'), + ('product', '0013_detailmodel_alter_instuckcolors_product_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name='blogmodel', + name='author', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='blogs', to=settings.AUTH_USER_MODEL, verbose_name='نویسنده'), + ), + migrations.AlterField( + model_name='blogmodel', + name='category', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='blogs', to='product.subcategorymodel', verbose_name='دسته بندی'), + ), + migrations.AlterField( + model_name='blogmodel', + name='content', + field=models.TextField(verbose_name='محتوا'), + ), + migrations.AlterField( + model_name='blogmodel', + name='cover_image', + field=models.ImageField(blank=True, upload_to='blog_covers/', verbose_name='کاور بلاگ'), + ), + migrations.AlterField( + model_name='blogmodel', + name='created_at', + field=models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='ساخته شده در'), + ), + migrations.AlterField( + model_name='blogmodel', + name='is_published', + field=models.BooleanField(default=False, verbose_name='انتشار در وبسایت'), + ), + migrations.AlterField( + model_name='blogmodel', + name='meta_description', + field=models.CharField(help_text='این فیلد را حتما پر کنید', max_length=300, verbose_name='متا دیسکریپشن'), + ), + migrations.AlterField( + model_name='blogmodel', + name='meta_keywords', + field=models.CharField(help_text='این فیلد را حتما پر کنید', max_length=300, verbose_name='متا کیورد'), + ), + migrations.AlterField( + model_name='blogmodel', + name='summery', + field=models.TextField(verbose_name='خلاصه'), + ), + migrations.AlterField( + model_name='blogmodel', + name='title', + field=models.CharField(max_length=200, verbose_name='عنوان'), + ), + migrations.AlterField( + model_name='blogmodel', + name='updated_at', + field=models.DateTimeField(auto_now=True, verbose_name='ابدیت شده در'), + ), + migrations.AlterField( + model_name='blogmodel', + name='views', + field=models.PositiveIntegerField(default=0, verbose_name='بازدید'), + ), + ] diff --git a/backend/chat/migrations/0003_alter_productchatmodel_product_and_more.py b/backend/chat/migrations/0003_alter_productchatmodel_product_and_more.py new file mode 100644 index 0000000..0ba4f55 --- /dev/null +++ b/backend/chat/migrations/0003_alter_productchatmodel_product_and_more.py @@ -0,0 +1,32 @@ +# Generated by Django 5.1.2 on 2025-02-04 19:55 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('chat', '0002_alter_productchatmodel_options'), + ('product', '0013_detailmodel_alter_instuckcolors_product_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name='productchatmodel', + name='product', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='product.productmodel', verbose_name='محصول'), + ), + migrations.AlterField( + model_name='productchatmodel', + name='thread', + field=models.CharField(blank=True, max_length=400, null=True, verbose_name='ترد هوش مصنوعی'), + ), + migrations.AlterField( + model_name='productchatmodel', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='کاربر'), + ), + ] diff --git a/backend/core/settings/unfold_conf.py b/backend/core/settings/unfold_conf.py index a8693a0..e7105d4 100644 --- a/backend/core/settings/unfold_conf.py +++ b/backend/core/settings/unfold_conf.py @@ -119,6 +119,7 @@ UNFOLD = { "title": _("نظرات"), "icon": "chat", "link": reverse_lazy("admin:product_commentmodel_changelist"), + "badge": "utils.admin.comment_count", }, { "title": _("قیمت دلار"), @@ -210,6 +211,7 @@ UNFOLD = { "title": _("تیکت"), "icon": "confirmation_number", "link": reverse_lazy("admin:ticket_ticket_changelist"), + "badge": "utils.admin.new_ticket_count", }, ], diff --git a/backend/core/views.py b/backend/core/views.py index 212a5bc..0a361be 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -8,16 +8,19 @@ from django.utils.translation import gettext_lazy as _ from django.views.generic import RedirectView, TemplateView from unfold.views import UnfoldModelAdminViewMixin from order.models import OrderModel -class HomeView(RedirectView): - pattern_name = "admin:index" +from ticket.models import Ticket + + def dashboard_callback(request, context): pending_count = OrderModel.objects.filter(status='ADMIN_PENDING').count() + open_tickets_count = Ticket.objects.filter(status__in=['open', 'in_progress']).count() context.update(random_data()) context.update({'pending_count': pending_count}) + context.update({'open_tickets_count': open_tickets_count}) return context diff --git a/backend/home/migrations/0003_alter_homeimagemodel_description1_and_more.py b/backend/home/migrations/0003_alter_homeimagemodel_description1_and_more.py new file mode 100644 index 0000000..62d9b4e --- /dev/null +++ b/backend/home/migrations/0003_alter_homeimagemodel_description1_and_more.py @@ -0,0 +1,58 @@ +# Generated by Django 5.1.2 on 2025-02-04 19:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0002_alter_homeimagemodel_options'), + ] + + operations = [ + migrations.AlterField( + model_name='homeimagemodel', + name='description1', + field=models.TextField(verbose_name='توضیحات عکس اول'), + ), + migrations.AlterField( + model_name='homeimagemodel', + name='description2', + field=models.TextField(verbose_name='توضیحات عکس دوم'), + ), + migrations.AlterField( + model_name='homeimagemodel', + name='image1', + field=models.ImageField(upload_to='diff_image/', verbose_name='عکس اول'), + ), + migrations.AlterField( + model_name='homeimagemodel', + name='image2', + field=models.ImageField(upload_to='diff_image/', verbose_name='عکس دوم'), + ), + migrations.AlterField( + model_name='homeimagemodel', + name='link1', + field=models.URLField(verbose_name='لینک عکس اول'), + ), + migrations.AlterField( + model_name='homeimagemodel', + name='link2', + field=models.URLField(verbose_name='لینک عکس دوم'), + ), + migrations.AlterField( + model_name='homeimagemodel', + name='title1', + field=models.CharField(max_length=50, verbose_name='عنوان عکس اول'), + ), + migrations.AlterField( + model_name='homeimagemodel', + name='title2', + field=models.CharField(max_length=50, verbose_name='عنوان عکس دوم'), + ), + migrations.AlterField( + model_name='homeimagemodel', + name='unique_filed', + field=models.CharField(choices=[('unique', 'unique')], default='unique', max_length=20, unique=True, verbose_name='یونیک فیلد'), + ), + ] diff --git a/backend/order/migrations/0002_alter_discountcode_expiration_date_and_more.py b/backend/order/migrations/0002_alter_discountcode_expiration_date_and_more.py new file mode 100644 index 0000000..ca1dd0f --- /dev/null +++ b/backend/order/migrations/0002_alter_discountcode_expiration_date_and_more.py @@ -0,0 +1,52 @@ +# Generated by Django 5.1.2 on 2025-02-04 19:55 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0005_alter_useraddressmodel_options_alter_user_is_active_and_more'), + ('order', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name='discountcode', + name='expiration_date', + field=models.DateTimeField(verbose_name='تاریخ انقضا'), + ), + migrations.AlterField( + model_name='discountcode', + name='name', + field=models.CharField(max_length=50, verbose_name='کد تخفیف'), + ), + migrations.AlterField( + model_name='discountcode', + name='percent', + field=models.DecimalField(decimal_places=2, max_digits=4, verbose_name='درصد'), + ), + migrations.AlterField( + model_name='discountcode', + name='quantity', + field=models.PositiveIntegerField(verbose_name='تعداد'), + ), + migrations.AlterField( + model_name='orderitemmodel', + name='order', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='order.ordermodel', verbose_name='سفارش'), + ), + migrations.AlterField( + model_name='ordermodel', + name='address', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='orders', to='account.useraddressmodel', verbose_name='ادرس'), + ), + migrations.AlterField( + model_name='ordermodel', + name='user', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='orders', to=settings.AUTH_USER_MODEL, verbose_name='کاربر'), + ), + ] diff --git a/backend/order/models.py b/backend/order/models.py index 30af675..f7efb32 100644 --- a/backend/order/models.py +++ b/backend/order/models.py @@ -75,7 +75,7 @@ class OrderModel(models.Model): class OrderItemModel(models.Model): order = models.ForeignKey(OrderModel, on_delete=models.CASCADE, related_name='items', verbose_name='سفارش') quantity = models.SmallIntegerField(verbose_name="تعداد") - product = models.ForeignKey(ProductModel, on_delete=models.CASCADE, verbose_name="محصول") + product = models.ForeignKey(ProductModel, on_delete=models.PROTECT, verbose_name="محصول") class Meta: verbose_name = 'محصول خریداری شده' verbose_name_plural = 'محصولات خریداری شده' diff --git a/backend/product/admin.py b/backend/product/admin.py index dce4047..fc0c602 100644 --- a/backend/product/admin.py +++ b/backend/product/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin from .models import * -from unfold.admin import ModelAdmin, TabularInline +from unfold.admin import ModelAdmin, TabularInline, StackedInline from import_export.admin import ImportExportModelAdmin from unfold.contrib.import_export.forms import ExportForm, ImportForm, SelectableFieldsExportForm @@ -10,6 +10,20 @@ from unfold.widgets import ( UnfoldAdminColorInputWidget, ) from unfold.decorators import action, display + +@admin.register(InPackItems) +class InPackItemsAdmin(ModelAdmin, ImportExportModelAdmin): + import_form_class = ImportForm + export_form_class = ExportForm + search_fields = ['item_title'] + compressed_fields = True + warn_unsaved_form = True + + formfield_overrides = { + ArrayField: { + "widget": ArrayWidget, + } + } class InStuckColorsInLine(TabularInline): model = InStuckColors extra = 0 @@ -17,23 +31,52 @@ class InStuckColorsInLine(TabularInline): formfield_overrides = { models.CharField: {"widget": UnfoldAdminColorInputWidget()}, } + verbose_name = 'رنگ موجود' + verbose_name_plural = 'رنگهای موجود' + + +@admin.register(DetailModel) +class DetailModelAdmin(ModelAdmin, ImportExportModelAdmin): + import_form_class = ImportForm + export_form_class = ExportForm + search_fields = ['title'] + compressed_fields = True + warn_unsaved_form = True + + formfield_overrides = { + ArrayField: { + "widget": ArrayWidget, + } + } + + + + +class DetailModelInLine(StackedInline): + model = ProductDetailModel + extra = 0 + tab = True + autocomplete_fields = ['detail'] + verbose_name = 'جزئیات محصول' + verbose_name_plural = 'جزئیات محصول' @admin.register(ProductModel) class ProductModelAdmin(ModelAdmin, ImportExportModelAdmin): import_form_class = ImportForm export_form_class = ExportForm - inlines = [InStuckColorsInLine] + inlines = [InStuckColorsInLine, DetailModelInLine] readonly_fields = ('slug', ) search_fields = ['name', 'description', ] list_filter = ['currency', 'show', 'category'] - autocomplete_fields = ['related_products'] + autocomplete_fields = ['related_products', 'in_pack_items'] # compressed_fields = True warn_unsaved_form = True list_display = ['display_image', 'display_price', 'view', 'show', 'rating', 'category', 'discount', 'sell'] fieldsets = ( - ('Main Fileds', {'fields': ('name', 'description', 'price', 'min_price', 'currency', 'discount', 'category', 'related_products', 'show',), "classes": ["tab"],}), - ('SEO Fileds', {'fields': ('meta_description', 'meta_keywords', 'meta_rating', 'slug'), "classes": ["tab"],}), - ('Users Fileds', {'fields': ('rating', 'view', 'sell', ), "classes": ["tab"],}) + ('فیلد های اصلی', {'fields': ('name', 'description', 'price', 'min_price', 'currency', 'discount', 'category', 'related_products', 'show',), "classes": ["tab"],}), + ('فیلد های سيو', {'fields': ('meta_description', 'meta_keywords', 'meta_rating', 'slug'), "classes": ["tab"],}), + ('فیلد های مربوط به کاربر', {'fields': ('rating', 'view', 'sell', ), "classes": ["tab"],}), + ('فیلد های ایتم های پک', {'fields': ('in_pack_items', ), "classes": ["tab"],}) ) formfield_overrides = { @@ -129,7 +172,7 @@ class CommentAdmin(ModelAdmin, ImportExportModelAdmin): } } def display_content(self, obj): - return obj.display_content[0:20] + '...' + return obj.content[0:35] + '...' display_content.short_description = 'محتوای کامنت' @admin.register(DollorModel) diff --git a/backend/product/migrations/0013_detailmodel_alter_instuckcolors_product_and_more.py b/backend/product/migrations/0013_detailmodel_alter_instuckcolors_product_and_more.py new file mode 100644 index 0000000..6382790 --- /dev/null +++ b/backend/product/migrations/0013_detailmodel_alter_instuckcolors_product_and_more.py @@ -0,0 +1,76 @@ +# Generated by Django 5.1.2 on 2025-02-04 19:55 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0012_productmodel_min_price'), + ] + + operations = [ + migrations.CreateModel( + name='DetailModel', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=50, verbose_name='عنوان')), + ('detail_text1', models.CharField(max_length=150, verbose_name='متن جزیات ۱')), + ('detail_text2', models.CharField(max_length=150, verbose_name='متن جزیات ۲')), + ('detail_text3', models.CharField(max_length=150, verbose_name='متن جزیات ۳')), + ('detail_text4', models.CharField(max_length=150, verbose_name='متن جزیات ۴')), + ], + options={ + 'verbose_name': 'مدل جزیات', + 'verbose_name_plural': 'مدل های جزیات', + }, + ), + migrations.AlterField( + model_name='instuckcolors', + name='product', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='colors', to='product.productmodel', verbose_name='محصول'), + ), + migrations.AlterField( + model_name='maincategorymodel', + name='name', + field=models.CharField(max_length=50, verbose_name='نام دسته بندی'), + ), + migrations.AlterField( + model_name='productmodel', + name='meta_description', + field=models.CharField(blank=True, help_text='این فیلد را حتما پر کنید', max_length=300, null=True, verbose_name='متا دیسکریپشن'), + ), + migrations.AlterField( + model_name='productmodel', + name='meta_keywords', + field=models.CharField(blank=True, help_text='این فیلد را حتما پر کنید', max_length=300, null=True, verbose_name='متا کیورد'), + ), + migrations.AlterField( + model_name='productmodel', + name='meta_rating', + field=models.FloatField(default=5, help_text='امتیاز محصول', verbose_name='متا ریتینگ'), + ), + migrations.AlterField( + model_name='productmodel', + name='related_products', + field=models.ManyToManyField(blank=True, to='product.productmodel', verbose_name='محصولات مرتبط'), + ), + migrations.AlterField( + model_name='subcategorymodel', + name='name', + field=models.CharField(max_length=50, verbose_name='نام دسته بندی'), + ), + migrations.CreateModel( + name='ProductDetailModel', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('detail', models.ManyToManyField(to='product.detailmodel', verbose_name='جزيات ها')), + ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='product.productmodel', verbose_name='محصول مرتبط')), + ], + options={ + 'verbose_name': 'جزیات محصول', + 'verbose_name_plural': 'جزیات محصول ها', + }, + ), + ] diff --git a/backend/product/migrations/0014_inpackitems_alter_productdetailmodel_product_and_more.py b/backend/product/migrations/0014_inpackitems_alter_productdetailmodel_product_and_more.py new file mode 100644 index 0000000..7f940f2 --- /dev/null +++ b/backend/product/migrations/0014_inpackitems_alter_productdetailmodel_product_and_more.py @@ -0,0 +1,36 @@ +# Generated by Django 5.1.2 on 2025-02-04 20:48 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0013_detailmodel_alter_instuckcolors_product_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='InPackItems', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('item_title', models.CharField(max_length=50)), + ('cover', models.ImageField(upload_to='product_items/', verbose_name='کاور ایتم')), + ], + options={ + 'verbose_name': 'ایتم داخل پک', + 'verbose_name_plural': 'ایتم های داخل پک', + }, + ), + migrations.AlterField( + model_name='productdetailmodel', + name='product', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='details', to='product.productmodel', verbose_name='محصول مرتبط'), + ), + migrations.AddField( + model_name='productmodel', + name='in_pack_items', + field=models.ManyToManyField(blank=True, to='product.inpackitems', verbose_name='ایتم های داخل پک'), + ), + ] diff --git a/backend/product/models.py b/backend/product/models.py index d0dacdb..b2e4091 100644 --- a/backend/product/models.py +++ b/backend/product/models.py @@ -85,6 +85,18 @@ class DollorModel(models.Model): verbose_name_plural = 'مدل دلار' +class InPackItems(models.Model): + item_title = models.CharField(max_length=50) + cover = models.ImageField(upload_to='product_items/', verbose_name='کاور ایتم') + + class Meta: + verbose_name = 'ایتم داخل پک' + verbose_name_plural = 'ایتم های داخل پک' + + def __str__(self): + return self.item_title + + class ProductModel(models.Model): name = models.CharField(max_length=255, verbose_name='نام') description = models.TextField(verbose_name='توضیحات') @@ -113,6 +125,8 @@ class ProductModel(models.Model): created_at = models.DateTimeField(auto_now_add=True, verbose_name='زمان ثبت محصول') category = models.ForeignKey(SubCategoryModel, null=True, on_delete=models.SET_NULL, related_name='products', verbose_name='دسته بندی محصول') related_products = models.ManyToManyField('self', blank=True, verbose_name='محصولات مرتبط') + in_pack_items = models.ManyToManyField(InPackItems, blank=True, verbose_name='ایتم های داخل پک') + def format_discount_price(self): discount_price = int(self.price * (100 - self.discount) / 100) formatted_num = "{:,.0f}".format(discount_price) @@ -152,6 +166,34 @@ class ProductModel(models.Model): verbose_name_plural = 'محصولات' + +class DetailModel(models.Model): + title = models.CharField(max_length=50, verbose_name='عنوان') + detail_text1 = models.CharField(max_length=150 , verbose_name='متن جزیات ۱') + detail_text2 = models.CharField(max_length=150 , verbose_name='متن جزیات ۲') + detail_text3 = models.CharField(max_length=150 , verbose_name='متن جزیات ۳') + detail_text4 = models.CharField(max_length=150 , verbose_name='متن جزیات ۴') + def __str__(self): + return self.title + + class Meta: + verbose_name = 'مدل جزیات' + verbose_name_plural = 'مدل های جزیات' + + +class ProductDetailModel(models.Model): + product = models.ForeignKey(ProductModel, on_delete=models.CASCADE, verbose_name='محصول مرتبط', related_name='details') + detail = models.ManyToManyField(DetailModel, verbose_name='جزيات ها') + + class Meta: + verbose_name = 'جزیات محصول' + verbose_name_plural = 'جزیات محصول ها' + + def __str__(self): + return f'جزيیات محصول {self.product}' + + + class InStuckColors(models.Model): color = models.CharField(_("رنگ"), null=True, blank=True, max_length=255) in_stuck = models.PositiveIntegerField(default=0, verbose_name="تعداد موجود") diff --git a/backend/product/serializers.py b/backend/product/serializers.py index 7a4b60f..f43b9cd 100644 --- a/backend/product/serializers.py +++ b/backend/product/serializers.py @@ -10,11 +10,25 @@ class InStuckColorsSerializer(serializers.ModelSerializer): fields = ['color', 'in_stuck'] +class DetailSerializer(serializers.ModelSerializer): + class Meta: + model = DetailModel + fields = '__all__' + +class ProductDetailSerializer(serializers.ModelSerializer): + detail = DetailSerializer(many=True, read_only=True) + class Meta: + model = ProductDetailModel + exclude = ('product',) + + + class DynamicProductSerializer(serializers.ModelSerializer): colors = InStuckColorsSerializer(many=True, read_only=True) price = 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) view_type = self.context.get('view_type', 'all') @@ -32,7 +46,7 @@ class DynamicProductSerializer(serializers.ModelSerializer): fields = "__all__" view_type = { 'list': ['name', 'price', 'image1', 'video', 'rating', 'discount', 'slug', 'category', 'colors'], - 'instance': ['name', 'description', 'price', 'image1', 'image2', 'image3', 'video', 'rating', 'discount', 'slug', 'meta_description', 'meta_keywords', 'meta_rating', 'category', 'colors', 'related_products'], + 'instance': ['name', 'description', 'price', 'image1', 'image2', 'image3', 'video', 'rating', 'discount', 'slug', 'meta_description', 'meta_keywords', 'meta_rating', 'category', 'colors', 'related_products', 'details', 'in_pack_items'], 'chat': ['name', 'description', 'price', 'in_stock', 'discount', 'colors'] } diff --git a/backend/templates/formula/service.html b/backend/templates/formula/service.html index 1ddcb8b..664e402 100644 --- a/backend/templates/formula/service.html +++ b/backend/templates/formula/service.html @@ -5,8 +5,8 @@