Add ProductKit binding to ConfigurableKitProductAttribute values
Implementation of kit binding feature for ConfigurableKitProduct variants: - Added ForeignKey field `kit` to ConfigurableKitProductAttribute * References ProductKit with CASCADE delete * Optional field (blank=True, null=True) * Indexed for efficient queries - Created migration 0007_add_kit_to_attribute * Handles existing data (NULL values for all current records) * Properly indexed for performance - Updated template configurablekit_form.html * Injected available ProductKits into JavaScript * Added kit selector dropdown in card interface * Each value now has associated kit selection * JavaScript validates kit selection alongside values - Updated JavaScript in card interface * serializeAttributeValues() now collects kit IDs * Creates parallel JSON arrays: values and kits * Stores in hidden fields: attributes-X-values and attributes-X-kits - Updated views _save_attributes_from_cards() in both Create and Update * Reads kit IDs from POST JSON * Looks up ProductKit objects * Creates ConfigurableKitProductAttribute with FK populated * Gracefully handles missing kits - Fixed _should_delete_form() method * More robust handling of formset deletion_field * Works with all formset types - Updated __str__() method * Handles NULL kit case Example workflow: Dlina: 50 -> Kit A, 60 -> Kit B, 70 -> Kit C Upakovka: BEZ -> Kit A, V_UPAKOVKE -> (no kit) Tested with test_kit_binding.py - all tests passing - Kit creation and retrieval - Attribute creation with kit FK - Mixed kit-bound and unbound attributes - Querying attributes by kit - Reverse queries (get kit for attribute value) Added documentation: KIT_BINDING_IMPLEMENTATION.md 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -405,9 +405,13 @@ class ConfigurableKitProduct(BaseProductEntity):
|
||||
|
||||
class ConfigurableKitProductAttribute(models.Model):
|
||||
"""
|
||||
Атрибут родительского вариативного товара.
|
||||
Определяет схему атрибутов для экспорта на WooCommerce и подобные площадки.
|
||||
Например: name="Цвет", option="Красный" или name="Размер", option="M".
|
||||
Атрибут родительского вариативного товара с привязкой к ProductKit.
|
||||
|
||||
Каждое значение атрибута связано с конкретным ProductKit.
|
||||
Например:
|
||||
- Длина: 50 → ProductKit (A)
|
||||
- Длина: 60 → ProductKit (B)
|
||||
- Длина: 70 → ProductKit (C)
|
||||
"""
|
||||
parent = models.ForeignKey(
|
||||
ConfigurableKitProduct,
|
||||
@@ -425,6 +429,15 @@ class ConfigurableKitProductAttribute(models.Model):
|
||||
verbose_name="Значение опции",
|
||||
help_text="Например: Красный, M, 60см"
|
||||
)
|
||||
kit = models.ForeignKey(
|
||||
ProductKit,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='as_attribute_value_in',
|
||||
verbose_name="Комплект для этого значения",
|
||||
help_text="Какой ProductKit связан с этим значением атрибута",
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
position = models.PositiveIntegerField(
|
||||
default=0,
|
||||
verbose_name="Порядок отображения",
|
||||
@@ -440,14 +453,16 @@ class ConfigurableKitProductAttribute(models.Model):
|
||||
verbose_name = "Атрибут вариативного товара"
|
||||
verbose_name_plural = "Атрибуты вариативных товаров"
|
||||
ordering = ['parent', 'position', 'name', 'option']
|
||||
unique_together = [['parent', 'name', 'option']]
|
||||
unique_together = [['parent', 'name', 'option', 'kit']]
|
||||
indexes = [
|
||||
models.Index(fields=['parent', 'name']),
|
||||
models.Index(fields=['parent', 'position']),
|
||||
models.Index(fields=['kit']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.parent.name} - {self.name}: {self.option}"
|
||||
kit_str = self.kit.name if self.kit else "no kit"
|
||||
return f"{self.parent.name} - {self.name}: {self.option} ({kit_str})"
|
||||
|
||||
|
||||
class ConfigurableKitOption(models.Model):
|
||||
|
||||
Reference in New Issue
Block a user