3

I am currently programatically importing products into my product catalog, however, I am having difficulty uploading images for each product. Here's what my code looks like:

# frobshop/custom_utils/product_importer.py

from oscar.apps.catalogue.models import Product
...

for product in my_product_import_list:
    ...
    product_entry = Product()
    product_entry.title = my_product_title
    product_entry.product_class = my_product_class
    product_entry.category = my_category
    product_entry.primary_image = "product_images/my_product_filename.jpg"
    product_entry.save()

The details such as product title and product class are successfully saved when I check using the development server, however, I am unsure how to set an image for each product.

The entire product_images folder was initially located outside of my media directory, but since I wasn't getting any results, I copy pasted the entire folder of my images into the media directory but still with no results. I am assuming that quite a number of steps are being skipped, and maybe there's a convention on how to arrange images in the media directory. However, I am not sure where to find these steps and conventions.

Here's the portion of my settings.py file that deals with my installed apps, the media directory, and static files:

# frobshop/frobshop/settings.py

...

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',

    'django.contrib.sites',

    'django.contrib.messages',
    'django.contrib.staticfiles',

    'django.contrib.flatpages',

    'compressor',
    'widget_tweaks',
] + get_core_apps(['custom_apps.shipping', 'custom_apps.payment', 'custom_apps.checkout'])

...


STATIC_ROOT = 'static'
STATIC_URL = '/static/'
MEDIA_ROOT = 'media'
MEDIA_URL = '/media/'

For further clarity, here's my urls.py

from django.contrib import admin
from django.urls import path
from django.conf.urls import include, url
from oscar.app import application
from django.conf import settings
from django.conf.urls.static import static


urlpatterns = [
    url(r'^i18n/', include('django.conf.urls.i18n')),
    path('admin/', admin.site.urls),
    url(r'', application.urls),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
B B
  • 1,116
  • 2
  • 8
  • 20

2 Answers2

5

I think you can do it like this using File class:

from django.core.files import File

for product in my_product_import_list:
    ...
    product_entry = Product()
    product_entry.title = my_product_title
    product_entry.product_class = my_product_class
    product_entry.category = my_category
    image = File(open("product_images/my_product_filename.jpg", 'rb'))
    product_entry.primary_image = image
    product_entry.save()

Update

probably you should use OSCAR_MISSING_IMAGE_URL in settings:

OSCAR_MISSING_IMAGE_URL = "product_images/my_product_filename.jpg"  # relative path from media root

Alternatively, you can use ProductImage, like this:

    from oscar.apps.catalogue.models import ProductImage

    product_entry = Product()
    product_entry.title = my_product_title
    product_entry.product_class = my_product_class
    product_entry.category = my_category
    product_entry.save()
    product_image = ProductImage()
    image = File(open("product_images/my_product_filename.jpg", 'rb'))
    product_image.original = image
    product_image.caption = "Some Caption"
    product_image.product = product_entry
    product_image.save()

As ProductImage has a ForignKey relation to Product Model, and primary_image is a method in Product model, which takes image from ProductImage model, and returns the first one(ProductImage objects ordered by display_order field in that field)

ruddra
  • 50,746
  • 7
  • 78
  • 101
  • Thanks for the answer. I tried this, but the product images still do not show in the django-oscar admin dashboard. I'm still getting the "Image Not Available" placeholder. – B B Jun 19 '19 at 03:35
  • It probably is. I am using a relative path, and I have copy pasted the image folder onto the 1) project root 2) beside the import script 3) under the media folder 4) under the media/images folder. By the way, I am using an unmodified version of the django-oscar Product class – B B Jun 19 '19 at 03:48
  • I tested the code using relative path from project root. – ruddra Jun 19 '19 at 04:03
  • 1
    @BB I have also added code snippet on how to use Product Image – ruddra Jun 19 '19 at 04:27
  • 1
    Thanks! `ProductImage` worked like a charm. The images are now showing up in the dashboard and in the site. Setting the `OSCAR_MISSING_IMAGE_URL` in settings.py also allowed me to set a custom placeholder image for missing images. – B B Jun 19 '19 at 05:49
0

Not entirely sure how helpful this will be to anyone, but I was getting a SuspiciousFileOperation Exception when using @ruddra's answer here, even though I was using a tempfile within the media root...

File "/Users/user/Documents/project/venv/lib/python3.7/site-packages/django/core/files/utils.py", line 19, in validate_file_name
    "Detected path traversal attempt in '%s'" % name
django.core.exceptions.SuspiciousFileOperation: Detected path traversal attempt in '/Users/user/Documents/project/media/images/tmp6xem6hvs'

Fixed by first saving the image (got the idea from this SO answer), then saving the product image:

import tempfile
from django.core.files.images import ImageFile
...
lf = tempfile.NamedTemporaryFile(dir='media')
f = open('<full_path_to_project>/media/images/my_product_filename.jpg', 'rb')
lf.write(f.read())
image = ImageFile(lf)
product_image = ProductImage()
product_image.caption = "some caption"
product_image.product = product_entry
product_image.original = image
product_image.original.save('my_product_filename.jpg', image) # <-----
product_image.save()
...
kt-0
  • 357
  • 3
  • 10