Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix modelformset with S3 upload #469

Merged
merged 6 commits into from
Jun 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 19 additions & 13 deletions django_file_form/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,22 @@ def value_from_datadict(
files: Union[Dict, MultiValueDict],
prefixed_field_name: str,
):
if hasattr(files, "getlist"):
uploads = files.getlist(prefixed_field_name) + get_uploads(
data, prefixed_field_name
)
metadata = get_file_meta(data, prefixed_field_name)

for upload in uploads:
if upload.name in metadata:
upload.metadata = metadata[upload.name]
return uploads
else:
# NB: django-formtools wizard uses dict instead of MultiValueDict
return super().value_from_datadict(data, files, prefixed_field_name)
def get_uploads_from_files():
if hasattr(files, "getlist"):
return files.getlist(prefixed_field_name)
else:
# NB: django-formtools wizard uses dict instead of MultiValueDict
return (
super(UploadMultipleWidget, self).value_from_datadict(
data, files, prefixed_field_name
)
or []
)

uploads = get_uploads_from_files() + get_uploads(data, prefixed_field_name)
metadata = get_file_meta(data, prefixed_field_name)

for upload in uploads:
if upload.name in metadata:
upload.metadata = metadata[upload.name]
return uploads
1 change: 1 addition & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

* Issue #456: handle data parameter with the None value
* Issue #465: remove unused FILE_FORM_MAX_FILE_SIZE setting
* Issue #468: fix modelformset with S3 upload

**3.2.1 (20 april 2021)**

Expand Down
37 changes: 36 additions & 1 deletion testproject/django_file_form_example/forms.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import os

from django.core.exceptions import ValidationError
from django.forms import formset_factory, BaseFormSet, Form, CharField, ModelForm
from django.forms import (
formset_factory,
BaseFormSet,
Form,
CharField,
ModelForm,
modelformset_factory,
)

from django_file_form.forms import (
UploadedFileField,
Expand Down Expand Up @@ -178,3 +185,31 @@ class Meta:

input_file = MultipleUploadedFileField()
prefix = "example"


class ExampleMultipleModelS3Form(FileFormMixin, ModelForm):
class Meta:
model = Example2
fields = ("title",)

input_file = MultipleUploadedFileField()
prefix = "example"
s3_upload_dir = "s3_example"

def save(self, commit=True):
example = Example2.objects.create(title=self.cleaned_data["title"])

for f in self.cleaned_data["input_file"]:
try:
ExampleFile.objects.create(example=example, input_file=f)
finally:
f.close()

self.delete_temporary_files()


ExampleMultipleModelS3FormSet = modelformset_factory(
model=Example2,
form=ExampleMultipleModelS3Form,
extra=2,
)
39 changes: 39 additions & 0 deletions testproject/django_file_form_example/tests/live/test_s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,3 +325,42 @@ def test_s3_placeholder_delete(self):
page.find_upload_success_with_filename(
"test_placeholder2.txt", upload_index=0, container_element=file_container
)

def test_model_form_multiple_s3_set(self):
page = self.page
page.open("/model_form_multiple_s3_set")

# form 1
page.fill_title_field("abc", form_prefix="form-0")

temp_file1 = page.create_temp_file("123")
page.upload_js_for_input(
temp_file1,
page.selenium.find_element_by_css_selector("#id_form-0-input_file"),
)

page.assert_page_contains_text("3 Bytes")

# form 2
page.fill_title_field("def", form_prefix="form-1")

temp_file2 = page.create_temp_file("1234")
page.upload_js_for_input(
temp_file2,
page.selenium.find_element_by_css_selector("#id_form-1-input_file"),
)

page.assert_page_contains_text("4 Bytes")

# submit
page.submit()
page.assert_page_contains_text("Upload success")

# check results
example1 = Example2.objects.get(title="abc")
self.assertEqual(example1.files.count(), 1)
self.assertEqual(read_file(example1.files.all()[0].input_file), b"123")

example2 = Example2.objects.get(title="def")
self.assertEqual(example2.files.count(), 1)
self.assertEqual(read_file(example2.files.all()[0].input_file), b"1234")
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ def test_wizard(self):
page.submit()
page.assert_page_contains_text("Page 1")

def test_form_set(self):
def test_formset(self):
page = self.page

temp_file1 = page.create_temp_file("content1")
Expand Down
10 changes: 10 additions & 0 deletions testproject/django_file_form_example/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@
views.EditModelFormMultipleView.as_view(),
name="model_form_multiple_update",
),
path(
"model_form_multiple_s3",
views.CreateModelFormMultipleS3View.as_view(),
name="model_form_multiple_s3_create",
),
path(
"model_form_multiple_s3_set",
views.ModelFormMultipleS3SetView.as_view(),
name="model_form_multiple_s3_set_create",
),
path("multiple", views.MultipleExampleView.as_view(), name="multiple_example"),
path(
"multiple_without_js",
Expand Down
9 changes: 9 additions & 0 deletions testproject/django_file_form_example/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,15 @@ def get_initial(self):
return initial


class CreateModelFormMultipleS3View(FileModelFormMultipleMixin, generic.CreateView):
form_class = forms.ExampleMultipleModelS3Form


class ModelFormMultipleS3SetView(BaseFormView):
form_class = forms.ExampleMultipleModelS3FormSet
template_name = "form_set.html"


def permission_denied(request, exception):
return HttpResponseForbidden(
json.dumps(dict(status="permission denied")), content_type="application/json"
Expand Down