Repository: django-oscar/django-oscar

Issue:
Image not available for variants in basket
If a variant's parent has images the basket still shows a "Image Not Available" placeholder for all variants.

The expected behavior would be for the variant to default to the parents image.

Here's a workaround in my app:

``` python
class Product(AbstractProduct):

    def primary_image(self):
        # Hack to make primary image look at the parent if it's a child

        if self.structure == self.CHILD:
            product = self.parent
        else:
            product = self

        return super(Product, product).primary_image()
```

I can submit a pull request with a proper patch if you want.



Discussion:
https://github.com/john-parton/django-oscar/tree/issue/675/per_variant_image_support
has explicit handling for variant images, and fixes this properly, instead of just a simple workaround.

It was decided by the team that we would stick with approach, proposed by @mvantellingen - https://github.com/django-oscar/django-oscar/pull/1643#issuecomment-97575119 - allow to upload images for each variant and fallback to the parent product image.

Resolving patch:
diff --git a/src/oscar/apps/catalogue/abstract_models.py b/src/oscar/apps/catalogue/abstract_models.py
--- a/src/oscar/apps/catalogue/abstract_models.py
+++ b/src/oscar/apps/catalogue/abstract_models.py
@@ -569,12 +569,17 @@ def get_missing_image(self):
         # field.
         return MissingProductImage()
 
+    def get_all_images(self):
+        if self.is_child and not self.images.exists():
+            return self.parent.images.all()
+        return self.images.all()
+
     def primary_image(self):
         """
         Returns the primary image for a product. Usually used when one can
         only display one product image, e.g. in a list of products.
         """
-        images = self.images.all()
+        images = self.get_all_images()
         ordering = self.images.model.Meta.ordering
         if not ordering or ordering[0] != 'display_order':
             # Only apply order_by() if a custom model doesn't use default
@@ -584,18 +589,13 @@ def primary_image(self):
         try:
             return images[0]
         except IndexError:
-            if self.is_child:
-                # By default, Oscar's dashboard doesn't support child images.
-                # We just serve the parents image instead.
-                return self.parent.primary_image()
-            else:
-                # We return a dict with fields that mirror the key properties of
-                # the ProductImage class so this missing image can be used
-                # interchangeably in templates.  Strategy pattern ftw!
-                return {
-                    'original': self.get_missing_image(),
-                    'caption': '',
-                    'is_missing': True}
+            # We return a dict with fields that mirror the key properties of
+            # the ProductImage class so this missing image can be used
+            # interchangeably in templates.  Strategy pattern ftw!
+            return {
+                'original': self.get_missing_image(),
+                'caption': '',
+                'is_missing': True}
 
     # Updating methods
 


Test patch:
diff --git a/tests/integration/catalogue/test_product_image.py b/tests/integration/catalogue/test_product_image.py
--- a/tests/integration/catalogue/test_product_image.py
+++ b/tests/integration/catalogue/test_product_image.py
@@ -21,6 +21,24 @@ def test_images_are_in_consecutive_order(self):
         for idx, im in enumerate(product.images.all()):
             self.assertEqual(im.display_order, idx)
 
+    def test_variant_images(self):
+        parent = factories.ProductFactory(structure='parent')
+        variant = factories.create_product(parent=parent)
+        factories.create_product_image(product=variant, caption='Variant Image')
+        all_images = variant.get_all_images()
+        self.assertEquals(all_images.count(), 1)
+        product_image = all_images.first()
+        self.assertEquals(product_image.caption, 'Variant Image')
+
+    def test_variant_images_fallback_to_parent(self):
+        parent = factories.ProductFactory(structure='parent')
+        variant = factories.create_product(parent=parent)
+        factories.create_product_image(product=parent, caption='Parent Product Image')
+        all_images = variant.get_all_images()
+        self.assertEquals(all_images.count(), 1)
+        product_image = all_images.first()
+        self.assertEquals(product_image.caption, 'Parent Product Image')
+
 
 class TestMissingProductImage(StaticLiveServerTestCase):