Files
hossein-por-shop/backend/product/admin.py
T
2026-02-20 19:12:14 +03:30

478 lines
16 KiB
Python

from django.contrib import admin, messages
from django import forms
# from product.tasks import update_prices
from .models import *
from unfold.admin import TabularInline, StackedInline
from home.models import LearnVideoModel
from import_export.admin import ImportExportModelAdmin
from unfold.contrib.import_export.forms import ExportForm, ImportForm, SelectableFieldsExportForm
from unfold.contrib.forms.widgets import ArrayWidget, WysiwygWidget
from django.contrib.postgres.fields import ArrayField
from unfold.widgets import UnfoldAdminColorInputWidget
from unfold.decorators import action, display
from utils.admin import ModelAdmin
from django.shortcuts import redirect, render
from .permissions import ProductDetailCategoryPermission, ProductAdminPermission, ProductVariantAdminPermission, ProductVariantInlineAdminPermission, InPackItemsAdminPermission, AttributeTypeAdminPermission, AttributeValueAdminPermission
from django import forms
@admin.register(ProductDetailCategory)
class ProductDetailCategoryAdmin(ProductDetailCategoryPermission, 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,
}
}
@admin.register(UnitCategoryModel)
class UnitCategoryAdmin(ModelAdmin):
pass
@admin.register(InPackItems)
class InPackItemsAdmin(InPackItemsAdminPermission, 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,
}
}
from account.models import ShopModel
@admin.register(ShopModel)
class ShopModelAdmin(ModelAdmin, ImportExportModelAdmin):
search_fields = ['name']
compressed_fields = True
warn_unsaved_form = True
formfield_overrides = {
ArrayField: {
"widget": ArrayWidget,
}
}
def get_queryset(self, request):
if request.user.is_superuser:
return ShopModel.objects.all()
if not hasattr(request.user, 'shop'):
return ShopModel.objects.none()
queryset = ShopModel.objects.filter(id=request.user.shop.id)
return queryset
def has_view_permission(self, request, obj=None):
if request.user.is_superuser or obj == None:
return True
if not hasattr(request.user, 'shop'):
return False
return request.user.shop == obj
class AttributeValueInLine(StackedInline):
model = AttributeValue
extra = 0
show_change_link = True
min_num = 1
# autocomplete_fields = ['product_attributes', 'in_pack_items', 'images']
# search_fields = ['']
def has_view_permission(self, request, obj = ...):
return True
def has_add_permission(self, request, obj):
return True
def has_change_permission(self, request, obj = ...):
return False
def has_delete_permission(self, request, obj = ...):
return False
@admin.register(AttributeType)
class AttributeTypeAdmin(AttributeTypeAdminPermission, ModelAdmin, ImportExportModelAdmin):
import_form_class = ImportForm
export_form_class = ExportForm
search_fields = ['name']
compressed_fields = True
warn_unsaved_form = True
inlines = [AttributeValueInLine]
formfield_overrides = {
ArrayField: {
"widget": ArrayWidget,
}
}
@admin.register(AttributeValue)
class AttributeValueAdmin(AttributeValueAdminPermission, ModelAdmin, ImportExportModelAdmin):
import_form_class = ImportForm
export_form_class = ExportForm
search_fields = ['value', 'attribute_type__name']
compressed_fields = True
warn_unsaved_form = True
formfield_overrides = {
ArrayField: {
"widget": ArrayWidget,
}
}
# def get_form(self, request, obj=None, change=False, **kwargs):
# form = super().get_form(request, obj, change, **kwargs)
# form.base_fields["color"].widget = UnfoldAdminColorInputWidget()
# return form
@admin.register(ProductImageModel)
class ProductImagesAdmin(ModelAdmin):
import_form_class = ImportForm
export_form_class = ExportForm
search_fields = ['name']
compressed_fields = True
warn_unsaved_form = True
list_display = ['display_image', 'name',]
formfield_overrides = {
ArrayField: {
"widget": ArrayWidget,
}
}
def has_view_permission(self, request, obj=None):
return True
def has_add_permission(self, request, obj=None):
return True
@display(description='محصول', header=True)
def display_image(self, instance):
if instance and instance.image:
image = instance.image.url
else:
image = None
return [
instance.name,
None,
None,
{
"path": image,
"height": 30,
"width": 30,
"borderless": True,
# "squared": True,
},
]
@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 DetailInLine(StackedInline):
model = DetailModel
extra = 0
show_change_link = True
min_num = 1
max_num = 4
from unfold.widgets import UnfoldAdminTextInputWidget
# --- ProductVariantAdminForm for price formatting ---
class ProductVariantAdminForm(forms.ModelForm):
class Meta:
model = ProductVariant
fields = "__all__"
widgets = {
"input_price": UnfoldAdminTextInputWidget(attrs={"class": "price-input"}),
"min_price": UnfoldAdminTextInputWidget(attrs={"class": "price-input"}),
"profit": UnfoldAdminTextInputWidget(attrs={"class": "price-input"}),
}
@admin.register(ProductDetailModel)
class ProductDetailModel1Admin(ModelAdmin, ImportExportModelAdmin):
import_form_class = ImportForm
export_form_class = ExportForm
search_fields = ['detail_category__title', 'name']
compressed_fields = True
warn_unsaved_form = True
autocomplete_fields = ['detail_category',]
inlines = [DetailInLine]
formfield_overrides = {
ArrayField: {
"widget": ArrayWidget,
}
}
def has_view_permission(self, request, obj=None):
return True
def has_add_permission(self, request, obj=None):
return True
class ProductVariantInLine(ProductVariantInlineAdminPermission, StackedInline):
model = ProductVariant
form = ProductVariantAdminForm
extra = 0
show_change_link = True
tab = True
min_num = 1
readonly_fields = ['price', 'sell', 'price_in_dollor']
# inlines = [DetailModelInLine]
autocomplete_fields = ['product_attributes', 'in_pack_items', 'images', 'details']
fields = ['input_price', 'min_price', 'currency', 'price', 'profit', 'in_stock', 'discount', 'special_discount_percent', 'images', 'video', 'color', 'product_attributes', 'in_pack_items', 'details', 'sell', 'slider_category']
# 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)
def get_readonly_fields(self, request, obj = ...):
if request.user.is_superuser:
return ['price', 'sell', 'price_in_dollor']
else:
return ['price', 'sell', 'price_in_dollor', 'slider_category']
from unfold.contrib.filters.admin import RelatedDropdownFilter
class BulkSubCategoryForm(forms.Form):
"""فرم برای انتخاب زیر دسته‌بندی برای محصولات"""
subcategory = forms.ModelChoiceField(
queryset=SubCategoryModel.objects.all(),
required=True,
label='زیر دسته‌بندی',
help_text='زیر دسته‌بندی جدید را برای محصولات انتخاب شده انتخاب کنید'
)
@admin.register(ProductVariant)
class ProductVariantAdmin(ProductVariantAdminPermission, ModelAdmin, ImportExportModelAdmin):
form = ProductVariantAdminForm
class Media:
js = ("price-format.js",)
import_form_class = ImportForm
export_form_class = ExportForm
autocomplete_fields = ['product_attributes', 'images', 'in_pack_items', 'details']
warn_unsaved_form = True
readonly_fields = ['price', 'created_at']
list_filter = [('product__category', RelatedDropdownFilter), ('product__category__parent', RelatedDropdownFilter), ('product', RelatedDropdownFilter)]
list_filter_submit = True
list_display = ('product', 'created_at')
# inlines = [DetailModelInLine]
@admin.register(ProductModel)
class ProductModelAdmin(ProductAdminPermission, ModelAdmin, ImportExportModelAdmin):
import_form_class = ImportForm
export_form_class = ExportForm
inlines = [ProductVariantInLine]
readonly_fields = ('slug', 'created_at')
search_fields = ['name', 'description', ]
list_filter = [('category', RelatedDropdownFilter), 'show_in_bot', ('category__parent', RelatedDropdownFilter)]
list_filter_submit = True
autocomplete_fields = ['related_products', 'shop', 'category']
# compressed_fields = True
warn_unsaved_form = True
# list_per_page = 2
actions_list = ['redirect_to_learn', 'update_products_price']
list_display = ['display_image', 'shop__shop_name','display_price', 'view', 'rating', 'category', 'created_at']
fieldsets = (
('فیلد های اصلی', {'fields': ('name', 'description', 'category', 'image', 'related_products','show_in_trends', 'show_in_most_viewed', 'show_in_lot_of_discount', 'show_in_top_seller', 'shop', 'show_in_bot', 'bot_banner'), "classes": ["tab"],}),
('فیلد های سيو', {'fields': ('meta_description', 'meta_keywords', 'meta_rating', 'slug'), "classes": ["tab"],}),
('فیلد های مربوط به کاربر', {'fields': ('rating', 'view',), "classes": ["tab"],}),
# ('فیلد های ایتم های پک', {'fields': ('in_pack_items', ), "classes": ["tab"],})
)
formfield_overrides = {
models.TextField: {
"widget": WysiwygWidget,
},
ArrayField: {
"widget": ArrayWidget,
}
}
def get_readonly_fields(self, request, obj=None):
if request.user.is_superuser:
return ['slug', 'created_at']
else:
return ['show_in_bot', 'bot_banner', 'created_at', 'show_in_top_seller','show_in_trends', 'show_in_most_viewed', 'show_in_lot_of_discount','meta_description', 'meta_keywords', 'meta_rating', 'rating', 'view', 'slug']
def display_price(self, obj):
if obj.variants.all().first():
return obj.variants.all().first().price
display_price.short_description = 'قیمت تومانی'
@display(description='محصول', header=True)
def display_image(self, instance):
if instance and instance.variants.first() and instance.variants.first().images.first():
image = instance.variants.first().images.first().image.url if instance.variants.first().images.first().image else None
else:
image = None
return [
instance.name,
None,
None,
{
"path": image,
"height": 30,
"width": 30,
"borderless": True,
# "squared": True,
},
]
@action(description=f"اپدیت قیمت ها")
def update_products_price(self, request):
from product.tasks import update_prices
update_prices()
messages.success(request, f"قیمت {ProductVariant.objects.all().count()} تنوع محصول اپدیت شد")
return redirect("admin:product_productmodel_changelist")
def bulk_update_subcategory_action(self, request, queryset):
"""اکشن برای تغییر دسته‌بندی چند محصول همزمان"""
# اگر فرم ارسال شده است
if 'apply' in request.POST:
form = BulkSubCategoryForm(request.POST)
if form.is_valid():
subcategory = form.cleaned_data['subcategory']
count = queryset.count()
# به‌روزرسانی تمام محصولات انتخاب شده
queryset.update(category=subcategory)
messages.success(
request,
f'دسته‌بندی {count} محصول به "{subcategory.name}" تغییر یافت.'
)
return redirect('admin:product_productmodel_changelist')
else:
form = BulkSubCategoryForm()
# نمایش صفحه تأیید
context = {
'form': form,
'products': queryset,
'selected_ids': list(queryset.values_list('pk', flat=True)),
'action_name': 'bulk_update_subcategory_action',
'title': 'تغییر دسته‌بندی محصولات',
}
return render(
request,
'admin/product/bulk_subcategory_form.html',
context
)
bulk_update_subcategory_action.short_description = "تغییر دسته‌بندی محصولات انتخاب شده"
actions = ['bulk_update_subcategory_action']
@admin.register(MainCategoryModel)
class MainCategoryModelAdmin(ModelAdmin, ImportExportModelAdmin):
import_form_class = ImportForm
export_form_class = ExportForm
list_display = ['name', ]
readonly_fields = ('slug', )
search_fields = ['name', 'slug']
compressed_fields = True
warn_unsaved_form = True
formfield_overrides = {
ArrayField: {
"widget": ArrayWidget,
}
}
@admin.register(SubCategoryModel)
class SubCategoryModelAdmin(ModelAdmin, ImportExportModelAdmin):
list_display = ['name', 'parent']
search_fields = ['name', 'slug']
list_filter = ['parent', ]
import_form_class = ImportForm
export_form_class = ExportForm
readonly_fields = ('slug', )
compressed_fields = True
warn_unsaved_form = True
formfield_overrides = {
ArrayField: {
"widget": ArrayWidget,
}
}
def has_view_permission(self, request, obj = ...):
return True
@admin.register(CommentModel)
class CommentAdmin(ModelAdmin, ImportExportModelAdmin):
import_form_class = ImportForm
export_form_class = ExportForm
list_display = ['user', 'product', 'display_content','review_status']
search_fields = ['content',]
list_filter = ['review_status',]
compressed_fields = True
warn_unsaved_form = True
formfield_overrides = {
ArrayField: {
"widget": ArrayWidget,
}
}
radio_fields = {'review_status': admin.VERTICAL}
def display_content(self, obj):
return obj.content[0:35] + '...'
display_content.short_description = 'محتوای کامنت'
@admin.register(DollorModel)
class DollorAdmin(ModelAdmin, ImportExportModelAdmin):
import_form_class = ImportForm
export_form_class = ExportForm
exclude = ('unique_filed', )
compressed_fields = True
warn_unsaved_form = True
formfield_overrides = {
ArrayField: {
"widget": ArrayWidget,
}
}
readonly_fields = ('price',)