Django и Dropzone.js

Когда я загружаю файлы с помощью dropzone, он добавляет их в базу данных, но у них нет файла, а только идентификатор и дата создания. Я думаю, что это проблема, но я пробовал массу вещей, и я не могу понять это. См. Мои изменения ниже для более подробной учетной записи.

Вот представление

@login_required(login_url='/dashboard-login/')
def dashboard(request):
    current_user = request.user
    current_client = request.user.client

    files = ClientUpload.objects.filter(client=current_client)

    form = UploadFileForm()

    if request.method == 'POST':
        if request.FILES is None:
            logger = logging.getLogger(__name__)
            logger.warning("No files were attached to the upload.")
            return HttpResponseBadRequest('No Files Attached.')

        if form.is_valid():
            upload = form.save()
            form = UploadFileForm(request.POST, request.FILES)

        else:
            uploaded_files = [request.FILES.get('file_upload[%d]' % i)
                for i in range(0, len(request.FILES))]

            for f in uploaded_files:
                client_upload = ClientUpload.objects.create(client=current_client, file_upload=f)

            #for key in request.FILES:
            #    cupload = ClientUpload.objects.create(client=current_client, file_upload=request.FILES[key])

        logger = logging.getLogger(__name__)
        logger.debug(request.FILES)
        logger.info("File(s) uploaded from " + current_client.company)          

        return HttpResponseRedirect(reverse('dashboard'))

    data = {'form': form, 'client': current_client, 'files': files}
    return render_to_response('dashboard.html', data, context_instance=RequestContext(request))

Вот мои параметры dz:

url: '127.0.0.1:8003/dashboard/',
      method: "post",
      withCredentials: false,
      parallelUploads: 12,
      uploadMultiple: true,
      maxFilesize: 256*4*2,
      paramName: "file_upload",
      createImageThumbnails: true,
      maxThumbnailFilesize: 20,
      thumbnailWidth: 100,
      thumbnailHeight: 100,
      maxFiles: 12,
      params: {},
      clickable: true,
      ignoreHiddenFiles: true,
      acceptedFiles: null,
      acceptedMimeTypes: null,
      autoProcessQueue: false,
      addRemoveLinks: true,
      previewsContainer: null,
      dictDefaultMessage: "Drop files here to upload",
      dictFallbackMessage: "Your browser does not support drag and drop file uploads.",
      dictFallbackText: "Please use the fallback form below to upload your files.",
      dictFileTooBig: "File is too big ({{filesize}}MB). Max filesize: {{maxFilesize}}MB.",
      dictInvalidFileType: "You can't upload files of this type.",
      dictResponseError: "Server responded with {{statusCode}} code.",
      dictCancelUpload: "Cancel upload",
      dictCancelUploadConfirmation: "Are you sure you want to cancel this upload?",
      dictRemoveFile: "Remove",
      dictRemoveFileConfirmation: null,
      dictMaxFilesExceeded: "You can only upload {{maxFiles}} files.",

И вот шаблон:

{% load i18n %}
{% load staticfiles %}
{% load crispy_forms_tags %}

<link href="{% static 'css/dropzone2.css' %}" type="text/css" rel="stylesheet"/>

<form class="dropzone" id="myDropzone" method="post" action="{% url 'dashboard' %}" enctype="multipart/form-data">
    {% csrf_token %}
    <div class="fallback">
        <input name="file" type="file" multiple />
    </div>  
</form>
<button class="upload-control btn-success btn" type="submit" id='submit-all' onclick="document.getElementById('myDropzone').submit()">
    <i class="glyphicon glyphicon-upload"></i>
    <span>{% trans 'Submit' %}</span>
</button>

<style>
    .upload-control {
        margin-top: 10px;
        margin-bottom: 0px;
    }
</style>
<script src="{% static 'js/dropzone.js' %}"></script>
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
<script type="text/javascript">
    Dropzone.autoDiscover = false
    $(document).ready(function() {
        Dropzone.options.myDropzone = {

            init : function() {
                var submitButton = document.querySelector("#submit-all")
                myDropzone = this;

                submitButton.addEventListener("click", function(e) {
                    e.stopPropagation();
                    e.preventDefault();
                    myDropzone.processQueue();
                });

                this.on("sendingmultiple", function() {
                    // Figure out what I want here or if I want at all
                });

                this.on("successmultiple", function(files, response) {
                    window.location.reload();
                });

                this.on("errormultiple", function(files, response) {
                    // Figure out what I want here or if I want at all
                });

                }
                // Do I need this?
                //myDropzone.on('success', myDropzone.processQueue.bind(myDropzone));
        };
});    
</script>

EDIT:

Теперь он работает после добавления http://в настройку URL. Но когда я загружаю файл, он добавляется в базу данных, но поле файла пустое. Многозначный показывает файл, когда я его распечатываю, но когда он сохраняется в базе данных, в поле файла ничего нет.

Когда я загружаю один файл, я получаю его в request.FILES:

<MultiValueDict: {u'file_upload[]': [<InMemoryUploadedFile: normal.PNG (image/png)>]}>

Когда я загружаю два, я получаю это в request.FILES:

<MultiValueDict: {u'file_upload[]': [<TemporaryUploadedFile: normal.PNG (image/png)>]}>

Несмотря на два файла, он показывает только один, но добавляет их как в базу данных (как без файлов, так и с идентификатором и датой создания). Также что такое TemporaryUploadedFile и InMemoryUploadedFile?

Он должен иметь индексы в файле u'file_upload [] ', когда я загружаю несколько, но это не так. У меня есть правильные настройки для загрузки кратных.

Но я не могу вывести их из MultiValueDict. И когда я пытаюсь что-то вроде:

for upload in request.FILES:
    client_upload = ClientUpload.objects.create(client=current_client, file_upload=upload)

Я столкнулся с этой проблемой, когда панель администратора показывает идентификатор и время, но не файл. Это происходит при загрузке одного или нескольких файлов. Я не уверен, какая разница между InMemoryUploadedfile и TemporaryUploadedFile. Как я могу извлечь файлы из MultiValueDict? get() не работает, со списком comp я просто получаю пустой список.

Другая странная вещь, когда я загружаю определенные файлы, MultiValueDict пуст, а с другими - нет. Также кажется, что мое мнение вызывается более одного раза (в соответствии с выходами журнала), и это нормально, за исключением того, что он должен быть пост, а затем перенаправляться на get, но, похоже, имеет более одного почтового запроса. Я проверил инструменты dev в chrome, и я вижу только один, но, как ни странно, он выводит мой оператор журнала дважды каждый раз, когда я отправляю. Я знаю, что проблема, вероятно, на мой взгляд, но я пробовал массу вещей и не могу понять, что не так.

У кого-нибудь есть идеи?

Ответы

Ответ 1

Я работаю с Dropzone и Django самостоятельно для создания объектов Image для каждого загруженного файла, который, похоже, сродни тому, что вы хотите сделать. Я хотел бы указать на некоторые вещи, которые я испытал, и показать вам, как я это делаю, чтобы понять, помогает ли это.

Что вам нужно

То, что вам нужно для создания записи в базе данных для файлов, загруженных с помощью Dropzone, это:

  • HTML-форма Dropzone
  • Инициализация Javascript Dropzone.
  • Django View для обработки загруженных файлов.

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

Доступ к загруженным файлам

Сначала давайте поговорим о том, как получить доступ к файлам в request.FILES. Установив uploadMultiple: true в вашей конфигурации Dropzone, вы устанавливаете Dropzone не для отправки dzfile, а для отправки каждого файла, представленного как dzfile[%d] (т.е. dzfile[0], dzfile[1] и т.д.).

Даже если это не так, вы используете request.FILES, как если бы это был список (for f in request.FILES), но, как вы указываете, на самом деле это dict.

Здесь, что показывает Python, когда я печатаю request.FILES:

<MultiValueDict: {u'dzfile[1]': [<InMemoryUploadedFile: image2.jpg (image/jpeg)>], u'dzfile[2]': [<InMemoryUploadedFile: image3.jpg (image/jpeg)>], u'dzfile[0]': [<InMemoryUploadedFile: image1.jpg (image/jpeg)>]}>

Чтобы получить доступ к фактическим файлам, вам нужно get каждую клавишу по его имени.

files = [request.FILES.get('dzfile[%d]' % i)
     for i in range(0, len(request.FILES))]

Теперь у вас есть список файлов, который вы хотели. Просто повторите его и создайте свои объекты, как хотите. Я не уверен, как ваши модели работают, поэтому я собираюсь приблизиться.

for f in files:
    # Create a ClientUpload object by setting its FK to client and
    # FileField to the file. Correct me if I deduced the models incorrectly
    client_upload = ClientUpload.objects.create(
        client=current_client,
        file_upload=f,
    )

Этого должно быть достаточно для создания объектов, которые вы хотите.

Dropzone Javascript

Похоже, что в прослушивателе событий Click вы добавляете кнопку отправки, вы должны добавить

e.preventDefault();
e.stopPropagation();

перед вызовом processQueue(), чтобы избежать представления двойной формы.

Что касается sendingmultiple, successmultiple и errormultiple, что вы хотите там сделать? В комментариях есть только те, которые указывают, когда эти события триггеров.

Я лично использую:

this.on('sendingmultiple', function () {
    // `sendingmultiple` to hide the submit button
    $('#my-dropzone').find('button[type=submit]').hide();
});
this.on('successmultiple', function (files, response) {
    // `successmultiple` to reload the page (and show the updated info)
    window.location.reload();
});
this.on('errormultiple', function (files, response) {
    // `errormultiple` to un-hide the button
    $('#my-dropzone').find('button[type=submit]').show();
});

Но, конечно, вы можете делать то, что хотите.

И, наконец, что вы собираетесь делать с этой последней строкой в ​​теге <script>? Я не совсем понимаю, похоже, если вы хотите перепрограммировать очередь на успех. Кажется, он не принадлежит.

Прокомментируйте, если что-нибудь уйдет, но эта настройка работает отлично для меня.

Ответ 2

В вашем Javascript, я думаю, вы хотите использовать это для paramName:

paramName: "file_upload",

чтобы форма Django или ModelForm распознала загруженные файлы.

Также убедитесь, что в запросе на загрузку используется тип содержимого multipart/form-data.

Кроме того, попробуйте вместо "get_list":

dz_files = request.FILES.getlist("file_upload")

Ответ 3

Я не знаю, поможет ли то, что я покажу вам, но, возможно, это поможет другим. Работа с dropzone заставила меня сделать обходной путь, потому что ajax, файлы и объединенные django всегда немного сложны.

поэтому в моем html у меня есть этот код:

<div class="logos">
  <i class="fa fa-upload" id="dropzone_icon" data-name="icon" title="{% trans "Drag and drop or click" %}" alt="{% trans "Drag and drop or click" %}" ></i>
  <input type="hidden" name="icon" value="" >
  <input type="hidden" name="icon_name" value="" >
  <div class="img-holder">
    <img title='{% trans "Upload a Company Icon" %}' id="img_icon" alt='{% trans "Company icon" %}' src="{* icon *}"/>
  </div>
  <label>{% trans "Company Icon" %}</label>
</div>

в моем js я получил следующее:

dropz = new Dropzone(value, {
    url: "branding/dropzone",
    maxFiles: 1,
    acceptedFiles: "image/*",
    thumbnail: function(file, dataUrl) {
        /* change existing image */
        var file_type = file.name.split('.');
        file_type = file_type[file_type.length - 1];
        if(!(file_type=="png" || file_type=="jpg" || file_type=="jpeg")){
            createAlert('file type must be .png, .jpg or .jpeg', '#pp_content', 'alert');
            return false;
        }
        $("input[name='icon']").val(dataUrl.split(",")[1]);
        $("input[name='icon_name']").val(file.name);
        $("#img_" + type).prop("src", dataUrl);
        this.removeFile(file);
    },
    previewTemplate: "<span></span>",
    autoProcessQueue: false
});

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

после отправки входов как формы в ajax, так я обрабатываю их в моих view.py:

import datetime
from django.core.files.base import ContentFile

def base64_to_image(img_b64,img_name):
"""
Creates image file from bas64 encoded data
:param img_b64: base64 data
:param img_name: filename for created image
:return: file or false if there no data
"""
if img_b64:
    image_data = b64decode(img_b64)
    img_file = ContentFile(image_data,datetime.datetime.now().strftime("%y%d%m_%H%M%S") + img_name)
    return img_file
else:
    return False

company.icon = base64_to_image(request.POST["icon"], request.POST["icon_name"])
company.save()

Это моя работа по работе с dropzone, возможно, это поможет и другим здесь.