如何用aiohttp实现带文件的POST请求及Django测试适配?
Let's break this down into two parts to fix your file upload issues with aiohttp and Django testing.
Fixing the initial aiohttp file upload error
The TypeError you're hitting happens because aiohttp's request() method doesn't accept a files keyword argument — that's a common gotcha! To mimic the multipart/form-data submission from your HTML form (which lets Django populate request.FILES), you need to structure your upload data correctly using the data parameter instead.
Here's the corrected code:
import aiohttp async def upload_file(url): # Use a context manager to guarantee the file gets closed properly with open('file/path', 'rb') as file: # Structure for file uploads: a dict where values are tuples # Format: (filename, file_object, content_type) form_payload = { 'key': ('your_file_name.txt', file, 'text/plain') # Adjust content type to match your file } async with aiohttp.request('post', url, data=form_payload) as response: return await response.text()
This structure tells aiohttp to build a valid multipart/form-data request, just like your HTML form. Django will now pick up the file in request.FILES['key'] as expected.
Adjusting pytest test code with Django's InMemoryUploadedFile
Your test code is creating an InMemoryUploadedFile, but you're passing the raw file object f instead of the uploaded file instance. Plus, you still need to follow the same tuple structure for aiohttp to recognize it as a file upload.
Here's the revised test code:
from django.core.files.uploadedfile import InMemoryUploadedFile from django.core.files.base import DjangoFile import aiohttp def get_uploaded_file(file_path): with open(file_path, "rb") as f: file = DjangoFile(f) # Extract just the filename (instead of using the full path) for realism filename = file_path.split('/')[-1] uploaded_file = InMemoryUploadedFile( file, None, filename, "text/plain", file.size, None, None ) return uploaded_file async def test_file_upload(url, path): uploaded_file = get_uploaded_file(path) # Use the uploaded file instance in the required tuple structure test_form_data = { 'key': (uploaded_file.name, uploaded_file, uploaded_file.content_type) } async with aiohttp.request('post', url, data=test_form_data) as response: return await response.text()
Key notes for the test code:
- We're using the
InMemoryUploadedFileinstance directly in the tuple, along with its built-innameandcontent_typeattributes. - We extract a clean filename from the path instead of passing the full path, which matches real-world upload behavior.
- Make sure your pytest setup uses
pytest-asyncioto handle async test functions properly.
内容的提问来源于stack exchange,提问作者lee

