feat: Унификация slug-идентификаторов и улучшение формы комплектов
- Добавлено поле slug в модель Product с автоматической транслитерацией кириллицы - Обновлена логика генерации slug в Product и ProductKit с использованием unidecode - Изменена логика обработки изображений: теперь используется slug вместо sku - Улучшен UX формы создания комплекта: блок загрузки фото доступен сразу при создании 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -322,6 +322,7 @@ class Product(models.Model):
|
|||||||
|
|
||||||
name = models.CharField(max_length=200, verbose_name="Название")
|
name = models.CharField(max_length=200, verbose_name="Название")
|
||||||
sku = models.CharField(max_length=100, blank=True, null=True, verbose_name="Артикул", db_index=True)
|
sku = models.CharField(max_length=100, blank=True, null=True, verbose_name="Артикул", db_index=True)
|
||||||
|
slug = models.SlugField(max_length=200, unique=True, blank=True, verbose_name="URL-идентификатор")
|
||||||
variant_suffix = models.CharField(
|
variant_suffix = models.CharField(
|
||||||
max_length=20,
|
max_length=20,
|
||||||
blank=True,
|
blank=True,
|
||||||
@@ -398,6 +399,20 @@ class Product(models.Model):
|
|||||||
if not self.sku:
|
if not self.sku:
|
||||||
self.sku = generate_product_sku(self)
|
self.sku = generate_product_sku(self)
|
||||||
|
|
||||||
|
# Автоматическая генерация slug из названия с транслитерацией
|
||||||
|
if not self.slug or self.slug.strip() == '':
|
||||||
|
from unidecode import unidecode
|
||||||
|
# Транслитерируем кириллицу в латиницу, затем применяем slugify
|
||||||
|
transliterated_name = unidecode(self.name)
|
||||||
|
self.slug = slugify(transliterated_name)
|
||||||
|
|
||||||
|
# Убеждаемся, что slug уникален
|
||||||
|
original_slug = self.slug
|
||||||
|
counter = 1
|
||||||
|
while Product.objects.filter(slug=self.slug).exclude(pk=self.pk).exists():
|
||||||
|
self.slug = f"{original_slug}-{counter}"
|
||||||
|
counter += 1
|
||||||
|
|
||||||
# Автоматическая генерация ключевых слов для поиска
|
# Автоматическая генерация ключевых слов для поиска
|
||||||
# Собираем все релевантные данные в одну строку
|
# Собираем все релевантные данные в одну строку
|
||||||
keywords_parts = [
|
keywords_parts = [
|
||||||
@@ -510,7 +525,10 @@ class ProductKit(models.Model):
|
|||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if not self.slug:
|
if not self.slug:
|
||||||
self.slug = slugify(self.name)
|
from unidecode import unidecode
|
||||||
|
# Транслитерируем кириллицу в латиницу, затем применяем slugify
|
||||||
|
transliterated_name = unidecode(self.name)
|
||||||
|
self.slug = slugify(transliterated_name)
|
||||||
# Убеждаемся, что slug уникален
|
# Убеждаемся, что slug уникален
|
||||||
original_slug = self.slug
|
original_slug = self.slug
|
||||||
counter = 1
|
counter = 1
|
||||||
@@ -774,9 +792,9 @@ class ProductPhoto(models.Model):
|
|||||||
|
|
||||||
# Если было загружено новое изображение
|
# Если было загружено новое изображение
|
||||||
if self.image and (is_new or old_image_path):
|
if self.image and (is_new or old_image_path):
|
||||||
# Обрабатываем изображение с использованием SKU товара как идентификатора
|
# Обрабатываем изображение с использованием slug товара как идентификатора
|
||||||
# SKU гарантирует уникальность и читаемость имени файла
|
# slug гарантирует уникальность и читаемость имени файла
|
||||||
identifier = self.product.sku if self.product.sku else self.product.slug
|
identifier = self.product.slug
|
||||||
processed_paths = ImageProcessor.process_image(self.image, 'products', identifier=identifier)
|
processed_paths = ImageProcessor.process_image(self.image, 'products', identifier=identifier)
|
||||||
# Сохраняем только путь к оригиналу в поле image
|
# Сохраняем только путь к оригиналу в поле image
|
||||||
self.image = processed_paths['original']
|
self.image = processed_paths['original']
|
||||||
|
|||||||
@@ -23,12 +23,11 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<!-- Секция управления фотографиями -->
|
<!-- Секция управления фотографиями -->
|
||||||
{% if object %}
|
|
||||||
<div class="mb-4 p-3 bg-light rounded">
|
<div class="mb-4 p-3 bg-light rounded">
|
||||||
<h5 class="mb-3">Управление фотографиями</h5>
|
<h5 class="mb-3">Управление фотографиями</h5>
|
||||||
|
|
||||||
<!-- Существующие фотографии (только при редактировании) -->
|
<!-- Существующие фотографии (только при редактировании) -->
|
||||||
{% if productkit_photos %}
|
{% if object and productkit_photos %}
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<h6 class="mb-3">Текущие фотографии ({{ photos_count }})</h6>
|
<h6 class="mb-3">Текущие фотографии ({{ photos_count }})</h6>
|
||||||
<div class="row g-2 mb-3">
|
<div class="row g-2 mb-3">
|
||||||
@@ -123,7 +122,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class="my-4">
|
<hr class="my-4">
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<!-- Основная информация о комплекте -->
|
<!-- Основная информация о комплекте -->
|
||||||
<h5 class="mb-3">Основная информация</h5>
|
<h5 class="mb-3">Основная информация</h5>
|
||||||
|
|||||||
Reference in New Issue
Block a user