Issue Displaying All Colors of a Product Variant in Django Template
I'm working on a Django application where I have a Product model with a many-to-many relationship with a Color model. I want to display all colors associated with a product variant on its detail page. However, when I click on a specific color (e.g., white), the page only shows that color instead of all available colors for the product variant.
Here’s the relevant code:
Models:
class Color(models.Model):
code = models.CharField(max_length=7) # Hex color code
image = models.ImageField(upload_to='colors/') # Image field for color representation
product_varients = models.ManyToManyField('Product', related_name='color_variants', blank=True)
class Product(models.Model):
pid = ShortUUIDField(length=10, max_length=100, prefix="prd", alphabet="abcdef")
user = models.ForeignKey(CustomUser , on_delete=models.SET_NULL, null=True)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, related_name="category")
color = models.ManyToManyField(Color, blank=True)
View Function:
def product_varient_detail_view(request, pid):
try:
product_variant = Product.objects.get(pid=pid)
except Product.DoesNotExist:
return render(request, "core/error_page.html")
wishlist = wishlist_model.objects.filter(user=request.user) if request.user.is_authenticated else None
sub_category = SubCategory.objects.all()
categories = Category.objects.prefetch_related('subcategories').order_by('?')[:4]
nav_category = Category.objects.filter(special_category=True).prefetch_related('subcategories').order_by('?')[:4]
# Initialize variables
colors = product_variant.color_variants.all() # Get the colors of the product variant
linked_colors = set() # Use a set to avoid duplicates
# Get linked colors
for color in colors:
for linked_product in color.product_varients.exclude(id=product_variant.id): # Exclude current product
linked_colors.update(linked_product.color_variants.all()) # Update the set with linked product colors
# Convert linked_colors back to a list for the template
linked_colors = list(linked_colors)
sizes = product_variant.size.filter(product=product_variant)
p_image = product_variant.p_images.filter(product=product_variant)
# Getting review
reviews = ProductReview.objects.filter(product=product_variant)
review_count = reviews.count()
average_rating = ProductReview.objects.filter(product=product_variant).aggregate(rating=Avg('rating'))
# Product Review Form
review_form = ProductReviewForm()
if request.GET.get('color'):
color_id = request.GET.get('color')
color = Color.objects.get(coid=color_id)
product_varient = color.product_varients.first()
if product_varient:
return redirect('core:product_varient_detail', pid=product_varient.pid)
try:
product_p = Product.objects.get(pid=pid)
context1 = {'product_p': product_p}
if request.GET.get('size'):
size_name = request.GET.get('size')
size_object = Size.objects.get(name=size_name)
price = product_variant.get_product_price_by_size(size_object)
context1['selected_size'] = size_name
context1['updated_price'] = price
except Exception as e:
print(e)
return render(request, "core/error_page.html")
main_product_color = product_variant.main_product_color
context = {
"p": product_variant,
"w": wishlist,
"p_image": p_image,
"sub_category": sub_category,
"categories": categories,
"nav_category": nav_category,
'colors': colors,
'linked_colors': linked_colors,
'sizes': sizes,
'reviews': reviews,
'review_count': review_count,
'average_rating': average_rating,
'review_form': review_form,
'main_product_color': main_product_color,
}
context.update(context1)
return render(request, "core/product_varient_detail.html", context)
Template Code:
{% if colors %}
{% for c in colors %}
<div class="color__radio">
<input type="radio" id="color_{{ c.id }}" name="color"
data-image-url="{{ c.image.url }}"
class="color-input"
data-url="{% url 'core:product_varient_detail' c.product_varients.first.pid %}?color={{ c.coid }}">
<label class="color__radio-label"
for="color_{{ c.id }}"
style="background-color: {{ c.code }};"></label>
</div>
{% endfor %}
{% endif %}
{% if linked_colors %}
<h3>Linked Product Colors</h3>
{% for c in linked_colors %}
<div class="color__radio">
<input type="radio" id="linked_color_{{ c.id }}" name="linked_color"
data-image-url="{{ c.image.url }}"
class="color-input"
data-url="{% url 'core:product_varient_detail' c.product_varients.first.pid %}?color={{ c.coid }}">
<label class="color__radio-label"
for="linked_color_{{ c.id }}"
style="background-color: {{ c.code }};"></label>
</div>
{% endfor %}
{% endif %}
The issue I'm facing is that when I click on a specific color, the page only shows that color instead of all available colors for the product variant. How can I modify my code to ensure that all colors are displayed on the product variant detail page, regardless of which color I click on?
To ensure that all colors for the product variant are displayed, you'll need to adjust the logic that filters products based on the selected color. Currently, when a color is clicked, you are redirecting to the product variant associated with that color, which limits the displayed colors to only those linked to the clicked color variant.
Here's the modified code snippet. (You can add your own logic)
def product_varient_detail_view(request, pid):
try:
product_variant = Product.objects.get(pid=pid)
except Product.DoesNotExist:
return render(request, "core/error_page.html")
wishlist = wishlist_model.objects.filter(user=request.user) if request.user.is_authenticated else None
sub_category = SubCategory.objects.all()
categories = Category.objects.prefetch_related('subcategories').order_by('?')[:4]
nav_category = Category.objects.filter(special_category=True).prefetch_related('subcategories').order_by('?')[:4]
colors = product_variant.color_variants.all()
selected_color = None
if request.GET.get('color'):
color_id = request.GET.get('color')
selected_color = Color.objects.get(pk=color_id)
sizes = product_variant.size.filter(product=product_variant)
p_image = product_variant.p_images.filter(product=product_variant)
reviews = ProductReview.objects.filter(product=product_variant)
review_count = reviews.count()
average_rating = ProductReview.objects.filter(product=product_variant).aggregate(rating=Avg('rating'))
review_form = ProductReviewForm()
context = {
"p": product_variant,
"w": wishlist,
"p_image": p_image,
"sub_category": sub_category,
"categories": categories,
"nav_category": nav_category,
'colors': colors,
'selected_color': selected_color, # Add selected color to context
'sizes': sizes,
'reviews': reviews,
'review_count': review_count,
'average_rating': average_rating,
'review_form': review_form,
}
return render(request, "core/product_varient_detail.html", context)
This is the modified template.
{% if colors %}
{% for c in colors %}
<div class="color__radio">
<input type="radio" id="color_{{ c.id }}" name="color"
data-image-url="{{ c.image.url }}"
class="color-input"
data-url="{% url 'core:product_varient_detail' product_variant.pid %}?color={{ c.id }}"
{% if selected_color and selected_color.id == c.id %}checked{% endif %}>
<label class="color__radio-label"
for="color_{{ c.id }}"
style="background-color: {{ c.code }};"></label>
</div>
{% endfor %}
{% endif %}