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

WIP: Separate file field in django form constructor #4121

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ their individual contributions.
* `Andrea Reina <https://www.github.com/andreareina>`_
* `Andrew Sansom <https://www.github.com/qthequartermasterman>`_
* `Anne Archibald <https://www.github.com/td-anne>`_
* `Arjoonn Sharma <https://github.com/thesage21>`_
* `Ben Anhalt <https://github.com/benanhalt>`_
* `Ben Peterson <https://github.com/killthrush>`_ ([email protected])
* `Benjamin Lee <https://github.com/Benjamin-Lee>`_ ([email protected])
Expand Down
6 changes: 6 additions & 0 deletions hypothesis-python/RELEASE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
RELEASE_TYPE: minor

This release fixes file field handling for django forms when using
:func:`~hypothesis.extra.django.from_form` (:issue:`4093`).

Thanks to Arjoonn Sharma for this fix!
12 changes: 12 additions & 0 deletions hypothesis-python/src/hypothesis/extra/django/_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,10 @@ def from_form(

unbound_form = form(**form_kwargs)
fields_by_name = {}
file_fields_by_name = set()
for name, field in unbound_form.fields.items():
if isinstance(field, df.FileField):
file_fields_by_name.add(name)
if isinstance(field, df.MultiValueField):
# PS: So this is a little strange, but MultiValueFields must
# have their form data encoded in a particular way for the
Expand All @@ -201,11 +204,20 @@ def from_form(
for name, field in sorted(fields_by_name.items()):
if name not in field_strategies and not field.disabled:
field_strategies[name] = from_field(field)
# File strategies need to be passed separately into the form constructor
# and so we create a separate dict for them. These fields are not supposed
# to be sent via the data constructor
# ref: https://docs.djangoproject.com/en/5.1/topics/http/file-uploads/#id4
file_strategies = {}
for name in list(field_strategies):
if name in file_fields_by_name:
file_strategies[name] = field_strategies.pop(name)

return _forms_impl(
st.builds(
partial(form, **form_kwargs), # type: ignore
data=st.fixed_dictionaries(field_strategies), # type: ignore
files=st.fixed_dictionaries(file_strategies), # type: ignore
)
)

Expand Down
7 changes: 7 additions & 0 deletions hypothesis-python/tests/django/toystore/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
Company,
CouldBeCharming,
Customer,
FileFields,
ManyNumerics,
ManyTimes,
OddFields,
Expand All @@ -46,6 +47,12 @@ class Meta:
fields = "__all__"


class FileFieldsForm(ReprModelForm):
class Meta:
model = FileFields
fields = "__all__"


class CustomerForm(ReprModelForm):
class Meta:
model = Customer
Expand Down
4 changes: 4 additions & 0 deletions hypothesis-python/tests/django/toystore/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ class CustomishDefault(models.Model):
customish = CustomishField(default="b")


class FileFields(models.Model):
file1 = models.FileField()


class MandatoryComputed(models.Model):
name = models.CharField(max_length=100, unique=True)
company = models.ForeignKey(Company, null=False, on_delete=models.CASCADE)
Expand Down
7 changes: 7 additions & 0 deletions hypothesis-python/tests/django/toystore/test_given_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
CustomerForm,
DynamicForm,
EmailFieldForm,
FileFieldsForm,
InternetProtocolForm,
ManyMultiValueForm,
ManyNumericsForm,
Expand Down Expand Up @@ -130,6 +131,12 @@ def test_tight_validators_form(self, x):
self.assertTrue(1 <= x.data["_float_one_to_five"] <= 5)
self.assertTrue(5 <= len(x.data["_string_five_to_ten"]) <= 10)

@given(from_form(FileFieldsForm))
def test_file_fields_form(self, x):
assert x.is_valid()
if "file1" in x.data:
self.assertTrue(x.data["file1"])

@given(from_form(UsernameForm))
def test_username_form(self, username_form):
self.assertTrue(username_form.is_valid())
Expand Down
Loading