I am building a ecommerse store with django. One of the requirement is that a product can have a color and different sizes. I also want to keep track of the quantity of each size.
I can create ProductVariation like product=’test’, size=’small’, color=’red’, quantity=5 , product=’test’, size=’large’, color=’red’, quantity=10. Now in db there are two objects created,how can I query that I only get color red in queryset when I am rendering product details.
I looked at this example – Django Products model for e-commerce site
but did not understand this bit –
Color.objects.filter(productvariant__product=some_product).distinct()
These are my models:
class Product(models.Model):
"""
Product model to allow creating product records and linking with category
"""
product_uuid = models.UUIDField(
default=uuid.uuid4, editable=False, unique=True)
product_name = models.CharField(max_length=255)
sub_category = models.ForeignKey(
'SubCategory', on_delete=models.CASCADE, related_name='products')
product_description = models.TextField()
is_featured = models.BooleanField(default=False)
product_sku = models.CharField(max_length=255, null=True, blank=True)
product_price = models.DecimalField(max_digits=6, decimal_places=2)
def __str__(self):
return self.product_name
class Meta:
verbose_name = 'Product'
verbose_name_plural = 'Products'
class ProductVariation(models.Model):
"""
ProductVariation model to allow creating product with different size and color
"""
product_var_uuid = models.UUIDField(
default=uuid.uuid4, editable=False, unique=True)
product = models.ForeignKey(
'Product', on_delete=models.CASCADE, related_name='product_variations')
size = models.ForeignKey('Size', on_delete=models.CASCADE, related_name="product_sizes")
color = models.ForeignKey('Color', on_delete=models.CASCADE, related_name="color")
quantity = models .IntegerField()
def __str__(self):
return self.product.product_name
class Meta:
constraints = [
models.UniqueConstraint(
fields=['product', 'color', 'size'],
name='unique_prod_color_size_combo'
)
]
verbose_name = 'Product Variation'
verbose_name_plural = 'Product Variations'
class Size(models.Model):
product_size_uuid = models.UUIDField(
default=uuid.uuid4, editable=False, unique=True)
product_size = models.CharField(max_length=20)
def __str__(self):
return self.product_size
class Meta:
verbose_name = 'Product Size'
verbose_name_plural = 'Product Sizes'
class Color(models.Model):
product_color_uuid = models.UUIDField(
default=uuid.uuid4, editable=False, unique=True)
product_color = models.CharField(max_length=20)
def __str__(self):
return self.product_color
class Meta:
verbose_name = 'Product Color'
verbose_name_plural = 'Product Colors'
>Solution :
What we do here is filter by joining on the ProductVariant and then on the Product, so with a query:
Color.objects.filter(color__product=some_product).distinct()
but this is because the related_name of color in ProductVariation is color which makes not much sense. That should be something like:
class ProductVariation(models.Model):
# …
color = models.ForeignKey(
'Color', on_delete=models.CASCADE, related_name='product_variants'
)
# …
since it is the name of the relation in reverse. In that case we filter with:
Color.objects.filter(product_variants__product=some_product).distinct()
This will then make a query that looks like:
SELECT DISTINCT color.*
FROM color
INNER JOIN productvariation ON color.id = productvariation.color_id
WHERE productvariation.product_id = some_product_id