Django: загрузка файлов с помощью AJAX: форма говорит, что поле ввода файла пусто (или токен CSRF отсутствует или неверен)

Я пытаюсь добавить очень простую модальную форму загрузки файлов в свое приложение Django.

Но когда я нажимаю кнопку отправки, форма показывает мне сообщение об ошибке: это поле обязательно для заполнения.

Все отображается правильно:

  • Моя главная страница загружается правильно
  • Когда я нажимаю кнопку дополнения к архиву Agregar (добавить вложение), модальная форма отображается правильно, и все поля отображаются так, как я хочу.
  • Проблема возникает, когда я нажимаю кнопку архива Adjuntar (прикрепить файл) в моем модальном поле: форма выдает ошибку, как будто я пытаюсь загрузить нулевой файл!

Теперь... Я забыл добавить cache: false, contentType: false, processData: false к вызову $.ajax(), но когда я добавляю их, я получаю следующую ошибку: Forbidden (CSRF token missing or incorrect.). Итак... я не знаю, как поступить!

Я уже написал (успешно) модальную форму, которая помогает мне добавлять примечания (связанные) к моему lead объекту (используя эта ссылка), и я пытаюсь воспроизвести точно такой же процесс для модального диалога загрузки файла.. ..но не работает :(

Любая помощь будет действительно оценена.

Кстати: я тестирую это с помощью Chrome (IE не будет использоваться!)



Вот мой код:

модели.py

def lead_dir_path(instance, filename):
    """
    Files will be saved to: MEDIA_ROOT/leads/<int:pk>/<filename>
    where <int:pk> is lead's primary key, and <filename> is just that.

    Filename will be set to an UUID value.
    """
    ext = filename.split('.')[-1]
    filename = '%s.%s' % (uuid.uuid4(), ext)
    return 'leads/%s/%s' % (instance.lead.pk, filename)


class ArchivosAdjuntosLead(models.Model):
    lead = models.ForeignKey(Lead, on_delete=models.CASCADE)
    descripcion = models.CharField(max_length=100)
    archivo = models.FileField(upload_to=lead_dir_path)

просмотры.py

def agregar_adjunto_json(request, pk):
    """
    Adds a file to lead with id=pk
    """
    context = {}
    data = {}

    lead = get_object_or_404(Lead, pk=pk)
    context['lead'] = lead

    if request.method == 'POST':
        form = AdjuntarArchivoLeadForm_v2(request.POST, request.FILES)
        if form.is_valid():
            form.save();
            data['form_is_valid'] = True
        else:
            data['form_is_valid'] = False
    else:
        form = AdjuntarArchivoLeadForm_v2()
        form.initial = {'lead': lead}
    context['form'] = form

    data['html_form'] = render_to_string(
        template_folder + 'partial_templates/partial_adjuntar_archivo.html',
        context,
        request = request,
    )
    return JsonResponse(data)

формы.py

class AdjuntarArchivoLeadForm_v2(forms.ModelForm):
    class Meta():
        model = ArchivosAdjuntosLead
        fields = ['lead', 'descripcion', 'archivo']

        widgets = {
          'lead': forms.TextInput(attrs={'class':'form-control', 'style':'display:none;'}),
          'descripcion': forms.TextInput(attrs={'class':'form-control'}),
          'archivo': forms.FileInput(attrs={'class':'form-control'}),
        }

partial_adjuntar_archivo.html

Я использую этот частичный шаблон для создания модальной формы:

<form method="POST" enctype="multipart/form-data"
      action="{% url 'leads:agregar_adjunto_v2' pk=lead.pk %}"
      id="js_adjuntar_archivo_form">
  {% csrf_token %}

  <div class="modal-header">
    <h4 class="modal-title">Adjuntar archivo</h4>
  </div>

  <div class="modal-body">

    {{ form.as_p }}

    <div class="modal-footer">
      <button type="submit" class="btn btn-primary col-4">Adjuntar archivo</button>
      <button type="button" class="btn btn-secondary col-4" data-dismiss="modal">Cancelar</button>
    </div>
  </div>
</form>

my_lead_page.html

Это страница, на которой я создаю модальную форму:

{% extends "leads/base.html" %}

{% load static %}

{% block contenido %}

<!-- Lots and lots of info -->
<button type="button" class="btn btn-sm btn-primary col-2" id="btn_agregar_adjunto">
  Agregar archivo adjunto
</button>
{% endblock %}

{% block other_scripts %}
<script type="text/javascript" src="{% static 'js/leads/archivos_adjuntos.js'%}"></script>

{% endblock %}

archivos_adjuntos.js

$(function() {
  $("#btn_agregar_adjunto").click(function() {
    $.ajax({
      url: 'adjuntar_archivo/',
      type: 'get',
      dataType: 'json',
      beforeSend: function() {
        $("#modal-form").modal("show");
      },
      success: function(data) {
        $("#modal-form .modal-content").html(data.html_form);
      }
    });
  });

  $("#modal-form").on("submit", "#js_adjuntar_archivo_form", function() {
    var form = $(this);
    $.ajax({
      url: form.attr("action"),
      data: form.serialize(),
      type: form.attr("method"),
      dataType: 'json',
      cache: false,
      contentType: false,
      processData: false, 
      success: function(data) {
        if(data.form_is_valid) {
          alert("Archivo adjuntado");
        } else {
          $("#modal-form .modal-content").html(data.html_form);
        }
      }
    });
    return false;
  });
  
});

person Barranka    schedule 06.07.2018    source источник
comment
Вы пытались отправить данные с помощью formData?   -  person Lemayzeur    schedule 06.07.2018
comment
@Lemayzeur, что ты имеешь в виду?   -  person Barranka    schedule 06.07.2018
comment
Вместо form.serialize() попробуйте отправить его с помощью js formData() должно работать   -  person Lemayzeur    schedule 06.07.2018
comment
@Lemayzeur Это сделало это! Не могу поверить, что это было так просто! Не могли бы вы добавить свой комментарий в качестве ответа? Я бы очень хотел проголосовать за это!   -  person Barranka    schedule 06.07.2018


Ответы (1)


Вместо form.serialize() попробуйте отправить его с помощью js formData(), должно сработать.

Вот пример:

$("#modal-form").on("submit", "#js_adjuntar_archivo_form", function() {
    $that = this;
    var form = new FormData($(this)[0]);

    $.ajax({
        url:$that.attr("action"),
        type:$that.attr("method"),
        data:form,
        processData: false,
        contentType: false,

        // rest of the code'''

    });
    return false;
});
person Lemayzeur    schedule 06.07.2018