Home > other >  Images are uploaded into S3 but URL within django application is not displaying correctly
Images are uploaded into S3 but URL within django application is not displaying correctly

Time:07-23

Problem

I am able to upload images into S3, however the structure of the URL within my django app is not structured properly.
The URL appears like this: http://localhost:8000/plants/https://'%s.s3.amazonaws.com'%AWS_STORAGE_BUCKET_NAME/media/plant_images/image.jpeg
However, I want it to appear as enter image description here

Context
I have a model that allows me to upload multiple images. I think I need to rewrite the way I call for the image within my template, or within my model. After reading through Django documentation and other stackoverflow questions, I'm not sure how to resolve this issue.

settings.py/base.py

# MEDIA
# ------------------------------------------------------------------------------
USE_S3 = os.getenv('USE_S3') == 'TRUE'

AWS_ACCESS_KEY_ID = config('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = config('AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = config('AWS_STORAGE_BUCKET_NAME')
AWS_QUERYSTRING_AUTH = config('AWS_QUERYSTRING_AUTH')
AWS_S3_CUSTOM_DOMAIN = config('AWS_S3_CUSTOM_DOMAIN')
AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',
}
AWS_LOCATION = config('AWS_LOCATION')
PUBLIC_MEDIA_LOCATION = 'media'
AWS_QUERYSTRING_AUTH = False

MEDIA_URL = 'https://%s/%s/' % (AWS_S3_CUSTOM_DOMAIN, AWS_LOCATIN)
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

The section of my template that is calling for the URL {{ image.images.url }}

{% for image in plant.plant_images.all %}
{% if forloop.first %}
<div >
{% else %}
<div >
{% endif %}
    <img src="{{ image.images.url }}" alt="{{ plant.name }}">
</div>
{% endfor %}
</div>

models.py

import datetime

from django.conf import settings
from django.db import models
from django.utils import timezone
from django.template.defaultfilters import slugify
from django.urls import reverse
from django_quill.fields import QuillField

# Create your models here.

class Plant(models.Model):
    name = models.CharField(max_length=120)
    slug = models.SlugField(null=False, unique=True)
    description = models.TextField()

    def __str__(self):
        return self.name

    def publish(self):
        self.published_date = timezone.now()
        self.save()

    def get_absolute_url(self):
        return reverse("plant_detail", kwargs={"slug": self.slug})

class PlantImage(models.Model):
    plant = models.ForeignKey(Plant, default=None, on_delete=models.CASCADE, related_name="plant_images")
    images = models.ImageField(upload_to = 'plant_images/')
 
    def __str__(self):
        return self.plant.name

views.py

from .models import Plant, PlantImage

def plant_index(request):
    plant_objects = Plant.objects.all()
    context = {'plant_objects': plant_objects}
    return render(request, 'plants/plant_index.html', context)

class PlantDetailView(DetailView):
    model = Plant
    template_name = 'plants/plant_detail.html'
    slug = 'slug'

    def get_context_data(self, **kwargs):
        context = super(PlantDetailView, self).get_context_data(**kwargs)
        context['plant_images'] = PlantImage.objects.all()
        return context

plant_detail = PlantDetailView.as_view()

I can see the images are successfully being stored in s3 after I upload them via my Django backend UI, however the URL is not allowing me to display the images on the page correctly. enter image description here

CodePudding user response:

You can use boto3 config to upload your images and that will not add http://localhost:8000/plants in your base url of image.

from storages.backends.s3boto import S3BotoStorage

class PublicMediaStorage(S3Boto3Storage):
    location = "media"
    default_acl = "public-read"
    file_overwrite = False
    custom_domain = False

and use the above class in your model using storage params of django model

class PlantImage(models.Model):
    plant = models.ForeignKey(Plant, default=None, on_delete=models.CASCADE, related_name="plant_images")
    images = models.ImageField(storage=PublicMediaStorage() ,upload_to = 'plant_images/')
 
    def __str__(self):
        return self.plant.name
  • Related