add bulk update subcategory action and form for ProductVariant; simplify delete permission logic
This commit is contained in:
@@ -10,8 +10,9 @@ from django.contrib.postgres.fields import ArrayField
|
|||||||
from unfold.widgets import UnfoldAdminColorInputWidget
|
from unfold.widgets import UnfoldAdminColorInputWidget
|
||||||
from unfold.decorators import action, display
|
from unfold.decorators import action, display
|
||||||
from utils.admin import ModelAdmin
|
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 .permissions import ProductDetailCategoryPermission, ProductAdminPermission, ProductVariantAdminPermission, ProductVariantInlineAdminPermission, InPackItemsAdminPermission, AttributeTypeAdminPermission, AttributeValueAdminPermission
|
||||||
|
from django import forms
|
||||||
|
|
||||||
@admin.register(ProductDetailCategory)
|
@admin.register(ProductDetailCategory)
|
||||||
class ProductDetailCategoryAdmin(ProductDetailCategoryPermission, ModelAdmin, ImportExportModelAdmin):
|
class ProductDetailCategoryAdmin(ProductDetailCategoryPermission, ModelAdmin, ImportExportModelAdmin):
|
||||||
@@ -227,6 +228,18 @@ class ProductVariantInLine(ProductVariantInlineAdminPermission, StackedInline):
|
|||||||
|
|
||||||
|
|
||||||
from unfold.contrib.filters.admin import RelatedDropdownFilter
|
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)
|
@admin.register(ProductVariant)
|
||||||
class ProductVariantAdmin(ProductVariantAdminPermission, ModelAdmin, ImportExportModelAdmin):
|
class ProductVariantAdmin(ProductVariantAdminPermission, ModelAdmin, ImportExportModelAdmin):
|
||||||
import_form_class = ImportForm
|
import_form_class = ImportForm
|
||||||
@@ -252,6 +265,7 @@ class ProductModelAdmin(ProductAdminPermission, ModelAdmin, ImportExportModelAdm
|
|||||||
autocomplete_fields = ['related_products', 'shop']
|
autocomplete_fields = ['related_products', 'shop']
|
||||||
# compressed_fields = True
|
# compressed_fields = True
|
||||||
warn_unsaved_form = True
|
warn_unsaved_form = True
|
||||||
|
list_per_page = 2
|
||||||
actions_list = ['redirect_to_learn', 'update_products_price']
|
actions_list = ['redirect_to_learn', 'update_products_price']
|
||||||
list_display = ['display_image', 'shop__shop_name','display_price', 'view', 'show', 'rating', 'category', 'created_at']
|
list_display = ['display_image', 'shop__shop_name','display_price', 'view', 'show', 'rating', 'category', 'created_at']
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
@@ -301,6 +315,46 @@ class ProductModelAdmin(ProductAdminPermission, ModelAdmin, ImportExportModelAdm
|
|||||||
messages.success(request, f"قیمت {ProductVariant.objects.all().count()} تنوع محصول اپدیت شد")
|
messages.success(request, f"قیمت {ProductVariant.objects.all().count()} تنوع محصول اپدیت شد")
|
||||||
return redirect("admin:product_productmodel_changelist")
|
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(
|
# @display(
|
||||||
# description=("نمایش در صفحه ی اصلی"),
|
# description=("نمایش در صفحه ی اصلی"),
|
||||||
|
|||||||
@@ -30,13 +30,7 @@ class ProductAdminPermission:
|
|||||||
return request.user.shop == obj.shop
|
return request.user.shop == obj.shop
|
||||||
|
|
||||||
def has_delete_permission(self, request, obj=None):
|
def has_delete_permission(self, request, obj=None):
|
||||||
if request.user.is_superuser or obj == None:
|
return request.user.is_superuser
|
||||||
return True
|
|
||||||
|
|
||||||
if not hasattr(request.user, 'shop'):
|
|
||||||
return False
|
|
||||||
|
|
||||||
return request.user.shop == obj.shop
|
|
||||||
|
|
||||||
def has_view_permission(self, request, obj=None):
|
def has_view_permission(self, request, obj=None):
|
||||||
if request.user.is_superuser or 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 %}
|
||||||
Reference in New Issue
Block a user