Как использовать FilePond в проекте Django - PullRequest
1 голос
/ 27 октября 2019

Фон: У меня есть две модели: SellingItem и SellingItemImages. SellingItemImages имеет собственный FileField, который может принимать несколько файлов. Поместив две формы (itemform и imageform) в один элемент (enctype = "multipart / form-data"), я смог позволить пользователям загружать несколько изображений. Теперь я хочу включить оптимизацию изображений на стороне клиента и улучшенный пользовательский интерфейс. Я опробовал filepond, но столкнулся с некоторыми проблемами. Я организовал этот пост по

  1. , показывающему код django без filepond
  2. , показывающий код с filepond, что
  3. Я достиг с filepond
  4. вопросов почто делать дальше

** 1) код django без filepond. ** models.py

# models.py
class SellingItem(models.Model):
    seller = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    name = models.CharField(max_length=200)
    description = models.CharField(max_length= 500, null=True, blank=True)
    price = models.IntegerField(default=0)


class SellingItemImages(models.Model):
    sellingitem = models.ForeignKey(SellingItem, default = None, on_delete=models.CASCADE, related_name='images')
    image = ContentTypeRestrictedFileField(content_types=['image/png', 'image/jpeg','image/jpg'],blank=True, 
                                   max_upload_size=5242880) 
    #ContentTypeRestrictedFileField is a custom FileField. 

вот Forms.py

class SellingItemForm(forms.ModelForm):

    class Meta:
        model = SellingItem
        fields = ('name', 'description', 'price')

class SellingItemImagesForm(forms.ModelForm):

    class Meta:
        model = SellingItemImages
        fields= ('image',)
        widgets = {
            'image': forms.FileInput(attrs={'multiple':True,}),
        }

Вот так выглядит views.py

@login_required
def post_newitem(request):

    if request.method == 'POST':

        itemform = SellingItemForm(request.POST)
        imageform = SellingItemImagesForm(request.POST, request.FILES)

        if '_cancel' in request.POST:
            itemform = SellingItemForm()
            imageform = SellingItemImagesForm()
            return render(request, 'market/post_newitem.html',
                  {'itemform': itemform, 'imageform': imageform})

        else:
            if '_publish' in request.POST:  
                print('hit publish')  
                if itemform.is_valid() and imageform.is_valid():
                    print('two forms are valid')
                    sellingitem = itemform.save(commit=False)
                    sellingitem.seller = request.user
                    sellingitem.published_date = timezone.now()
                    sellingitem.save()

                    files = request.FILES.getlist('image')
                    for f in files:
                        photo = SellingItemImages(sellingitem=sellingitem, image=f)
                        photo.save()
                    return redirect('market_home')    
                else:
                    print(itemform.errors, imageform.errors)    

    else:
        itemform = SellingItemForm()
        imageform = SellingItemImagesForm(request.POST)
    return render(request, 'market/post_newitem.html',
                  {'itemform': itemform, 'imageform': imageform})

Вот шаблон post_newitem.html. Здесь я поместил две формы под одним элементом.

{% extends 'market/marketbase.html' %}
{% block content %}
    <form id="post_form" method="post" action="" enctype="multipart/form-data">

    {% csrf_token %}

    {% for hidden in itemform.hidden_fields %}
        {{ hidden }}
    {% endfor %}

    {% load widget_tweaks %}
        <table>
            <tr>
                <td>{{ itemform.name |add_class:"name_form_field"}}</td>
            </tr>
            <tr>
                <td>{{ itemform.description |add_class:"description_form_field" }}</td>
            </tr>
            <tr>
                <td>{{ itemform.price |add_class:"price_form_field" }}</td>
            </tr>

        {% for hidden in imageform.hidden_fields %}
            {{ hidden }}
        {% endfor %}
            <tr>
                <td>
                    {{ imageform.image |add_class:"image_form_field" }}
                </td>
            </tr>
        </table>

    <input class='edit-delete-buttons' type="submit" name="_publish">
    <input class='edit-delete-buttons' type="submit" name="_cancel">
    </form>
{% endblock %}

Приведенный выше код позволяет пользователям загружать несколько изображений. Как упоминалось ранее, для улучшения пользовательского интерфейса и оптимизации изображений на стороне клиента я обратился к этой прекрасной библиотеке JavaScript, filepond.

2) с filepond

<script>
document.addEventListener('DOMContentLoaded', function() {

    // Create FilePond object
    const inputElement = document.querySelector('input[type="file"]');
    const pond = FilePond.create(inputElement, {
        // track addfile event
        onaddfile: (err, fileItem) => {
        console.log(err, fileItem.getMetadata('resize'));
        },
        // to see whether all files are processed
        onprocessfiles: () => {
            console.log('process finished for all files');
        },
        // show when a file is reverted
        onprocessfilerevert: (fileItem) => {
            console.log(fileItem + 'is reverted');
        },

    });
});

    FilePond.registerPlugin(
        FilePondPluginImagePreview,
        FilePondPluginImageCrop,
        FilePondPluginImageTransform,
        FilePondPluginFileValidateType,
        FilePondPluginImageResize);

    var csrf_token="{{ csrf_token }}";

    FilePond.setOptions({
        imagePreviewHeight: 100,
        allowMultiple: true,
        imageCropAspectRatio: 1,
        imageResizeTargetWidth: 256,
        imageResizeMode: 'contain',
        imageTransformOutputQuality: 80,
        maxFiles: 4,

        server: {
            // url, none, because endpoints located on the same server
            process: {
                headers: {"X-CSRFToken":csrf_token,},
                url: '/media/',
                method: 'POST',
            },
            revert: {
                headers: {
                "X-CSRFToken":csrf_token,
                },
                url: '/media/',
                method: 'DELETE',
            },
            fetch: null,
            load: null,
        }
        });
    </script>

3) что я сделал с filepond до сих пор

С помощью приведенного выше кода я смог a. показать область перетаскивания filepond b. показать предварительный просмотр изображения c. filepond, показывающий, что загрузка завершена, как показано на следующем рисунке d. в консоли Chrome Develop Tools, показывая «процесс завершен для всех файлов»

изображение, показывающее область перетаскивания filepond после выбора двух файлов

4) вопросы о том, чтосделать следующее

a: связанный с сервером: Я понимаю, что зеленая подсветка с надписью "загрузка завершена" предназначена для пользователей. Это не обязательно означает, что файл загружен на сервер.

Были ли файлы загружены на сервер? Является ли мой сервер конфигурации. верный? Как узнать, загружены ли файлы на сервер (с помощью консоли)?

b: django связанный: как только файлы загружены на сервер, как можно получить эти файлы и указать нужные модели django (вмой случай, SellingItemsImages)?

Я попытался files = request.FILES.getlist ('filepond'), как показано в этом сообщении , но файлы вернули пустой список. Я не знаю, если это потому, что этот фрагмент не работает, или это потому, что у меня нет загруженных файлов для начала.

c: django форма связана: как упомянуто в фоновом режиме, у меня есть дваформы, одна обычная форма с именем, ценой и т. д .;еще один для загрузки изображений. Без filepond я отправлял обе формы с помощью одной кнопки отправки в одном представлении post_newitem. С filepond, я думаю, у меня есть несколько вариантов: - вариант 1: отправлять обычную форму с кнопкой отправки, а отправлять файлы filepond асинхронно. - вариант 2: позволить filepond оптимизировать изображения (через transmpmpgin) и отправлять изображения и другие области формы (имя, цена и т. д.) в виде FormData.

Я надеюсь получить некоторую информацию о плюсах и минусах этих двух вариантови как поступить с этими двумя вариантами.

1 Ответ

0 голосов
/ 28 октября 2019

Во-первых, спасибо за подробный вопрос.

a) Зеленый означает, что сервер возвратил ответ 200 OK на запрос загрузки. Таким образом, файл должен быть загружен. Вы можете проверить, был ли файл отправлен на вкладке сети ваших инструментов разработчика, щелкнув запрос POST и проверив его тело (оно должно отображаться в виде двоичных данных / данных многочастной формы).

б) Я не знаком с Джанго, поэтому боюсь, что не могу помочь с Джанго. FilePod отправляет два элемента в POST, один объект JSON (метаданные файла) и один объект файла, может быть, это вызывает некоторые проблемы с Django? Подробнее об этом здесь: https://pqina.nl/filepond/docs/patterns/api/server/

Также может быть полезен серверный компонент Django: https://github.com/ImperialCollegeLondon/django-drf-filepond

c) Преимущество варианта 2 заключается в том, что нет необходимости хранить временные файлы,но окончательная отправка формы займет больше времени, и при наличии больших файлов она может быть менее стабильной на медленных / мобильных соединениях. С опцией 1 у вас есть возможность восстановить временные загруженные файлы, когда пользователь уходит и возвращается на страницу. Я писал о плюсах и минусах загрузки отредактированных на стороне клиента файлов здесь: https://pqina.nl/blog/the-trouble-with-editing-and-uploading-files-in-the-browser/

...