Issue 1:
Here's a branch with my use case:

from starlette.testclient import TestClient

from fastapi import FastAPI, File, Form

app = FastAPI()

@app.post("/file_before_form")
def file_before_form(
file: bytes = File(...), city: str = Form(...),
):
return {"file_content": file, "city": city}

@app.post("/file_after_form")
def file_after_form(
city: str = Form(...), file: bytes = File(...),
):
return {"file_content": file, "city": city}

client = TestClient(app)

def test_file_before_form():
response = client.post(
"/file_before_form", data={"city": "Thimphou"}, files={"file": "<file content>"}
)
assert response.status_code == 200, response.text
assert response.json() == {"file_content": "<file content>", "city": "Thimphou"}

def test_file_after_form():
response = client.post(
"/file_after_form", data={"city": "Thimphou"}, files={"file": "<file content>"}
)
assert response.status_code == 200, response.text # <-------------- this fails since ab2b86f
assert response.json() == {"file_content": "<file content>", "city": "Thimphou"}

Description

Upgrading FastApi from 0.42.0 to 0.61.0 on one of my project, I stumbled upon a regression in which FastApi will respond with HTTP Error 422 when ever posting a form-data request to an endpoint where a File is declared after a Form.

Searching the Internet, I found another user suffering from this issue : https://stackoverflow.com/questions/61376754/fastapi-files-must-be-loaded-before-form-in-function-parameters

I've created a branch with tests to showcase the issue : https://github.com/thomasleveil/fastapi/tree/file_and_form_together

Environment

OS: Linux

FastAPI Version 0.44.1 and later

Python version: 3.8.5

Additional context

I've bisected the repo and figured out the regression was introduced in commit ab2b86f

Issue 2:
First check

I added a very descriptive title to this issue.

I used the GitHub search to find a similar issue and didn't find it.

I searched the FastAPI documentation, with the integrated search.

I already searched in Google "How to X in FastAPI" and didn't find any information.

I already read and followed all the tutorial in the docs and didn't find an answer.

I already checked if it is not related to FastAPI but to Pydantic.

I already checked if it is not related to FastAPI but to Swagger UI.

I already checked if it is not related to FastAPI but to ReDoc.

After submitting this, I commit to one of:

Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.

I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.

Implement a Pull Request for a confirmed bug.

Description + Example

I have an endpoint that returns an instance of this Container class, which has a list as one of its attributes, called items.

import logging
from pydantic import BaseModel, Field
from typing import List

class Container(BaseModel):
items: List[int] = Field(default_factory=list)

def __init__(self, **data):
logging.debug(f'Init-ing Container with {data}')
super().__init__(**data)

# At runtime, compute some value and add to the list
self.items.append(99)

As shown, I overrode the __init__ because I have to "internally" modify the list after instantiation (same as in the Private Model Attributes section of the Pydantic docs). I'm also using the Field(default_factory=list) to make sure each instance creates its own list.

Testing the class on its own works as expected:

container_1 = Container(items=[1, 2, 3])
logging.debug(container_1.items)
container_2 = Container(items=[4, 5, 6])
logging.debug(container_2.items)

logging.debug(f'{id(container_1.items)}, {id(container_2.items)}')
assert id(container_1.items) != id(container_2.items)

[2020-11-17 08:58:24.713] root:DEBUG Init-ing Container with {'items': [1, 2, 3]}
[2020-11-17 08:58:24.713] root:DEBUG [1, 2, 3, 99]
[2020-11-17 08:58:24.713] root:DEBUG Init-ing Container with {'items': [4, 5, 6]}
[2020-11-17 08:58:24.713] root:DEBUG [4, 5, 6, 99]
[2020-11-17 09:43:22.566] root:DEBUG 4481341504, 4481418560

Now, I set it as a response_model to one of my endpoints:

@api.get('/things', response_model=Container)
def get_things():
return Container(items=[1, 2, 3])

The response I get however has a duplicate 99 value:

This seems to indicate that the __init__ was called twice on the same object? It's somehow related to the Mutable Default Argument problem. Hitting the endpoint multiple times does not keep adding 99, so it seems it is still partially correct that it instantiates a new object, but seems to call __init__ twice.

[2020-11-17 09:23:08.501] root:DEBUG Init-ing Container with {'items': [1, 2, 3]}
[2020-11-17 09:23:08.502] root:DEBUG Init-ing Container with {'items': [1, 2, 3, 99]}

But testing the class on its own does not have this issue, and my endpoint is instantiating the class only once here. So, what am I doing wrong here?

Now, if I use instead the responses parameter:

@api.get('/things', responses={200: {'model': Container}})
def get_things():
return Container(items=[1, 2, 3])

I get the correct response:

And the logs correctly show only one instantiation:

[2020-11-17 09:23:59.786] root:DEBUG Init-ing Container with {'items': [1, 2, 3]}

So, what is wrong with my usage of the response_model parameter?

As for the Swagger/OpenAPI schema, using either response_model or responses show the current schema in the docs, so no problem/difference there. Also, I'm actually not sure if this is a Pydantic or FastAPI issue, but using just the class outside of an endpoint that does not show this problem. So I figure, it's related to being used as a response_model.

Environment

OS: macOS 10.15.7

FastAPI Version: 0.61.2

Pydantic Version: 1.7.2

Python Version: 3.8.6