title: How to use FilePond in Django project
body: <p><strong>Background:</strong>
  I have two models: SellingItem and SellingItemImages. SellingItemImages has a custom FileField that can take multiple files. By putting two forms(itemform and imageform) under single  element (enctype="multipart/form-data"), I was able to allow users to upload multiple images. Now, I want to incorporate client side image optimization and better UI. I tried out filepond but am facing some challenges. I organized this post by </p>
  
  <ol>
  <li>showing django code without filepond </li>
  <li>showing code with filepond what</li>
  <li>I accomplished with filepond so far   </li>
  <li>questions on what to do next</li>
  </ol>
  
  <p>** 1)django code without filepond.**
  models.py</p>
  
  <pre><code># 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. 
  </code></pre>
  
  <p>Here is forms.py</p>
  
  <pre><code>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,}),
          }
  </code></pre>
  
  <p>Here is views.py</p>
  
  <pre><code>@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})
  </code></pre>
  
  <p>Here is template post_newitem.html. Here I put two forms under single  element. </p>
  
  <pre><code>{% extends 'market/marketbase.html' %}
  {% block content %}
      &lt;form id="post_form" method="post" action="" enctype="multipart/form-data"&gt;
  
      {% csrf_token %}
  
      {% for hidden in itemform.hidden_fields %}
          {{ hidden }}
      {% endfor %}
  
      {% load widget_tweaks %}
          &lt;table&gt;
              &lt;tr&gt;
                  &lt;td&gt;{{ itemform.name |add_class:"name_form_field"}}&lt;/td&gt;
              &lt;/tr&gt;
              &lt;tr&gt;
                  &lt;td&gt;{{ itemform.description |add_class:"description_form_field" }}&lt;/td&gt;
              &lt;/tr&gt;
              &lt;tr&gt;
                  &lt;td&gt;{{ itemform.price |add_class:"price_form_field" }}&lt;/td&gt;
              &lt;/tr&gt;
  
          {% for hidden in imageform.hidden_fields %}
              {{ hidden }}
          {% endfor %}
              &lt;tr&gt;
                  &lt;td&gt;
                      {{ imageform.image |add_class:"image_form_field" }}
                  &lt;/td&gt;
              &lt;/tr&gt;
          &lt;/table&gt;
  
      &lt;input class='edit-delete-buttons' type="submit" name="_publish"&gt;
      &lt;input class='edit-delete-buttons' type="submit" name="_cancel"&gt;
      &lt;/form&gt;
  {% endblock %}
  </code></pre>
  
  <p>The above code works in allowing users to upload multiple images. As mentioned earlier, to get better UI and client-side image optimization, I turned to this nice javascript library, filepond.</p>
  
  <p><strong>2) code with filepond</strong></p>
  
  <pre><code>&lt;script&gt;
  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) =&gt; {
          console.log(err, fileItem.getMetadata('resize'));
          },
          // to see whether all files are processed
          onprocessfiles: () =&gt; {
              console.log('process finished for all files');
          },
          // show when a file is reverted
          onprocessfilerevert: (fileItem) =&gt; {
              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,
          }
          });
      &lt;/script&gt;
  </code></pre>
  
  <p><strong>3) what I accomplished with filepond so far</strong></p>
  
  <p>With the above code, I was able to 
  a. show filepond drop-drag area
  b. show image preview
  c. filepond showing upload completed as shown in the following image
  d. in Chrome Develop Tools console, showing "process finished for all files"</p>
  
  <p><a href="https://i.stack.imgur.com/p5hK6.png" rel="nofollow noreferrer">image showing filepond drop area after selecting two files</a></p>
  
  <p><strong>4) questions on what to do next</strong></p>
  
  <p>a: server related:I understand that the green highlight with "upload finished" is for users. This does not necessarily mean that the file is uploaded to server. </p>
  
  <p>Were the files uploaded to the server? 
  Is my server config. correct? 
  How can one know if the files are uploaded to server (using console)?</p>
  
  <p>b: django related: once the files are uploaded to server, how can one retrieve these files and point to the right django models (in my case, SellingItemsImages)? </p>
  
  <p>I tried files=request.FILES.getlist('filepond') as shown in this <a href="https://stackoverflow.com/questions/54708393/how-to-point-filepond-to-django-media-files">post</a>, but files returned empty list. I do not know if this is because this snippet does not work, or it is because I do not have any files uploaded to start with.</p>
  
  <p>c: django form related: as mentioned in the background, I have two forms, one is regular form with name, price, etc; another one for uploading images. Without filepond, I was sending both forms with one submit button in one post_newitem view. With filepond, I guess I have a couple of options:
  - option 1: send regular form with submit button, while send filepond files asynchronously. 
  - option 2: let filepond optimize images (via transformplugin), and send images and other form areas(name, price, etc) as FormData.</p>
  
  <p>I hope to get some input on pros and cons about these two options, and how to proceed with these two options. </p>