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

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

 
 
Логотип GITA-DEV

Автор: Черноусов Антон aka Gita-Dev
Опубликовано: 18 Июн 2018 (последние правки 1 месяц)

admin content django

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

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

<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-фрэймворков.

Похожие статьи

Запуск Web-приложений Django в Production-режиме (UWSGI/Nginx)

Запуск Web-приложений Django в Production-режиме (UWSGI/Nginx)

Django-приложения довольно сильно отличаются от php-приложений как структурой проекта, так и методом запуска.  Django-приложения по методу запуска больше похожи на Java, чем на PHP и классические ASP-проекты. Сегодня мы будем строить классическую связку из python-приложения и web-сервера Nginx обслуживающего реверс-проксирование запросов к приложению и предоставление файлов из каталогов media и static. LXC-изоляция в моем случае используется для поддержания python-окружения проекта и именно таким способом я предпочитаю изолировать проекты, а виртуальное окружение я предпочитаю не использовать, так как пару раз уже были проблемы при переносе Django-проектов.


Отправка оповещений Django-приложения в приватный чат Rocket.Chat

Отправка оповещений Django-приложения в приватный чат Rocket.Chat

Вчера я подумал, что если я все же вернулся к использованию Rocket.Chat и он меня уже не так бесит как предыдущие версии, то можно настроить систему оповещений о событиях на сайте и сбоях в работе Django-приложения в приватный чат Rocket.Chat. Для Python быстро нашелся вполне работоспособный модуль который идеально подошел для отправки сообщений в приватные чаты.


Создание и публикация Django-приложения (Часть первая, создание типового Django-приложения)

Создание и публикация Django-приложения (Часть первая, создание типового Django-приложения)

Для тех кто знаком с Django-разработкой в этой статье вряд ли найдется, что то интересное, но для тех кто хотел бы понять как начать писать на Django/Python небольшие приложения думаю будет интересно, и дополнительно рассмотрим простой метод автоматизированного деплоя нашего приложения на продакшн-сервер. Эту статью я решил написать для самого себя в виде небольшой шпаргалки при запуске новых проектов, поэтому если что непонятно, пишите в комментариях и я попробую рассказать более подробно.


Логирование ошибок в Django-проекте на боевом сервере

Логирование ошибок в Django-проекте на боевом сервере

Как вы наверное знаете у Django Framework имеется отличная система отладки и вывода отладочной информации при ошибке WEB-приложения и включается режим отладки простым DEBUG = True. Естественно, что на продакшн (боевых) сервера такое поведение недопустимо и в случае сбоя клиенту отображается лишь страница 500 с соответствующим кодом возврата и конечно клиенту этого вполне достаточно, но нам то необходимо оперативно отреагировать на сбой web-приложения и принять меры.


Создание Sitemap.xml для Django-проектов

Создание Sitemap.xml для Django-проектов

Для чего нужен файл sitemap.xml я думаю объяснять не стоит и все кто знаком с web-разработкой понимают, что этот файл так же важен как и robots.txt. Если вы используете полноценную CMS, то там за вас всю работу уже проделали и например в Django CMS поддержка Sitemap.xml и Robots.txt есть что называется из коробки, но в чистых Django Framework-проектах все эти операции придется проделать самостоятельно.


Миграция WEB-приложения Django с версии 2.7 на 3.5 (на практическом примере)

Миграция WEB-приложения Django с версии 2.7 на 3.5 (на практическом примере)

Про консоль управления виртуализацией WebVirtCloud (бывший WebVirtManager) я уже как то рассказывал, но главной его проблемой как я уже сказал является то что он пострен на базе устаревшего Python2 и автор тащит его вперед именно в таком виде. Переписывать он его отказывается мотивируя это тем что все и так работает, но на самом деле там внутри довольно много легаси-мусора. Я его умудился немного переписать под свежую редакцию Django и Python3, но дело еще далеко до завершения хотя пользоваться уже можно.


Отзывы и комментарии