From bd05efd3b5062a82cdfc51b440708625fa26e535 Mon Sep 17 00:00:00 2001 From: Raz Date: Fri, 21 Mar 2025 18:47:19 +0100 Subject: [PATCH] attempt to handle multi color image --- .../shop/images/products/noir_hat.png.avif | Bin 0 -> 10499 bytes shop/templates/shop/product_item.html | 17 +++- shop/templatetags/__init__.py | 0 shop/templatetags/shop_extras.py | 82 ++++++++++++++++++ 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 shop/static/shop/images/products/noir_hat.png.avif create mode 100644 shop/templatetags/__init__.py create mode 100644 shop/templatetags/shop_extras.py diff --git a/shop/static/shop/images/products/noir_hat.png.avif b/shop/static/shop/images/products/noir_hat.png.avif new file mode 100644 index 0000000000000000000000000000000000000000..b451e1d977d94d6c0f2e4e0fc762271eea9e6f89 GIT binary patch literal 10499 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 Ooi6q|v=v(~^ZP$2G`kl7 literal 0 HcmV?d00001 diff --git a/shop/templates/shop/product_item.html b/shop/templates/shop/product_item.html index e00c05e..84c6eb9 100644 --- a/shop/templates/shop/product_item.html +++ b/shop/templates/shop/product_item.html @@ -1,7 +1,11 @@ +{% load shop_extras %}
{% if product.image %} - {{ product.title }} + {{ product.title }} {% else %}
No Image Available
{% endif %} @@ -33,6 +37,7 @@ title="{{ color.name }}" data-color-id="{{ color.id }}" data-color-name="{{ color.name }}" + data-color-image="{{ product.image|color_image_url:color.name }}" onclick="selectColor('{{ product.id }}', '{{ color.id }}', '{{ color.name }}', this)">
{% endfor %}
@@ -123,6 +128,16 @@ function selectColor(productId, colorId, colorName, element) { // Add selected class to clicked color element.classList.add('selected'); + + // Update product image based on selected color + const productImage = document.getElementById(`product-image-${productId}`); + + if (productImage) { + const colorImage = element.getAttribute('data-color-image'); + if (colorImage) { + productImage.src = colorImage; + } + } } diff --git a/shop/templatetags/__init__.py b/shop/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/shop/templatetags/shop_extras.py b/shop/templatetags/shop_extras.py new file mode 100644 index 0000000..c6f6406 --- /dev/null +++ b/shop/templatetags/shop_extras.py @@ -0,0 +1,82 @@ +from django import template +import os + +register = template.Library() + +@register.filter +def color_image_url(product_image, color_name): + """ + Returns color-specific image URL with any supported extension. + Falls back to the original image if no color variant exists. + """ + if not product_image or not color_name: + return product_image + + # Generate color suffix + suffix = generate_color_suffix(color_name) + + # Split path + directory, filename = os.path.split(product_image) + base_name, original_ext = os.path.splitext(filename) + + # List of supported image extensions to check + supported_extensions = ['.png.avif', '.jpg', '.jpeg', '.png', '.gif', '.webp', '.avif'] + + # Check for the color image with original extension first + color_filename = f"{suffix}_{base_name}{original_ext}" + color_image = os.path.join(directory, color_filename) + + # Extract the path after /static/ + static_prefix = '/static/' + if color_image.startswith(static_prefix): + rel_path = color_image[len(static_prefix):] + else: + rel_path = color_image.lstrip('/') + + # Check if file with original extension exists + from django.conf import settings + app_static_path = os.path.join(settings.BASE_DIR, 'shop', 'static', rel_path) + + if os.path.exists(app_static_path): + return color_image + + # If not found with original extension, try other extensions + for ext in supported_extensions: + if ext == original_ext: + continue # Skip the original extension as we already checked it + + color_filename = f"{suffix}_{base_name}{ext}" + color_image = os.path.join(directory, color_filename) + + if color_image.startswith(static_prefix): + rel_path = color_image[len(static_prefix):] + else: + rel_path = color_image.lstrip('/') + + app_static_path = os.path.join(settings.BASE_DIR, 'shop', 'static', rel_path) + + if os.path.exists(app_static_path): + return color_image + + # If no color variant is found with any extension, return the original image + return product_image + +def generate_color_suffix(color_name): + """ + Generates a URL-friendly suffix from a color name + Example: "Noir / Gris Foncé Chiné" becomes "noir_gris_fonce_chine" + """ + import unicodedata + import re + + # Convert to lowercase and replace accents + value = color_name.lower() + value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii') + + # Replace slashes and spaces with underscores + value = re.sub(r'[/\s]+', '_', value) + + # Remove any remaining non-alphanumeric characters + value = re.sub(r'[^\w_]', '', value) + + return value