from django.contrib import admin
from django.utils.html import format_html
import nested_admin
from .models import ProductCategory, ProductTag, Product, ProductKit, KitItem
from .models import ProductPhoto, ProductKitPhoto, ProductCategoryPhoto
from .models import ProductVariantGroup, KitItemPriority, SKUCounter
@admin.register(ProductVariantGroup)
class ProductVariantGroupAdmin(admin.ModelAdmin):
list_display = ['name', 'get_products_count', 'created_at']
search_fields = ['name', 'description']
list_filter = ['created_at']
readonly_fields = ['created_at', 'updated_at']
def get_products_count(self, obj):
return obj.products.count()
get_products_count.short_description = 'Товаров'
class ProductCategoryAdmin(admin.ModelAdmin):
list_display = ('photo_preview', 'name', 'sku', 'slug', 'parent', 'is_active')
list_filter = ('is_active', 'parent')
prepopulated_fields = {'slug': ('name',)}
search_fields = ('name', 'sku')
readonly_fields = ('photo_preview_large',)
def photo_preview(self, obj):
"""Превью фото в списке категорий"""
first_photo = obj.photos.first()
if first_photo and first_photo.image:
return format_html(
'',
first_photo.image.url
)
return "Нет фото"
photo_preview.short_description = "Фото"
def photo_preview_large(self, obj):
"""Большое превью фото в форме редактирования"""
first_photo = obj.photos.first()
if first_photo and first_photo.image:
return format_html(
'
',
first_photo.image.url
)
return "Нет фото"
photo_preview_large.short_description = "Превью основного фото"
class ProductTagAdmin(admin.ModelAdmin):
list_display = ('name', 'slug')
prepopulated_fields = {'slug': ('name',)}
search_fields = ('name',)
class ProductAdmin(admin.ModelAdmin):
list_display = ('photo_preview', 'name', 'sku', 'get_categories_display', 'cost_price', 'sale_price', 'get_variant_groups_display', 'is_active')
list_filter = ('is_active', 'categories', 'tags', 'variant_groups')
search_fields = ('name', 'sku', 'description', 'search_keywords')
filter_horizontal = ('categories', 'tags', 'variant_groups')
readonly_fields = ('photo_preview_large',)
autocomplete_fields = []
fieldsets = (
('Основная информация', {
'fields': ('name', 'sku', 'variant_suffix', 'description', 'categories', 'unit')
}),
('Цены', {
'fields': ('cost_price', 'sale_price')
}),
('Дополнительно', {
'fields': ('tags', 'variant_groups', 'is_active')
}),
('Поиск', {
'fields': ('search_keywords',),
'classes': ('collapse',),
'description': 'Поле для улучшенного поиска. Автоматически генерируется при сохранении, но вы можете добавить дополнительные ключевые слова (синонимы, альтернативные названия и т.д.).'
}),
('Фото', {
'fields': ('photo_preview_large',),
'classes': ('collapse',),
}),
)
def get_categories_display(self, obj):
categories = obj.categories.all()[:3]
if not categories:
return "-"
result = ", ".join([cat.name for cat in categories])
if obj.categories.count() > 3:
result += f" (+{obj.categories.count() - 3})"
return result
get_categories_display.short_description = 'Категории'
def get_variant_groups_display(self, obj):
groups = obj.variant_groups.all()[:3]
if not groups:
return "-"
result = ", ".join([g.name for g in groups])
if obj.variant_groups.count() > 3:
result += f" (+{obj.variant_groups.count() - 3})"
return result
get_variant_groups_display.short_description = 'Группы вариантов'
def photo_preview(self, obj):
"""Превью фото в списке товаров"""
first_photo = obj.photos.first()
if first_photo and first_photo.image:
return format_html(
'
',
first_photo.image.url
)
return "Нет фото"
photo_preview.short_description = "Фото"
def photo_preview_large(self, obj):
"""Большое превью фото в форме редактирования"""
first_photo = obj.photos.first()
if first_photo and first_photo.image:
return format_html(
'
',
first_photo.image.url
)
return "Нет фото"
photo_preview_large.short_description = "Превью основного фото"
class ProductKitAdmin(admin.ModelAdmin):
list_display = ('photo_preview', 'name', 'slug', 'get_categories_display', 'pricing_method', 'is_active')
list_filter = ('is_active', 'pricing_method', 'categories', 'tags')
prepopulated_fields = {'slug': ('name',)}
filter_horizontal = ('categories', 'tags')
readonly_fields = ('photo_preview_large',)
def get_categories_display(self, obj):
categories = obj.categories.all()[:3]
if not categories:
return "-"
result = ", ".join([cat.name for cat in categories])
if obj.categories.count() > 3:
result += f" (+{obj.categories.count() - 3})"
return result
get_categories_display.short_description = 'Категории'
def photo_preview(self, obj):
"""Превью фото в списке комплектов"""
first_photo = obj.photos.first()
if first_photo and first_photo.image:
return format_html(
'
',
first_photo.image.url
)
return "Нет фото"
photo_preview.short_description = "Фото"
def photo_preview_large(self, obj):
"""Большое превью фото в форме редактирования"""
first_photo = obj.photos.first()
if first_photo and first_photo.image:
return format_html(
'
',
first_photo.image.url
)
return "Нет фото"
photo_preview_large.short_description = "Превью основного фото"
class KitItemPriorityInline(nested_admin.NestedTabularInline):
model = KitItemPriority
extra = 0 # Не показывать пустые формы
fields = ['product', 'priority']
autocomplete_fields = ['product']
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.select_related('product')
def formfield_for_foreignkey(self, db_field, request, **kwargs):
"""Показывать только товары из выбранной группы вариантов"""
if db_field.name == "product":
# Получаем kit_item из родительского объекта через request
# Это будет работать автоматически с nested_admin
pass
return super().formfield_for_foreignkey(db_field, request, **kwargs)
class KitItemInline(nested_admin.NestedStackedInline):
model = KitItem
extra = 0 # Не показывать пустые формы
fields = ['product', 'variant_group', 'quantity', 'notes']
autocomplete_fields = ['product']
inlines = [KitItemPriorityInline]
class Media:
css = {
'all': ('admin/css/custom_nested.css',)
}
class ProductPhotoInline(admin.TabularInline):
model = ProductPhoto
extra = 1
readonly_fields = ('image_preview', 'all_versions_preview')
fields = ('image', 'image_preview', 'all_versions_preview', 'order')
def image_preview(self, obj):
"""Превью оригинального фото"""
if obj.image:
return format_html(
'
',
obj.get_original_url()
)
return "Нет изображения"
image_preview.short_description = "Оригинал (превью)"
def all_versions_preview(self, obj):
"""Показывает все версии изображения"""
if not obj.image:
return "Нет изображения"
return format_html(
'