Рефакторинг системы вариативных товаров и справочник атрибутов
Основные изменения: - Переименование ConfigurableKitProduct → ConfigurableProduct - Добавлена поддержка Product как варианта (не только ProductKit) - Создан справочник атрибутов (ProductAttribute, ProductAttributeValue) - CRUD для управления атрибутами с inline редактированием значений - Пересозданы миграции с нуля для всех приложений - Добавлена ссылка на атрибуты в навигацию 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
116
myproject/products/models/attributes.py
Normal file
116
myproject/products/models/attributes.py
Normal file
@@ -0,0 +1,116 @@
|
||||
"""
|
||||
Модели для справочника атрибутов товаров.
|
||||
Используется для создания переиспользуемых атрибутов (Длина стебля, Цвет, Размер и т.д.)
|
||||
"""
|
||||
from django.db import models
|
||||
from django.utils.text import slugify
|
||||
from unidecode import unidecode
|
||||
|
||||
|
||||
class ProductAttribute(models.Model):
|
||||
"""
|
||||
Справочник атрибутов для вариативных товаров.
|
||||
Примеры: Длина стебля, Цвет, Размер, Упаковка.
|
||||
"""
|
||||
name = models.CharField(
|
||||
max_length=100,
|
||||
unique=True,
|
||||
verbose_name="Название",
|
||||
help_text="Например: Длина стебля, Цвет, Размер"
|
||||
)
|
||||
slug = models.SlugField(
|
||||
max_length=100,
|
||||
unique=True,
|
||||
blank=True,
|
||||
verbose_name="Slug",
|
||||
help_text="Автоматически генерируется из названия"
|
||||
)
|
||||
description = models.TextField(
|
||||
blank=True,
|
||||
verbose_name="Описание",
|
||||
help_text="Опциональное описание атрибута"
|
||||
)
|
||||
position = models.PositiveIntegerField(
|
||||
default=0,
|
||||
verbose_name="Позиция",
|
||||
help_text="Порядок отображения в списке"
|
||||
)
|
||||
created_at = models.DateTimeField(
|
||||
auto_now_add=True,
|
||||
verbose_name="Дата создания"
|
||||
)
|
||||
updated_at = models.DateTimeField(
|
||||
auto_now=True,
|
||||
verbose_name="Дата обновления"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Атрибут товара"
|
||||
verbose_name_plural = "Атрибуты товаров"
|
||||
ordering = ['position', 'name']
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
self.slug = slugify(unidecode(self.name))
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def values_count(self):
|
||||
"""Количество значений у атрибута"""
|
||||
return self.values.count()
|
||||
|
||||
|
||||
class ProductAttributeValue(models.Model):
|
||||
"""
|
||||
Значения атрибутов.
|
||||
Примеры для атрибута "Длина стебля": 50, 60, 70, 80.
|
||||
"""
|
||||
attribute = models.ForeignKey(
|
||||
ProductAttribute,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='values',
|
||||
verbose_name="Атрибут"
|
||||
)
|
||||
value = models.CharField(
|
||||
max_length=100,
|
||||
verbose_name="Значение",
|
||||
help_text="Например: 50, 60, 70 (для длины) или Красный, Белый (для цвета)"
|
||||
)
|
||||
slug = models.SlugField(
|
||||
max_length=100,
|
||||
blank=True,
|
||||
verbose_name="Slug"
|
||||
)
|
||||
position = models.PositiveIntegerField(
|
||||
default=0,
|
||||
verbose_name="Позиция",
|
||||
help_text="Порядок отображения в списке значений"
|
||||
)
|
||||
created_at = models.DateTimeField(
|
||||
auto_now_add=True,
|
||||
verbose_name="Дата создания"
|
||||
)
|
||||
updated_at = models.DateTimeField(
|
||||
auto_now=True,
|
||||
verbose_name="Дата обновления"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Значение атрибута"
|
||||
verbose_name_plural = "Значения атрибутов"
|
||||
ordering = ['position', 'value']
|
||||
unique_together = ['attribute', 'value']
|
||||
indexes = [
|
||||
models.Index(fields=['attribute', 'position']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.attribute.name}: {self.value}"
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.slug:
|
||||
self.slug = slugify(unidecode(self.value))
|
||||
super().save(*args, **kwargs)
|
||||
Reference in New Issue
Block a user