add bulk update subcategory action and form for ProductVariant; simplify delete permission logic

This commit is contained in:
Parsa Nazer
2025-12-20 19:42:28 +03:30
parent 0377d04013
commit c0bb053785
3 changed files with 142 additions and 8 deletions
+55 -1
View File
@@ -10,8 +10,9 @@ 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
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):
@@ -227,6 +228,18 @@ class ProductVariantInLine(ProductVariantInlineAdminPermission, StackedInline):
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):
import_form_class = ImportForm
@@ -252,6 +265,7 @@ class ProductModelAdmin(ProductAdminPermission, ModelAdmin, ImportExportModelAdm
autocomplete_fields = ['related_products', 'shop']
# 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', 'show', 'rating', 'category', 'created_at']
fieldsets = (
@@ -301,6 +315,46 @@ class ProductModelAdmin(ProductAdminPermission, ModelAdmin, ImportExportModelAdm
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']
# @display(
# description=("نمایش در صفحه ی اصلی"),
+1 -7
View File
@@ -30,13 +30,7 @@ class ProductAdminPermission:
return request.user.shop == obj.shop
def has_delete_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.shop
return request.user.is_superuser
def has_view_permission(self, request, obj=None):
if request.user.is_superuser or obj == None:
@@ -0,0 +1,86 @@
{% extends "admin/base_site.html" %}
{% load static %}
{% block content %}
<div style="max-width: 800px; margin: 40px auto; padding: 20px;">
<h1 style="margin-bottom: 20px;">{{ title }}</h1>
<div style="background: #f8f9fa; padding: 20px; border-radius: 5px; margin-bottom: 20px;">
<h3 style="margin-top: 0;">محصولات انتخاب شده:</h3>
<ul style="list-style: none; padding: 0;">
{% for product in products %}
<li style="padding: 8px; border-bottom: 1px solid #dee2e6;">
<strong>{{ product.name }}</strong>
{% if product.category %}
<span style="color: #6c757d;"> - دسته فعلی: {{ product.category.name }}</span>
{% else %}
<span style="color: #dc3545;"> - بدون دسته‌بندی</span>
{% endif %}
</li>
{% endfor %}
</ul>
<p style="margin-top: 15px; font-weight: bold;">
تعداد کل: {{ products|length }} محصول
</p>
</div>
<form method="post" style="background: white; padding: 20px; border: 1px solid #dee2e6; border-radius: 5px;">
{% csrf_token %}
<div style="margin-bottom: 20px;">
<label for="{{ form.subcategory.id_for_label }}" style="display: block; margin-bottom: 8px; font-weight: bold;">
{{ form.subcategory.label }}
</label>
{{ form.subcategory }}
{% if form.subcategory.help_text %}
<p style="color: #6c757d; font-size: 0.9em; margin-top: 5px;">
{{ form.subcategory.help_text }}
</p>
{% endif %}
{% if form.subcategory.errors %}
<div style="color: #dc3545; margin-top: 5px;">
{{ form.subcategory.errors }}
</div>
{% endif %}
</div>
<input type="hidden" name="action" value="bulk_update_subcategory_action">
{% for id in selected_ids %}
<input type="hidden" name="_selected_action" value="{{ id }}">
{% endfor %}
<div style="margin-top: 30px; display: flex; gap: 10px;">
<button type="submit" name="apply"
style="background: #28a745; color: white; border: none; padding: 10px 20px;
border-radius: 5px; cursor: pointer; font-size: 14px;">
اعمال تغییرات
</button>
<a href="{% url 'admin:product_productmodel_changelist' %}"
style="background: #6c757d; color: white; border: none; padding: 10px 20px;
border-radius: 5px; text-decoration: none; display: inline-block; font-size: 14px;">
انصراف
</a>
</div>
</form>
</div>
<style>
select {
width: 100%;
padding: 8px 12px;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 14px;
}
select:focus {
outline: none;
border-color: #80bdff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
button:hover {
opacity: 0.9;
}
</style>
{% endblock %}