Django предоставляет отличные средства для быстрого построения back-end без лишних телодвижений. Однако создание на его основе галлереи связано с множеством ручной работы по загрузке каждого файла в отдельности.
Решений у этой проблемы несколько:
Я выбрал последний вариант, так как в этом случае нужно внести минимум изменений. Все операции выполняются на клиенте. Использовать для этого будем swfuploader.
Задумка проста, модифицируем с��андартный интерфейс добавления, так что бы перехватывать событие сохранения объекта. Заполненные поля будем использовать в качестве шаблона для создания множества объектов для выбранных файлов.
Итак выполним следующие действия.
Готово, теперь наши пользователи могут грузить фотки пачками. Можно пойти дальше — добавить прогресс бар и прочие навороты, но в моем случае в этом не было необходимости.
Ограничения:
В идеале хотелось что бы:
На всякий случай демка.
Решений у этой проблемы несколько:
- загрузка архива и его распаковка на сервере,
- использование специального поля,
- имитация множественных post-запросов.
Я выбрал последний вариант, так как в этом случае нужно внести минимум изменений. Все операции выполняются на клиенте. Использовать для этого будем swfuploader.
Задумка проста, модифицируем с��андартный интерфейс добавления, так что бы перехватывать событие сохранения объекта. Заполненные поля будем использовать в качестве шаблона для создания множества объектов для выбранных файлов.
Итак выполним следующие действия.
- Выкачиваем набор примеров swfupload: http://swfupload.googlecode.com/files/SWFUpload%20v2.2.0.1%20Samples.zip.
- из него нам падобятся следующие файлы:
- swfupload/swfupload.js (js кусок ядра);
- swfupload/swfupload.swf (flash кусок ядра);
- swfupload/swfupload.queue.js (реализация очереди);
- formsdemo/XPButtonUploadText_61x22.png (замена кнопки).
- Кладем их в медиа директорию djangosite/media/js/swfupload.
- Прописываем js в djangosite/gallery/admin.py:
from django.contrib import admin
from djangosite.gallery.models import Image
class ImageAdmin(admin.ModelAdmin):
class Media:
js = (
'/media/js/jquery.min.js',
'/media/js/swfupload/swfupload.js',
'/media/js/swfupload/swfupload.queue.js',
'/media/js/swfupload/swfupload.cookies.js',
'/media/js/multiupload.js',
)
admin.site.register(Image,ImageAdmin)
* This source code was highlighted with Source Code Highlighter. - Создаем управляющий js-код djangosite/media/js/multiupload.js
jQuery().ready(function(e){<br> DjangoMultiUpload.init();<br>});<br><br><br>var DjangoMultiUpload = new function(){<br> <br> // Общие переменные<br> this.swfu = null;<br> this.gui_submit = null;<br> this.old_upload = null;<br> this.busy = false;<br> <br> // Параметры для swfupload<br> this.debug = false;<br> this.flash_url = "/media/js/swfupload/swfupload.swf";<br> this.upload_url = "";<br><br> this.file_size_limit = "100 MB";<br> this.file_types = "*.jpg;*.gif;*.jpeg";<br> this.file_types_description = "Файлы изображений";<br> this.file_upload_limit = 100;<br> this.file_queue_limit = 0;<br> this.custom_settings = {<br> progressTarget : "fsUploadProgress",<br> cancelButtonId : "btnCancel"<br> };<br> this.button_image_url = "/media/js/swfupload/XPButtonUploadText_61x22.png",<br> this.button_width = "61",<br> this.button_height = "22",<br> this.button_placeholder_id = "spanButtonPlaceHolder", <br> this.file_post_name = null;<br><br> // Проверяем ос на Linux (проблемы совместимости).<br> // Проверяем количество input file, <br> // Должен быть один. <br> // деолжен быть обязательным<br> // Проверяем не редактирование ли это?<br> // Готовим Ui.<br> // Инициализируем swfupload<br> this.init = function(e){<br><br> if (navigator.appVersion.indexOf("Linux")!=-1)<br> return false;<br> <br> this.old_upload = jQuery('input[type=file]');<br> <br> if (this.old_upload.size() != 1)<br> return false;<br> <br> if (!jQuery("label[for=" + this.old_upload.attr('id') + "]").hasClass('required'))<br> return false;<br><br> if (this.old_upload.prev().get(0).tagName == "BR")<br> return false;<br> <br> this.gui();<br> <br> this.swfu = new SWFUpload(this);<br> <br> }<br> <br> // Скрываем оригинальный input file<br> // Добавляем холдер для контрола swfuploader<br> // Добавляем перехватчик отправки форм<br> // Скрываем лишние кнопки<br> this.gui = function(){<br> <br> this.file_post_name = this.old_upload.attr('name');<br> <br> jQuery("<span id='spanButtonPlaceHolder'></span>").insertAfter(this.old_upload);<br> <br> <br> <br> jQuery('form').submit(function(e){<br> DjangoMultiUpload.submit(e);<br> return false;<br> });<br> <br> this.old_upload.hide();<br> jQuery('input[name=_continue]').hide();<br> jQuery('input[name=_addanother]').hide();<br> <br> }<br> <br> // Проверка на занятость<br> // Проверка обязательные поля<br> // Запуск закачки<br> this.submit = function(e){<br> <br> if (this.busy){<br> alert('Uploading. Please wait.');<br> return false;<br> }<br> <br> if (this.prepare_post_data()){<br> <br> jQuery('input[name=_save]').attr('value',0);<br> <br> this.busy = true;<br> <br> this.swfu.startUpload();<br> }<br> <br> return false;<br> <br> }<br> <br> // Получаем все лейблы<br> // По ним находим поля и прописываем их в postdata swfuploader<br> // Если поле обязательное и не зполнено меняем цвет и возварщаем false<br> // При переборе игнорируем input-file<br> // На случай linux плеера передаем имя поля с файлом<br> this.prepare_post_data = function (){<br> <br><br> <br> var labels = jQuery('label');<br> <br> if (!labels.size())<br> return 0;<br> <br> var field = null;<br> var label = null;<br> var success = true;<br> <br> for (i = 0 ; i < labels.size();i++ )<br> {<br> <br> label = jQuery(labels.get(i))<br> <br> if (label.attr('for') == this.old_upload.attr('id'))<br> continue;<br><br> field = jQuery('#' + label.attr('for'));<br> <br> if (label.hasClass('required') && !field.attr('value')){<br> label.css('color','red !important');<br> success = false;<br> }<br><br> this.swfu.addPostParam(field.attr('name'), field.attr('value'));<br> <br> }<br> <br> this.swfu.addPostParam('file_post_name', this.file_post_name)<br> <br> return success;<br> <br> }<br> <br> // По завершении загрузки файла<br> this.upload_complete_handler = function(file)<br> {<br> jQuery('input[name=_save]').attr('value', parseInt(jQuery('input[type=submit]').attr('value'))+1)<br> };<br> <br> // По завершении загрузки всех файлов идем к списку объектов<br> this.queue_complete_handler = function(numFilesUploaded)<br> {<br> <br> this.busy = false;<br> <br> if (!this.settings.debug){<br> window.location="../";<br> }<br> }; <br> <br> // Прочие события<br> this.file_queued_handler = function(file){};<br> this.file_queue_error_handler = function(file, errorCode, message){};<br> this.file_dialog_complete_handler = function(numFilesSelected, numFilesQueued){};<br> this.upload_start_handler = function(file){};<br> this.upload_progress_handler = function(file, bytesLoaded, bytesTotal){};<br> this.upload_success_handler = function(file, serverData){};<br> this.upload_error_handler = function(file, errorCode, message){};<br> <br>};<br><br> <br><br> <br><br>* This source code was highlighted with Source Code Highlighter.
- у меня повсюду в админке используется jQuery. Если у вас его нет, так же кладем djangosite/media/js/jquery.min.js.
- А теперь немного грязнохакерства. В djangosite/gallery/middleware.py прописываем:
from django.conf import settings
class MultiUploadHacksMiddleware(object):
def process_request(self, request):
# Некоторые flash плееры не отправляют или отправляют неправильный sessionid
# Берем его из POST
if request.POST.has_key(settings.SESSION_COOKIE_NAME):
request.COOKIES[settings.SESSION_COOKIE_NAME] = \
request.POST[settings.SESSION_COOKIE_NAME]
# Другие не умеют менять название POST переменной с файлом
# Возьмем его из заранее подготовленной
if request.POST.has_key("file_post_name") and request.FILES.has_key("Filedata"):
request.FILES[request.POST["file_post_name"]] = request.FILES["Filedata"]
del request.FILES["Filedata"]
* This source code was highlighted with Source Code Highlighter.
- Добавляем наши хаки в settings.py
MIDDLEWARE_CLASSES = (
'djangosite.gallery.middleware.MultiUploadHacksMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
)
* This source code was highlighted with Source Code Highlighter.
Готово, теперь наши пользователи могут грузить фотки пачками. Можно пойти дальше — добавить прогресс бар и прочие навороты, но в моем случае в этом не было необходимости.
Ограничения:
- у модели должно быть только одно поле — файл,
- оно должно быть обязательным,
- прочие поля (кроме id, разумеется) не должны быть уникальными,
В идеале хотелось что бы:
- flash-плеер сразу брал правильные cookie (django все равно ругается но файлы создает),
- linux flash-плеер поддерживал flie_post_name,
- был метод openDialog() в swfupload для использования собственных контролов.
На всякий случай демка.