Browse Source

Merge remote-tracking branch 'origin/free-form-queries'

pull/9837/head
JONEMI19 3 years ago
parent
commit
1dde63024a
  1. 19
      fastapi/dependencies/utils.py
  2. 12
      tests/main.py
  3. 1195
      tests/test_application.py
  4. 13
      tests/test_invalid_mapping_param.py
  5. 14
      tests/test_invalid_sequence_param.py

19
fastapi/dependencies/utils.py

@ -1,4 +1,5 @@
import inspect import inspect
from collections import defaultdict
from contextlib import contextmanager from contextlib import contextmanager
from copy import deepcopy from copy import deepcopy
from typing import ( from typing import (
@ -450,6 +451,11 @@ def is_body_param(*, param_field: ModelField, is_path_param: bool) -> bool:
param_field.field_info, (params.Query, params.Header) param_field.field_info, (params.Query, params.Header)
) and is_scalar_sequence_field(param_field): ) and is_scalar_sequence_field(param_field):
return False return False
elif isinstance(param_field.field_info, (params.Query, params.Header)) and (
is_scalar_sequence_mapping_field(param_field)
or is_scalar_mapping_field(param_field)
):
return False
else: else:
assert isinstance( assert isinstance(
param_field.field_info, params.Body param_field.field_info, params.Body
@ -644,6 +650,19 @@ def request_params_to_args(
received_params, (QueryParams, Headers) received_params, (QueryParams, Headers)
): ):
value = received_params.getlist(field.alias) or field.default value = received_params.getlist(field.alias) or field.default
if is_scalar_mapping_field(field) and isinstance(
received_params, (QueryParams, Headers)
):
value = dict(received_params.multi_items()) or field.default
elif is_scalar_sequence_mapping_field(field) and isinstance(
received_params, (QueryParams, Headers)
):
if not len(received_params.multi_items()):
value = field.default
else:
value = defaultdict(list)
for k, v in received_params.multi_items():
value[k].append(v)
else: else:
value = received_params.get(field.alias) value = received_params.get(field.alias)
field_info = field.field_info field_info = field.field_info

12
tests/main.py

@ -1,5 +1,5 @@
import http import http
from typing import FrozenSet, Optional from typing import FrozenSet, List, Mapping, Optional
from fastapi import FastAPI, Path, Query from fastapi import FastAPI, Path, Query
@ -184,6 +184,16 @@ def get_query_param_required_type(query: int = Query()):
return f"foo bar {query}" return f"foo bar {query}"
@app.get("/query/params")
def get_query_params(query: Mapping[str, int] = Query({})):
return f"foo bar {query}"
@app.get("/query/sequence-params")
def get_sequence_query_params(query: Mapping[str, List[int]] = Query({})):
return f"foo bar {query}"
@app.get("/enum-status-code", status_code=http.HTTPStatus.CREATED) @app.get("/enum-status-code", status_code=http.HTTPStatus.CREATED)
def get_enum_status_code(): def get_enum_status_code():
return "foo bar" return "foo bar"

1195
tests/test_application.py

File diff suppressed because it is too large

13
tests/test_invalid_mapping_param.py

@ -0,0 +1,13 @@
from typing import List, Mapping
import pytest
from fastapi import FastAPI, Query
def test_invalid_sequence():
with pytest.raises(AssertionError):
app = FastAPI()
@app.get("/items/")
def read_items(q: Mapping[str, List[List[str]]] = Query(default=None)):
pass # pragma: no cover

14
tests/test_invalid_sequence_param.py

@ -1,4 +1,4 @@
from typing import Dict, List, Optional, Tuple from typing import Dict, List, Tuple
import pytest import pytest
from fastapi import FastAPI, Query from fastapi import FastAPI, Query
@ -39,15 +39,3 @@ def test_invalid_dict():
@app.get("/items/") @app.get("/items/")
def read_items(q: Dict[str, Item] = Query(default=None)): def read_items(q: Dict[str, Item] = Query(default=None)):
pass # pragma: no cover pass # pragma: no cover
def test_invalid_simple_dict():
with pytest.raises(AssertionError):
app = FastAPI()
class Item(BaseModel):
title: str
@app.get("/items/")
def read_items(q: Optional[dict] = Query(default=None)):
pass # pragma: no cover

Loading…
Cancel
Save