Skip to content

Instantly share code, notes, and snippets.

Last active Jun 26, 2021
What would you like to do?
Django widget to replace the file selection standard button by an image preview. Tested with Django 1.8.5
from django.forms import ModelForm, FileField
from .models import Campaign
from .widgets import ImagePreviewWidget
class DesignCampaignForm(ModelForm):
brand_logo = FileField(widget=ImagePreviewWidget)
class Meta:
model = Campaign
fields = ['brand_logo', 'brand_description']
/* Hide the input element for the image-preview widgets.
The "open file" dialog will be triggered by a click on the image preview */
input[type="file"].image-preview {
display: none;
function ImageRefresher() {
function init(inputId) {
var that = this;
this.inputId = '#' + inputId;
this.imgId = '#' + inputId + '_img';
this.origData = $(this.imgId).attr('src');
function readURL(input) {
var that = this;
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
} else {
$(this.imgId).attr('src', this.origData);
ImageRefresher.prototype.init = init;
ImageRefresher.prototype.readURL = readURL;
window.onload = function() {
$('.image-preview').each(function(index) {
var refresher = new ImageRefresher();
from django.forms.widgets import ClearableFileInput, Input, CheckboxInput
from django.utils.safestring import mark_safe
from django.utils.html import conditional_escape, format_html, html_safe
class ImagePreviewWidget(ClearableFileInput):
'''Basically a ClearableFileInput widget, but:
- remove from the template most clutter: we leave only the image, the clear and the <input>
- we define a class for the input, so that we can hide it with CSS
# The "for" in the <label> allows to open the "open file" dialog by clicking on the image, no js involved
template_with_initial = (
'<label for=%(id_for_label)s><img id="%(img_id)s" src="/media/%(initial)s" width="100px"></label>'
'%(clear_template)s<br />%(input)s'
INPUT_CLASS = 'image-preview' # This is the class of the <input> element, which we want to hide
def __init__(self, attrs=None):
super(ImagePreviewWidget, self).__init__(attrs)
self.attrs['class'] = self.INPUT_CLASS
# Override ClearableFileInput:render
def render(self, name, value, attrs=None):
id_for_label = self.id_for_label(attrs.get('id'))
substitutions = {
'initial_text': self.initial_text,
'input_text': self.input_text,
'clear_template': '',
'clear_checkbox_label': self.clear_checkbox_label,
# We need the id, so that clicking in the image triggers the "Open file" native window
'id_for_label': id_for_label,
'img_id': id_for_label + '_img',
template = '%(input)s'
substitutions['input'] = Input.render(self, name, value, attrs) # call Input.render directly
if self.is_initial(value):
template = self.template_with_initial
if not self.is_required:
checkbox_name = self.clear_checkbox_name(name)
checkbox_id = self.clear_checkbox_id(checkbox_name)
substitutions['clear_checkbox_name'] = conditional_escape(checkbox_name)
substitutions['clear_checkbox_id'] = conditional_escape(checkbox_id)
substitutions['clear'] = CheckboxInput().render(checkbox_name, False, attrs={'id': checkbox_id})
substitutions['clear_template'] = self.template_with_clear % substitutions
return mark_safe(template % substitutions)
class Media:
css = {
'all': ('css/image-preview-widget.css',)
js = ('js/image-preview-widget.js', )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment