DIsplay images using FileField in Django

Question:

I have a Django app which converts uploaded pdf files into images. The upload part and conversion part are working fine but I am having issues on displaying the converted images. I am using ModelForms to upload the pdf files using the FileField, converting them into .png and finally displaying them on a new page. If I used an ImageField, I won’t be able to upload pdf files. So I am using a FileField because that is the purpose of the app, to convert pdf to png!

models.py

from django.db import models
from django.contrib.auth.models import User

# Create your models here.

class UserUploadModel(models.Model):
    
    user = models.ForeignKey(User, on_delete = models.CASCADE, null = True)
    file = models.FileField(upload_to = 'file_uploads')

forms.py

from django import forms
from django.forms import ClearableFileInput
from app1.models import UserUploadModel

class UserUploadForm(forms.ModelForm):
    

    class Meta:

        model = UserUploadModel
        fields = ['file']
        widgets = {
            'file' : ClearableFileInput(attrs = {'multiple': True}),
        }

views.py

from django.shortcuts import render, redirect
from app1.forms import UserUploadForm
from app1.models import UserUploadModel
from app1.convert import convert_file
from app1.transfer import move_dir
import os
from project1 import settings
from django.contrib.auth.decorators import login_required

@login_required
def home(request):
    
    if request.method == 'POST':
        form = UserUploadForm(request.POST, request.FILES)
        
        if form.is_valid():
            f = form.save()
            f.user = request.user
            f.save()
          
            files = request.FILES.getlist('file')
                        
            f_list = []
            
            if files:
                for i in files:
                    file_instance = UserUploadModel(file = i)
                    file_instance.save()
                    f_list.append(file_instance.file.path)
                
                [convert_file(j) for j in f_list]
                
    
                src_dir = os.getcwd()
                dest_dir = os.path.join(src_dir, 'media/')            
                move_dir(src_dir, dest_dir, '*.png')

            
            return redirect('app1-display')
        
    else:
        
        form = UserUploadForm()
    
    return render(request, 'app1/home.html', {'form' : form})

@login_required
def display(request):
    
    return render(request, 'app1/display.html')

home.html

{%extends "app1/base.html"%}
{%block content%}

    <form method="POST" enctype="multipart/form-data">
      {%csrf_token%}
      {{form.as_p}}
      <input type="submit">
    </form>

{%endblock content%}

EDIT 1: the display.html is empty because I do not know how to display images stored in a directory. If I use ImageField, I can use the url of the image to display it but I am using FileField to upload, convert it to .png and then I store the images in a folder inside the BASE_DIR. Now how do I display the images stored in a folder in BASE_DIR?

Also I only want to show the images for the current logged in user

EDIT 2:
display.html

<!DOCTYPE>
<html>
<head>
    <title></title>
</head>
<body>

    {%if user.is_authenticated%}
        <h2>User: {{request.user.username}}</h2>
    {%endif%}

    {%for i in x%}
        <img src={{ i.file.url }}>
    {%endfor%}
    
</body>
</html>
Asked By: spectre

||

Answers:

You should first send all the images of current logged in user in the context then display it using .url in img tag.

views.py:

@login_required
def display(request):
    user_uploads=UserUploadModel.objects.filter(user=request.user)
    context={"user_uploads":user_uploads}
    
    return render(request, 'app1/display.html', context)

app1/display.html:

<body>
    {% if request.user.is_authenticated %}
        <h2>User : {{request.user.username}}</h2>
    {% endif %}
    <br><br>

    {% for user_upload in user_uploads %}
        <img src="{{user_upload.file.url}}" width='300px'>
        <br><br>
    {% endfor %}
</body>

Note: Models in Django doesn’t require model to be the suffix, so you may write the model name as UserUpload instead of UserUploadModel.

Additionally, I’d suggest to maintain proper gap between template tags such as it should be {% csrf_token %} not {%csrf_token%} and so on.

Answered By: Sunderam Dubey

I got it to work! The reason that duplicate images were being saved in directory was because I was saving the form form.save() which will first save the last of the 2 uploaded images (so 1 image saved!) and then I was saving the instance of each and every uploaded file (2 images saved!) which results in 3 images! Hence I changed my view to this:-

views.py

from django.shortcuts import render, redirect
from app1.models import UserUploadModel
from app1.forms import UserUploadForm
# Create your views here.

def home(request):
    
    if request.method=='POST':
        
        form = UserUploadForm(request.POST, request.FILES)
        
        if form.is_valid():            
            files = request.FILES.getlist('file')                
                
            if files:
                for i in files:
                    file_instance = UserUploadModel(file = i, user = request.user)
                    file_instance.save()
            
        return redirect('app1-display')
    
    else:
        
        form = UserUploadForm()
    
    return render(request, 'app1/home.html', {'form' : form})


def display(request):
    
    x = UserUploadModel.objects.filter(user = request.user)

    context = {
        'x' : x
        }
    
    return render(request, 'app1/display.html', context)

display.html

<!DOCTYPE>
<html>
<head>
    <title></title>
</head>
<body>

    {%if user.is_authenticated%}
        <h2>User: {{request.user.username}}</h2>
    {%endif%}

    {%if x%}
        {%for i in x%}
            <img src={{ i.file.url }}>
        {%endfor%}
    {%endif%}
</body>
</html>

This will allow user to upload multiple images and display all of them!
Thanks to @Sunderam Dubey for helping me out with the views.py.

Answered By: spectre