Django - Display Image in Template from form ( ImageField ) without saving to Model

1.1k Views Asked by At

I have been struggling with this for a couple of days. I am trying to create an html page in which the user imports an image and resizes it ( all of this without saving anything to a model ). I want to do 3 main things:

  1. After the user selects his image, to have the image displayed even if the form has not been submittedand to display the current width and height of the image.
  2. To be able to access and modify those width and height values and to change the values.
  3. After "submit" has been sent, to have on the same page the transformed image and to be able to download.

Also, I am a beginner, this being my first project. I would much appreciate if you can share some of your knowledge and leave me some tips :)

forms.py

class ImageForm(forms.Form):
    image = forms.ImageField()

views.py

def image_resize(request):
    form = forms.ImageForm()
    if request.method == "POST":
        form = forms.ImageForm(request.POST or None, request.FILES or None)
        if form.is_valid():
            image_object = form.cleaned_data['image']
            w, h = image_object.image.width, image_object.image.width
            print(w, h)
        else:
            form = forms.ImageForm(request.POST or None, request.FILES or None)
    context = {'form':form}
    return render(request, 'images/image-grayscale.html', context)

Let me know if something else is needed to make your life easier with this

1

There are 1 best solutions below

0
viciousvegs On

I'm a beginner too, but I've been able to make the first two steps somewhat (except the "image displayed even if the form has not been submitted") with cobbled together code from stackoverflow. Just wanted to share so far and in case I don't continue working on this, maybe you can make something of it.

My thoughts on the missing steps: "image displayed, even if the form has not been submitted" I'm thinking using ajax there to submit the form onchange(js), so it only looks like it hasn't been submitted, or, if possible, sending the img data to an iframe and changing the values outside the iframe and again submitting with ajax.

The project: I thought about temporary storage or cron/celery stuff, but then decided to try with data uri I think it's called. I'm not changing the images height/width, but using style="" to showcase what it would look like at those dimensions. (fun thought to try: proportional height/width editing). I'm using PIL to get the dimensions of the image and the changed dimensions of the image.

//--- html ---//

 <head><script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    </head>
    <body>

<h2>Image upload</h2>
<form method="post" enctype='multipart/form-data'> 
    {% csrf_token %}
    {{ form }}
    <label for="fheight">Wanted height</label>
    <input id="fheight" name="fheight" value="" type="number" min="50" max="1000">
    <label for="fwidth">Wanted width</label>
    <input id="fwidth" name="fwidth" value="" type="number" min="50" max="700">
    <input type="submit" value="Submit">
</form>

# Display image 
{% if image %}
<span>Height: {{height}}</span>
<span>Width: {{width}}</span>
<img src="{{ image }}" alt="img" style="top:10em; width: {{width}}px; height: {{height}}px; max-width: 700px; max-height: 1000px;">

{% endif %}

//--- views---//

from .forms import FilePreviewForm
from base64 import b64encode 

from PIL import Image
def preview_image(request):
    form = FilePreviewForm(request.POST, request.FILES)
    if form.is_valid():
        fheight = request.POST.get('fheight')
        fwidth = request.POST.get('fwidth')
        
        file = request.FILES['image']
        data = file.read()
        encoded = b64encode(data).decode()
        mime = 'image/jpeg;'
        imagep = Image.open(file)
        width, height = imagep.size
        if fheight or fwidth: 
            size = None
            if fheight:
                size = (int(width), int(fheight))
            if fwidth:
                size = (int(fwidth), int(height)) 
            if fheight and fwidth:
                size = (int(fwidth), int(fheight)) 
            image = imagep.resize(size, Image.ANTIALIAS)
            width, height = image.size
        context = {"image": "data:%sbase64,%s" % (mime, encoded), 'form': form, 'width':width, 'height':height}
        return render(request, 'image_preview.html', context)
    else:
        form = FilePreviewForm()
    return render(request, 'image_preview.html', {'form': form})



//---forms---//

    image = forms.ImageField(help_text="Upload image: ", required=True)

Hope it's helpful. Ask if you have questions. Good luck! I'll share more, if I continue.