Skip to content

File uploading#167

Draft
adamghill wants to merge 1 commit intomainfrom
file-upload
Draft

File uploading#167
adamghill wants to merge 1 commit intomainfrom
file-upload

Conversation

@adamghill
Copy link
Member

No description provided.

@adamghill adamghill self-assigned this Mar 10, 2021
@adamghill adamghill linked an issue Mar 10, 2021 that may be closed by this pull request
@adamghill adamghill marked this pull request as draft March 10, 2021 11:57
</div>

<div>
<form wire:submit.prevent="upload_file">

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this meant to be unicorn:submit?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! This would need to be unicorn:submit once this is all working.

@adamghill
Copy link
Member Author

File Uploading Plan

Overview

Based on PR #167, the goal is to add file uploading support to django-unicorn. The approach leverages Django's existing ModelForm capabilities for validation and saving, while updating the frontend Unicorn JavaScript to handle file inputs and transmit them via FormData.

Frontend Changes

1. Element Class (django_unicorn/static/unicorn/js/element.js)

  • Detection: Update init() to detect input[type="file"].
  • Attributes: Store file-specific attributes.
    if (this.el.type === "file") {
      this.file = {
          accept: this.el.accept,
          multiple: this.el.multiple,
      };
    }
  • Value: Update getValue() to return the files property (FileList) for file inputs instead of value.

2. MessageSender (django_unicorn/static/unicorn/js/messageSender.js)

  • Payload Construction: Modify the send function.
    • Check if any data in component.data or actionQueue is a File or FileList (or if the bound element is a file input).
    • If files are detected:
      • Create a FormData object.
      • Append the standard JSON payload (id, data, checksum, actionQueue, etc.) as a field (e.g. name it message or keep the structure flat if possible, but likely nested is cleaner: FormData.append('body', JSON.stringify(body))).
      • Append each file to the FormData with its key corresponding to the model name.
  • Headers:
    • If sending FormData, omit the Content-Type: application/json header. Let the browser set Content-Type: multipart/form-data; boundary=....

Backend Changes

1. Request Parsing (django_unicorn/views/__init__.py or action_parsers)

  • Body Parsing: Modify the logic that reads the request body.
  • If request.FILES is present (or request.content_type is multipart):
    • Extract the JSON payload from the body field of request.POST.
    • Treat this JSON as the standard component_request.

2. UnicornView Integration

  • Form Handling:
    • When instantiating form_class (if defined), pass request.FILES along with request.POST.
    • Example in validate() or setup():
      self.form = self.form_class(self.request.POST, self.request.FILES)
  • Manual Handling:
    • If not using form_class, ensure that the file data is accessible in the component instance, possibly by mapping request.FILES to the component attributes matching the input names.

Usage Example

Template (upload.html)

<div>
    <input type="file" u:model="document">
    <button u:click="upload_file">Upload</button>
    <div u:loading>Uploading...</div>
</div>

Component (upload.py)

from django_unicorn.components import UnicornView
from .forms import DocumentForm

class UploadView(UnicornView):
    form_class = DocumentForm
    document = None # Binds to the file input

    def upload_file(self):
        if self.form.is_valid():
            self.form.save()
            # Handle success (e.g. clear form, show message)

Form (forms.py)

from django import forms
from .models import Document

class DocumentForm(forms.ModelForm):
    class Meta:
        model = Document
        fields = ['document']

Security & State Considerations

  • Security: Always validate file type, extension, and size on the server (Django Forms handles this well).
  • State Preservation: HTML input[type="file"] is read-only for security. Unicorn cannot "re-hydrate" the file input value after a re-render.
    • Strategy: File uploads are often "one-off" actions. After upload, clear the input. If validation fails, the user may need to re-select the file.
  • Performance: Large file uploads via AJAX might need progress indicators. The beforeSend or xhr events in fetch (or converting to XMLHttpRequest if fetch doesn't support progress easeily) might be needed for progress bars, though fetch is currently used.

Implementation Steps

  1. Modify element.js to capture file input state.
  2. Modify messageSender.js to switch to FormData when files are present.
  3. Update Backend to parse multipart requests and inject files into the component/form.
  4. Create Tests verifying the flow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

How to handle File uploads

2 participants