Skip to content

Commit 3397d4d

Browse files
voegtlelLukas Voegtletiangolo
authored
✨ Implement response_model_exclude_defaults and response_model_exclude_none (fastapi#1166)
* Implemented response_model_exclude_defaults and response_model_exclude_none to be compatible pydantic options. * 🚚 Rename and invert include_none to exclude_none to keep in sync with Pydantic Co-authored-by: Lukas Voegtle <lukas.voegtle@sick.de> Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
1 parent 766157b commit 3397d4d

File tree

6 files changed

+223
-16
lines changed

6 files changed

+223
-16
lines changed

fastapi/applications.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ def add_api_route(
171171
response_model_by_alias: bool = True,
172172
response_model_skip_defaults: bool = None,
173173
response_model_exclude_unset: bool = False,
174+
response_model_exclude_defaults: bool = False,
175+
response_model_exclude_none: bool = False,
174176
include_in_schema: bool = True,
175177
response_class: Type[Response] = None,
176178
name: str = None,
@@ -197,6 +199,8 @@ def add_api_route(
197199
response_model_exclude_unset=bool(
198200
response_model_exclude_unset or response_model_skip_defaults
199201
),
202+
response_model_exclude_defaults=response_model_exclude_defaults,
203+
response_model_exclude_none=response_model_exclude_none,
200204
include_in_schema=include_in_schema,
201205
response_class=response_class or self.default_response_class,
202206
name=name,
@@ -222,6 +226,8 @@ def api_route(
222226
response_model_by_alias: bool = True,
223227
response_model_skip_defaults: bool = None,
224228
response_model_exclude_unset: bool = False,
229+
response_model_exclude_defaults: bool = False,
230+
response_model_exclude_none: bool = False,
225231
include_in_schema: bool = True,
226232
response_class: Type[Response] = None,
227233
name: str = None,
@@ -250,6 +256,8 @@ def decorator(func: Callable) -> Callable:
250256
response_model_exclude_unset=bool(
251257
response_model_exclude_unset or response_model_skip_defaults
252258
),
259+
response_model_exclude_defaults=response_model_exclude_defaults,
260+
response_model_exclude_none=response_model_exclude_none,
253261
include_in_schema=include_in_schema,
254262
response_class=response_class or self.default_response_class,
255263
name=name,
@@ -309,6 +317,8 @@ def get(
309317
response_model_by_alias: bool = True,
310318
response_model_skip_defaults: bool = None,
311319
response_model_exclude_unset: bool = False,
320+
response_model_exclude_defaults: bool = False,
321+
response_model_exclude_none: bool = False,
312322
include_in_schema: bool = True,
313323
response_class: Type[Response] = None,
314324
name: str = None,
@@ -334,6 +344,8 @@ def get(
334344
response_model_exclude_unset=bool(
335345
response_model_exclude_unset or response_model_skip_defaults
336346
),
347+
response_model_exclude_defaults=response_model_exclude_defaults,
348+
response_model_exclude_none=response_model_exclude_none,
337349
include_in_schema=include_in_schema,
338350
response_class=response_class or self.default_response_class,
339351
name=name,
@@ -359,6 +371,8 @@ def put(
359371
response_model_by_alias: bool = True,
360372
response_model_skip_defaults: bool = None,
361373
response_model_exclude_unset: bool = False,
374+
response_model_exclude_defaults: bool = False,
375+
response_model_exclude_none: bool = False,
362376
include_in_schema: bool = True,
363377
response_class: Type[Response] = None,
364378
name: str = None,
@@ -384,6 +398,8 @@ def put(
384398
response_model_exclude_unset=bool(
385399
response_model_exclude_unset or response_model_skip_defaults
386400
),
401+
response_model_exclude_defaults=response_model_exclude_defaults,
402+
response_model_exclude_none=response_model_exclude_none,
387403
include_in_schema=include_in_schema,
388404
response_class=response_class or self.default_response_class,
389405
name=name,
@@ -409,6 +425,8 @@ def post(
409425
response_model_by_alias: bool = True,
410426
response_model_skip_defaults: bool = None,
411427
response_model_exclude_unset: bool = False,
428+
response_model_exclude_defaults: bool = False,
429+
response_model_exclude_none: bool = False,
412430
include_in_schema: bool = True,
413431
response_class: Type[Response] = None,
414432
name: str = None,
@@ -434,6 +452,8 @@ def post(
434452
response_model_exclude_unset=bool(
435453
response_model_exclude_unset or response_model_skip_defaults
436454
),
455+
response_model_exclude_defaults=response_model_exclude_defaults,
456+
response_model_exclude_none=response_model_exclude_none,
437457
include_in_schema=include_in_schema,
438458
response_class=response_class or self.default_response_class,
439459
name=name,
@@ -459,6 +479,8 @@ def delete(
459479
response_model_by_alias: bool = True,
460480
response_model_skip_defaults: bool = None,
461481
response_model_exclude_unset: bool = False,
482+
response_model_exclude_defaults: bool = False,
483+
response_model_exclude_none: bool = False,
462484
include_in_schema: bool = True,
463485
response_class: Type[Response] = None,
464486
name: str = None,
@@ -484,6 +506,8 @@ def delete(
484506
response_model_exclude_unset=bool(
485507
response_model_exclude_unset or response_model_skip_defaults
486508
),
509+
response_model_exclude_defaults=response_model_exclude_defaults,
510+
response_model_exclude_none=response_model_exclude_none,
487511
include_in_schema=include_in_schema,
488512
response_class=response_class or self.default_response_class,
489513
name=name,
@@ -509,6 +533,8 @@ def options(
509533
response_model_by_alias: bool = True,
510534
response_model_skip_defaults: bool = None,
511535
response_model_exclude_unset: bool = False,
536+
response_model_exclude_defaults: bool = False,
537+
response_model_exclude_none: bool = False,
512538
include_in_schema: bool = True,
513539
response_class: Type[Response] = None,
514540
name: str = None,
@@ -534,6 +560,8 @@ def options(
534560
response_model_exclude_unset=bool(
535561
response_model_exclude_unset or response_model_skip_defaults
536562
),
563+
response_model_exclude_defaults=response_model_exclude_defaults,
564+
response_model_exclude_none=response_model_exclude_none,
537565
include_in_schema=include_in_schema,
538566
response_class=response_class or self.default_response_class,
539567
name=name,
@@ -559,6 +587,8 @@ def head(
559587
response_model_by_alias: bool = True,
560588
response_model_skip_defaults: bool = None,
561589
response_model_exclude_unset: bool = False,
590+
response_model_exclude_defaults: bool = False,
591+
response_model_exclude_none: bool = False,
562592
include_in_schema: bool = True,
563593
response_class: Type[Response] = None,
564594
name: str = None,
@@ -584,6 +614,8 @@ def head(
584614
response_model_exclude_unset=bool(
585615
response_model_exclude_unset or response_model_skip_defaults
586616
),
617+
response_model_exclude_defaults=response_model_exclude_defaults,
618+
response_model_exclude_none=response_model_exclude_none,
587619
include_in_schema=include_in_schema,
588620
response_class=response_class or self.default_response_class,
589621
name=name,
@@ -609,6 +641,8 @@ def patch(
609641
response_model_by_alias: bool = True,
610642
response_model_skip_defaults: bool = None,
611643
response_model_exclude_unset: bool = False,
644+
response_model_exclude_defaults: bool = False,
645+
response_model_exclude_none: bool = False,
612646
include_in_schema: bool = True,
613647
response_class: Type[Response] = None,
614648
name: str = None,
@@ -634,6 +668,8 @@ def patch(
634668
response_model_exclude_unset=bool(
635669
response_model_exclude_unset or response_model_skip_defaults
636670
),
671+
response_model_exclude_defaults=response_model_exclude_defaults,
672+
response_model_exclude_none=response_model_exclude_none,
637673
include_in_schema=include_in_schema,
638674
response_class=response_class or self.default_response_class,
639675
name=name,
@@ -659,6 +695,8 @@ def trace(
659695
response_model_by_alias: bool = True,
660696
response_model_skip_defaults: bool = None,
661697
response_model_exclude_unset: bool = False,
698+
response_model_exclude_defaults: bool = False,
699+
response_model_exclude_none: bool = False,
662700
include_in_schema: bool = True,
663701
response_class: Type[Response] = None,
664702
name: str = None,
@@ -684,6 +722,8 @@ def trace(
684722
response_model_exclude_unset=bool(
685723
response_model_exclude_unset or response_model_skip_defaults
686724
),
725+
response_model_exclude_defaults=response_model_exclude_defaults,
726+
response_model_exclude_none=response_model_exclude_none,
687727
include_in_schema=include_in_schema,
688728
response_class=response_class or self.default_response_class,
689729
name=name,

fastapi/encoders.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ def jsonable_encoder(
3434
by_alias: bool = True,
3535
skip_defaults: bool = None,
3636
exclude_unset: bool = False,
37-
include_none: bool = True,
37+
exclude_defaults: bool = False,
38+
exclude_none: bool = False,
3839
custom_encoder: dict = {},
3940
sqlalchemy_safe: bool = True,
4041
) -> Any:
@@ -58,8 +59,12 @@ def jsonable_encoder(
5859
exclude=exclude,
5960
by_alias=by_alias,
6061
exclude_unset=bool(exclude_unset or skip_defaults),
62+
exclude_none=exclude_none,
63+
exclude_defaults=exclude_defaults,
6164
)
6265
else: # pragma: nocover
66+
if exclude_defaults:
67+
raise ValueError("Cannot use exclude_defaults")
6368
obj_dict = obj.dict(
6469
include=include,
6570
exclude=exclude,
@@ -68,7 +73,8 @@ def jsonable_encoder(
6873
)
6974
return jsonable_encoder(
7075
obj_dict,
71-
include_none=include_none,
76+
exclude_none=exclude_none,
77+
exclude_defaults=exclude_defaults,
7278
custom_encoder=encoder,
7379
sqlalchemy_safe=sqlalchemy_safe,
7480
)
@@ -87,22 +93,22 @@ def jsonable_encoder(
8793
or (not isinstance(key, str))
8894
or (not key.startswith("_sa"))
8995
)
90-
and (value is not None or include_none)
96+
and (value is not None or not exclude_none)
9197
and ((include and key in include) or key not in exclude)
9298
):
9399
encoded_key = jsonable_encoder(
94100
key,
95101
by_alias=by_alias,
96102
exclude_unset=exclude_unset,
97-
include_none=include_none,
103+
exclude_none=exclude_none,
98104
custom_encoder=custom_encoder,
99105
sqlalchemy_safe=sqlalchemy_safe,
100106
)
101107
encoded_value = jsonable_encoder(
102108
value,
103109
by_alias=by_alias,
104110
exclude_unset=exclude_unset,
105-
include_none=include_none,
111+
exclude_none=exclude_none,
106112
custom_encoder=custom_encoder,
107113
sqlalchemy_safe=sqlalchemy_safe,
108114
)
@@ -118,7 +124,8 @@ def jsonable_encoder(
118124
exclude=exclude,
119125
by_alias=by_alias,
120126
exclude_unset=exclude_unset,
121-
include_none=include_none,
127+
exclude_defaults=exclude_defaults,
128+
exclude_none=exclude_none,
122129
custom_encoder=custom_encoder,
123130
sqlalchemy_safe=sqlalchemy_safe,
124131
)
@@ -153,7 +160,8 @@ def jsonable_encoder(
153160
data,
154161
by_alias=by_alias,
155162
exclude_unset=exclude_unset,
156-
include_none=include_none,
163+
exclude_defaults=exclude_defaults,
164+
exclude_none=exclude_none,
157165
custom_encoder=custom_encoder,
158166
sqlalchemy_safe=sqlalchemy_safe,
159167
)

fastapi/openapi/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def get_openapi_security_definitions(flat_dependant: Dependant) -> Tuple[Dict, L
8181
security_definition = jsonable_encoder(
8282
security_requirement.security_scheme.model,
8383
by_alias=True,
84-
include_none=False,
84+
exclude_none=True,
8585
)
8686
security_name = security_requirement.security_scheme.scheme_name
8787
security_definitions[security_name] = security_definition
@@ -310,4 +310,4 @@ def get_openapi(
310310
if components:
311311
output["components"] = components
312312
output["paths"] = paths
313-
return jsonable_encoder(OpenAPI(**output), by_alias=True, include_none=False)
313+
return jsonable_encoder(OpenAPI(**output), by_alias=True, exclude_none=True)

0 commit comments

Comments
 (0)