Can't get uploads to work in template


#1

@limedaring Hi Tracy, I cannot seem to get the images to work from your example in your IC book. I can upload images in admin and it creates the folder, so it seems everything works well, I just can’t get it to work in my template. I named my upload class differently than in book since I needed to provide upload classes for a few separate Models, so naming them differently was needed - not sure if this is part of my issue. Can you please take a peak at my code below and see if there is anything that stands out? Thank you for your help.

model

class Tool(Timestamp):
    model_number = models.ForeignKey(ModelNumber)
    price = models.SmallIntegerField()
    title = models.CharField(max_length=250)
    slug = models.SlugField()
    description = RichTextField()
    type = models.ForeignKey(Type)
    category = models.ForeignKey(Category)
    supplies = models.ManyToManyField(Part, blank=True)

    class Meta:
        verbose_name = 'Tool'
        verbose_name_plural = 'Tools'

    def __str__(self):
        return self.title

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


# Def for image path
def tool_image_path(instance, filename):
    return '/'.join(['tool_images', instance.tool.slug, filename])


# Upload class for Tools
class ToolUpload(models.Model):
    tool = models.ForeignKey(Tool, related_name="uploads")
    image = models.ImageField(upload_to=tool_image_path)

    class Meta:
        verbose_name = 'Tool Image Upload'
        verbose_name_plural = 'Tool Image Uploads' 

view

def tool_list(request):
    tools = Tool.objects.all()
    parts = Part.objects.all()
    tool_uploads = tools.uploads.all()
    return render(request, 'tool_list.html', {'tools': tools, 'parts': parts, 'tool_uploads': tool_uploads})

It generates an error in the browser as 'QuerySet' object has no attribute 'uploads'


#2

The problem is that tools is a Queryset — you can’t get uploads for all the tools off of a set of objects, only an individual object. So this would work in the view:

tool = Tool.objects.all()[0] # getting the first item in the set
tool_uploads = tool.uploads.all()

Buuuut you’re probably looking to get the uploaded image for every tool and display it on a page, right? So you’ll probably want to do that in the template. First off, read Chapter 13 (Database Pitfalls), if you haven’t already, and make sure to prefetch the uploads in the view:

views.py

tools = Tool.objects.prefetch_related('uploads').all()

And then loop through the uploads in the template to display them:

template.html

{% for tool in tools %}
    {% for upload in tool.uploads.all %}
    <img src="{{ upload.image.url }}" alt="" />
    {% endfor %}
{% endfor %}

Let me know if that answers your question! :)


#3

That worked great this way, thank you!!! Can you help me serve it as a background image - the below doesn’t work:

{% for upload in tool.uploads.all %}
     <div class="c-bg-img-center c-overlay-object" data-height="height" style="height: 230px; background-image: url({{ upload.image.url }});"></div>
 {% endfor %}

#4

What does the outputted HTML look like? Is it putting the URL in, just not the correct one?


#5

Ok, it works if I wrap it with the {% for tool in tools %} {% endfor %} - but I already had a for loop swrapping the entire page so I didn’t think I should need another, and I guess still a little confused why I would need the extra for statement. Thank you for your help…I burnt 4 hours yesterday trying to figure out how to do this…TIL :)


#6

Yeah sometimes you’ll need nested loops. :) Glad this helped!