Добавлена модель атрибутов для вариативных товаров (ConfigurableKitProductAttribute)
- Создана модель ConfigurableKitProductAttribute с полями name, option, position, visible - Добавлены формы и formsets для управления атрибутами родительского товара - Обновлены CRUD представления для работы с атрибутами (создание/редактирование) - Добавлен блок атрибутов в шаблоны создания/редактирования - Обновлена страница детального просмотра с отображением атрибутов товара - Добавлен JavaScript для динамического добавления форм атрибутов - Реализована валидация дубликатов атрибутов в formset - Атрибуты сохраняются в transaction.atomic() вместе с вариантами Теперь можно определять схему атрибутов для экспорта на WooCommerce без использования JSON или ID, только name и option.
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
# Generated by Django 5.0.10 on 2025-11-17 19:29
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('products', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ConfigurableKitProduct',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200, verbose_name='Название')),
|
||||
('sku', models.CharField(blank=True, db_index=True, max_length=100, null=True, verbose_name='Артикул')),
|
||||
('slug', models.SlugField(blank=True, max_length=200, unique=True, verbose_name='URL-идентификатор')),
|
||||
('description', models.TextField(blank=True, null=True, verbose_name='Описание')),
|
||||
('short_description', models.TextField(blank=True, help_text='Используется для карточек товаров, превью и площадок', null=True, verbose_name='Краткое описание')),
|
||||
('status', models.CharField(choices=[('active', 'Активный'), ('archived', 'Архивный'), ('discontinued', 'Снят')], db_index=True, default='active', max_length=20, verbose_name='Статус')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Дата обновления')),
|
||||
('archived_at', models.DateTimeField(blank=True, null=True, verbose_name='Время архивирования')),
|
||||
('archived_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='archived_%(class)s_set', to=settings.AUTH_USER_MODEL, verbose_name='Архивировано пользователем')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Конфигурируемый товар (из комплектов)',
|
||||
'verbose_name_plural': 'Конфигурируемые товары (из комплектов)',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ConfigurableKitOption',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('attributes', models.TextField(blank=True, verbose_name='Атрибуты варианта (для внешних площадок)')),
|
||||
('is_default', models.BooleanField(default=False, verbose_name='Вариант по умолчанию')),
|
||||
('kit', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='as_configurable_option_in', to='products.productkit', verbose_name='Комплект (вариант)')),
|
||||
('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='options', to='products.configurablekitproduct', verbose_name='Родитель (конфигурируемый товар)')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Вариант комплекта',
|
||||
'verbose_name_plural': 'Варианты комплектов',
|
||||
'indexes': [models.Index(fields=['parent'], name='products_co_parent__56ecfa_idx'), models.Index(fields=['kit'], name='products_co_kit_id_3fa7fe_idx'), models.Index(fields=['parent', 'is_default'], name='products_co_parent__ffa4ca_idx')],
|
||||
'unique_together': {('parent', 'kit')},
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 5.0.10 on 2025-11-17 19:30
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('products', '0002_configurablekitproduct_configurablekitoption'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='configurablekitproduct',
|
||||
options={'verbose_name': 'Вариативный товар (из комплектов)', 'verbose_name_plural': 'Вариативные товары (из комплектов)'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='configurablekitoption',
|
||||
name='parent',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='options', to='products.configurablekitproduct', verbose_name='Родитель (вариативный товар)'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,32 @@
|
||||
# Generated by Django 5.0.10 on 2025-11-17 21:38
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('products', '0003_alter_configurablekitproduct_options_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ConfigurableKitProductAttribute',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(help_text='Например: Цвет, Размер, Длина', max_length=150, verbose_name='Название атрибута')),
|
||||
('option', models.CharField(help_text='Например: Красный, M, 60см', max_length=150, verbose_name='Значение опции')),
|
||||
('position', models.PositiveIntegerField(default=0, help_text='Меньше = выше в списке', verbose_name='Порядок отображения')),
|
||||
('visible', models.BooleanField(default=True, help_text='Показывать ли атрибут на странице товара', verbose_name='Видимый на витрине')),
|
||||
('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='parent_attributes', to='products.configurablekitproduct', verbose_name='Родительский товар')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Атрибут вариативного товара',
|
||||
'verbose_name_plural': 'Атрибуты вариативных товаров',
|
||||
'ordering': ['parent', 'position', 'name', 'option'],
|
||||
'indexes': [models.Index(fields=['parent', 'name'], name='products_co_parent__4a7869_idx'), models.Index(fields=['parent', 'position'], name='products_co_parent__0904e2_idx')],
|
||||
'unique_together': {('parent', 'name', 'option')},
|
||||
},
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user