Использование jQuery Ajax для отправки данных Django-форм без обновления WEB-страницы

by Anton Chernousov aka GITA-DEV


Опубликовано: 18 Июн 2018 (последние правки 1 месяц, 2 недели)


Использование jQuery Ajax для отправки данных Django-форм без обновления WEB-страницы

Я последнее время довольно активно использую Java Script в своих проектах. 21-ый век все таки на дворе и надо соответствовать. Хотя почему то считается, что использовать чистый Ajax для работы с формами не стоит и необходимо предоставить возможность работы и с перезагрузкой страницы, но мне кажется, что врят ли сейчас кто-то пользуется IE6, ну и если и пользуется, это его персональное горе.

В одном из проектов, я использовал самодельную загрузку изображений без обновления страницы и хотя может показаться, что с виду, это довольно просто, но изнутри выглядит довольно интересно. 

Начнем с фронт-части:

<div style="height:30px;"></div>
<img id="blog-post-image-image" style="width:100%" class="img-responsive img-rounded" 
                          src="/static/images/no-picture.png" alt="Изображение для записи в блоге" />
<div style="height:20px;"></div>
<input id="blog-post-image-button" type="button" style="width:100%;" value="Загрузить изображение" />
<input type="file" name="blog-post-image-file-name" id="blog-post-image-file" style="display:none;"/> 

Я предоставил вам только блок HTML-разметки отвечающий за назначение главного изображения для записи в блоге.

Прикрепление изображения к записи в блоге без перезагрузки страницы

Как вы видите поле input отвечающее за диалог выбора файла скрыто стилем display:none, а вызов диалога выбора файла будет реализован нажатием на кнопку "Загрузить изображение" и сейчас мы опишем эту функциональную часть при помощи jQuery:

<script type="text/javascript">
    $("#blog-post-image-image").click(function(){
            $("#blog-post-image-file").focus().click();
    });   
    $("#blog-post-image-button").click(function(){
        $("#blog-post-image-file").focus().click();
    });    
</script>

Как вы видите, диалог выбора файла так же мы вызываем и при клике на само изображение. В этой части пока ничего сложного нет, единственное, что не забывайте о методе focus(), без него может не работать в ряде случаев. 

Итак, мы сделали вызов окна выбора файла и теперь хотелось бы при выборе локального файла, автоматически загружать его на сервер, а на стороне сервера, мы будем проверять что это действительно изображение и возвращать статус загрузки, далее при положительном статусе загрузки, мы в свою очередь будем менять атрибут src у нашего no-picture.png.

Начинаем с обработчика на стороне фронтенда и пока мы опишем его максимально упрощенно, оставив только функционал ajax-загрузки изображения и он отрабатывает при изменении скрытого файлового поля ввода:

<script type="text/javascript">
$("#blog-post-image-file").on('change',function(){
       var file_data = document.getElementById('blog-post-image-file').files[0];
       var form_data = new FormData();
       form_data.append("file", file_data);
       form_data.append("operation", 'blog-upload-blogimage');
       form_data.append("csrfmiddlewaretoken", $("input[name=csrfmiddlewaretoken]").val());

       $.ajax({
               url : '/portal-admin/blog/',
               type: "post",
       cache: false,
       timeout: 30000,
       processData: false,
       contentType: false,
       data: form_data,
       success: function(data){
                                console.log('== Image Uploaded ==');
                                   },
       });
});
</script>

Как вы видите здесь уже интереснее, чем простой клик по скрытому полю. Мы создаем временный объект FormData() который заполняем требуемыми данными и фоновым процессом отправляем для обработки на сервере. На сервере мы теперь в свою очередь должны принять POST-запрос который содержит изображение, декодировать его и сохранить.

if operation == 'blog-upload-blogimage':
    blog_image = ImageFile(request.FILES['file'])
    # Resize uploaded photo and save as PNG
    basewidth = 600
    img = Image.open(blog_image.file)
    wpercent = (basewidth/float(img.size[0]))
    hsize = int((float(img.size[1])*float(wpercent)))
    img = img.resize((basewidth,hsize), Image.ANTIALIAS)
    img.crop((0, 0, 600, 600))
   
    # Save image as hash
    hash_md5 = hashlib.md5()
    hash_md5.update(img.tobytes())
    filename_as_hash = hash_md5.hexdigest()
    save_file_name = settings.MEDIA_ROOT + '/hex-images/' + filename_as_hash + '.png'
    img.save(save_file_name)
    return {'result' : True,'file_name' : '/media/hex-images/' + filename_as_hash + '.png'}

На стороне сервера я сохраняю изображение во временное хранилище и именем файла назначаю md5-хэш этого файла и как вы видите в результате выполнения обработки возвращается путь к файлу на сервере и статус результата True. Следующим этапом хотелось бы вновь вернуться на фронт нашего web-приложения и модернизировать загрузчик картинок, добавив немного динамики на этапе обработки загрузки изображения.

<script type="text/javascript">

function UploadSucess() {
       $("#upload-success").show();
       $("#upload-failed").hide();
       setTimeout( function(){
           $("#upload-success").hide();
         }  , 4000 );        
}

function UploadFail(source_image) {
    $("#blog-post-image-image").attr('src', source_image);
       $("#upload-success").hide();
       $("#upload-failed").show();
       setTimeout( function(){
           $("#upload-failed").hide();
         }  , 4000 );        
}    

$("#blog-post-image-file").on('change',function(){
       var file_data = document.getElementById('blog-post-image-file').files[0];
       var form_data = new FormData();
       form_data.append("file", file_data);
       form_data.append("operation", 'blog-upload-blogimage');
       form_data.append("csrfmiddlewaretoken", $("input[name=csrfmiddlewaretoken]").val());

       var source_image = $("#blog-post-image-image").attr('src')
       $("#blog-post-image-image").attr('src', '/static/images/image-loader.gif');
       
       $.ajax({
               url : '/portal-admin/blog/',
               type: "post",
       cache: false,
       timeout: 30000,
       processData: false,
       contentType: false,
       data: form_data,
       error: function(jqXHR, textStatus, errorThrown) {
           UploadFail(source_image);
       },
       success: function(data){
                               if (data['result'])
                               {
                                  $("#blog-post-image-image").attr('src', data['file_name']);
                                  UploadSucess();  
                                   } else {
                                       UploadFail(source_image);
                                   }
                                   },
       });
});
</script>

Я использовал анимацию загрузки изображения (на этапе загрузки) и дополнительное сообщение о статусе загрузки, реализованные двумя дополнительными скрытыми bootstrap-сообщениями:

<div class="col-md-2">
    <div style="height:30px;"></div>
      <img id="blog-post-image-image" style="width:100%" class="img-responsive img-rounded" src="/static/images/no-picture.png" alt="Изображение для записи в блоге" />
       <div style="display:none;margin-bottom:0px;margin-top:10px;" id="upload-success"  class="alert alert-success">
                <strong>Успешно!</strong><br/> Изображение загружено.
</div>
         <div style="display:none;margin-bottom:0px;margin-top:10px;" id="upload-failed" class="alert alert-danger">
                 <strong>Ошибка!</strong><br/> Сбой загрузки.
    </div>    
<div style="height:10px;"></div>            
      <input id="blog-post-image-button" type="button" style="width:100%;" value="Загрузить изображение" />
      <input type="file" name="blog-post-image-file-name" id="blog-post-image-file" style="display:none;"/>         
</div> 

Как вы видите, можно реализовывать простое юзабилити и без тяжелых (для понимания) JavaScript-фрэймворков.


Есть вопросы?
Спрашивайте и я обязательно вам отвечу!

* Поля обязательные для заполнения .

Блог это некоммерческий проект! Если вам понравился мой блог и то что я пишу помогло вам на практике, то можете сказать спасибо материально.