Opened 3 years ago

Closed 3 years ago

Last modified 3 years ago

#33757 closed Cleanup/optimization (fixed)

Clarify django.test.Client.post() file upload example

Reported by: bastian-wattro Owned by: Tim Graham
Component: Documentation Version: dev
Severity: Normal Keywords:
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by bastian-wattro)

Following https://docs.djangoproject.com/en/4.0/topics/testing/tools/#django.test.Client.post
with Django version 4.0.4 to write my first unit tests, I failed to upload a file under a given keyword *with the *name* / *attachment* pattern*

As described there, I tried to set *name* and *file pointer* like this:

Submitting files is a special case. To POST a file, you need only provide the file field name as a key, and a file handle to the file you wish to upload as a value. For example:

with open('mal.csv', 'rb') as fp:

self.client.post('/post/endpoint/', {'name': 'fred', 'attachment': fp})

}}}

The name *attachment* here is not relevant; use whatever name your file-processing code expects.

This made me assume that the file given via the filepointer will be accessible via the keyword fred.

This [test](https://github.com/django/django/blob/d5bc36203057627f6f7d0c6dc97b31adde6f4313/tests/file_uploads/tests.py#L86-L93) works because the [endpoint being called](https://github.com/django/django/blob/d5bc36203057627f6f7d0c6dc97b31adde6f4313/tests/file_uploads/views.py#L18) (contrary to what I would expect) checks the keyword of the file pointer (fiel_field in this case) and the name separately.

What I wanted was

with open('mal.csv', 'rb') as fp:
  self.client.post('/post/endpoint/', {'fred': fp})

as (contraty to what is written in the docs) name is not required

Sorry for the confusion and thanks for the quick responses.

Change History (9)

comment:1 by Mariusz Felisiak, 3 years ago

Resolution: invalid
Status: newclosed

Thanks for the report, however it works for me, see also tests.

What worked was the (way more intuitive) ...

This snippet is exactly the same as the version you think is not working 🤔

If you're having trouble understanding how Django works, see TicketClosingReasons/UseSupportChannels for ways to get help.

comment:2 by Tim Graham, 3 years ago

The example in the documentation initializes a client c = Client() while the snippet here uses self.client. It might that the latter version has some state attached to it (like a login) which makes the difference, but it's impossible to debug without some minimal example code. Please include that in future reports (and first confirm a bug in Django using support channels).

comment:3 by bastian-wattro, 3 years ago

Description: modified (diff)
Resolution: invalid
Status: closednew
Summary: django.test.Client.post documentation is wrongdjango.test.Client.post documentation is confusing

Sorry for the confusion and thanks for the quick responses. I'll try to clarify:

Following the docs
with Django version 4.0.4 to write my first unit tests, I failed to upload a file under a given keyword with the name / attachment pattern

The docs state:

>>> c = Client()
>>> with open('wishlist.doc', 'rb') as fp:
...     c.post('/customers/wishes/', {'name': 'fred', 'attachment': fp})


The name *attachment* here is not relevant; use whatever name your file-processing code expects.

This made me assume that the file given via the file pointer will be accessible via the keyword fred.

This test works because the endpoint being called (contrary to what I would expect) checks the keyword of the file pointer (fiel_field in this case) and the name separately.

Reading the docs for a third time now I get the meaning.

What I don't understand is why the keyword 'name' is there, and why doesn't it simply say something like:


To submit a file, provide a file handle to the file you wish to upload as a value. For example:

>>> c = Client()
>>> with open('wishlist.doc', 'rb') as fp:
...     c.post('/customers/wishes/', {'wishes_file': fp})

will be accessible via request.FILES['wishes_file']


Again thanks for your time. I get now that this is something that probably only confuses new comers like my self.

comment:4 by Mariusz Felisiak, 3 years ago

Resolution: invalid
Status: newclosed

I don't see anything confusing in this docs.

This made me assume that the file given via the file pointer will be accessible via the keyword fred.

Docs explicitly states that the name attachment here is not relevant. fred is not a keyword but a value for the name and it's used that way in other examples.

What I don't understand is why the keyword 'name' is there, and why doesn't it simply say something like:

IMO it's valuable to show that other fields are handled properly and you don't need to send files separately.

comment:5 by bastian-wattro, 3 years ago

Docs explicitly states that the name attachment here is not relevant.

See that is what confuses me. attachment is very relevant, as it is the keyword in the FILES dict.
This is implicitly stated ("use whatever name your file-processing code expects") but not clear if you jump in the docs without context information.

comment:6 by Tim Graham, 3 years ago

Has patch: set
Resolution: invalid
Status: closednew
Summary: django.test.Client.post documentation is confusingClarify django.test.Client.post() file upload example
Type: BugCleanup/optimization
Version: 4.0dev

I offer a PR.

comment:7 by Mariusz Felisiak, 3 years ago

Owner: changed from nobody to Tim Graham
Status: newassigned
Triage Stage: UnreviewedReady for checkin

comment:8 by GitHub <noreply@…>, 3 years ago

Resolution: fixed
Status: assignedclosed

In 61badf1:

Fixed #33757 -- Clarified Client.post() file upload example.

comment:9 by Mariusz Felisiak <felisiak.mariusz@…>, 3 years ago

In 566437ad:

[4.1.x] Fixed #33757 -- Clarified Client.post() file upload example.

Backport of 61badf1d58e79b84874afa6a1e00b79f20e786d1 from main

Note: See TracTickets for help on using tickets.
Back to Top