Skip to content
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ Options:
--specify-tags TEXT
-c, --custom-visitor PATH
--disable-timestamp
--include-request-argument Auto-inject a FastAPI Request parameter into
operations when not present.
-d, --output-model-type [pydantic_v2.BaseModel|pydantic_v2.dataclass|dataclasses.dataclass|typing.TypedDict|msgspec.Struct]
[default: pydantic_v2.BaseModel]
-p, --python-version [3.10|3.11|3.12|3.13|3.14]
Expand Down
10 changes: 10 additions & 0 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Options:
--specify-tags TEXT
-c, --custom-visitor PATH
--disable-timestamp
--include-request-argument Auto-inject a FastAPI Request parameter into
operations when not present.
-d, --output-model-type [pydantic_v2.BaseModel|pydantic_v2.dataclass|dataclasses.dataclass|typing.TypedDict|msgspec.Struct]
[default: pydantic_v2.BaseModel]
-p, --python-version [3.10|3.11|3.12|3.13|3.14]
Expand Down Expand Up @@ -72,6 +74,14 @@ Render generated files with a custom template directory.

Input schema: `openapi/custom_template_security/custom_security.yaml`

### --include-request-argument

Auto-inject a FastAPI Request argument in generated operation signatures when not present.

`fastapi-codegen --input openapi/default_template/simple.yaml --output app --include-request-argument`

Input schema: `openapi/default_template/simple.yaml`

### --encoding

Read the input schema using an explicit text encoding.
Expand Down
2 changes: 2 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ Options:
--specify-tags TEXT
-c, --custom-visitor PATH
--disable-timestamp
--include-request-argument Auto-inject a FastAPI Request parameter into
operations when not present.
-d, --output-model-type [pydantic_v2.BaseModel|pydantic_v2.dataclass|dataclasses.dataclass|typing.TypedDict|msgspec.Struct]
[default: pydantic_v2.BaseModel]
-p, --python-version [3.10|3.11|3.12|3.13|3.14]
Expand Down
12 changes: 12 additions & 0 deletions docs/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ Options:
--specify-tags TEXT
-c, --custom-visitor PATH
--disable-timestamp
--include-request-argument Auto-inject a FastAPI Request parameter into
operations when not present.
-d, --output-model-type [pydantic_v2.BaseModel|pydantic_v2.dataclass|dataclasses.dataclass|typing.TypedDict|msgspec.Struct]
[default: pydantic_v2.BaseModel]
-p, --python-version [3.10|3.11|3.12|3.13|3.14]
Expand Down Expand Up @@ -339,6 +341,8 @@ Options:
--specify-tags TEXT
-c, --custom-visitor PATH
--disable-timestamp
--include-request-argument Auto-inject a FastAPI Request parameter into
operations when not present.
-d, --output-model-type [pydantic_v2.BaseModel|pydantic_v2.dataclass|dataclasses.dataclass|typing.TypedDict|msgspec.Struct]
[default: pydantic_v2.BaseModel]
-p, --python-version [3.10|3.11|3.12|3.13|3.14]
Expand Down Expand Up @@ -393,6 +397,14 @@ Render generated files with a custom template directory.

Input schema: `openapi/custom_template_security/custom_security.yaml`

### --include-request-argument

Auto-inject a FastAPI Request argument in generated operation signatures when not present.

`fastapi-codegen --input openapi/default_template/simple.yaml --output app --include-request-argument`

Input schema: `openapi/default_template/simple.yaml`

### --encoding

Read the input schema using an explicit text encoding.
Expand Down
1 change: 1 addition & 0 deletions fastapi_code_generator/_types/generate_config_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class GenerateConfigDict(TypedDict):
encoding: NotRequired[str]
enum_field_as_literal: NotRequired[Literal['all', 'one', 'none'] | None]
generate_routers: NotRequired[bool]
include_request_argument: NotRequired[bool]
input_file: str
model_file: NotRequired[str | None]
model_template_dir: NotRequired[str | None]
Expand Down
11 changes: 11 additions & 0 deletions fastapi_code_generator/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ def main(
None, "--custom-visitor", "-c"
),
disable_timestamp: bool = typer.Option(False, "--disable-timestamp"),
include_request_argument: bool = typer.Option(
False,
"--include-request-argument",
help=(
"Auto-inject a FastAPI Request parameter into operations when not "
"present."
),
),
output_model_type: DataModelType = typer.Option(
DataModelType.PydanticV2BaseModel.value, "--output-model-type", "-d"
),
Expand Down Expand Up @@ -125,6 +133,7 @@ def main(
enum_field_as_literal=enum_field_as_literal or None,
custom_visitors=custom_visitors,
disable_timestamp=disable_timestamp,
include_request_argument=include_request_argument,
generate_routers=generate_routers,
specify_tags=specify_tags,
output_model_type=output_model_type,
Expand Down Expand Up @@ -163,6 +172,7 @@ def generate_code(
enum_field_as_literal: Optional[LiteralType] = None,
custom_visitors: Optional[List[Path]] = None,
disable_timestamp: bool = False,
include_request_argument: bool = False,
generate_routers: Optional[bool] = None,
specify_tags: Optional[str] = None,
output_model_type: DataModelType = DataModelType.PydanticV2BaseModel,
Expand Down Expand Up @@ -195,6 +205,7 @@ def generate_code(
dump_resolve_reference_action=data_model_types.dump_resolve_reference_action,
custom_template_dir=model_template_dir,
target_python_version=python_version,
include_request_argument=include_request_argument,
use_annotated=use_annotated,
)

Expand Down
8 changes: 8 additions & 0 deletions fastapi_code_generator/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,14 @@ class GenerateConfig(BaseModel):
description="Omit timestamp headers from generated files.",
json_schema_extra=cast(Any, _cli_metadata("--disable-timestamp")),
)
include_request_argument: bool = Field(
default=False,
description=(
"Auto-inject a FastAPI Request argument in generated operation "
"signatures when not present."
),
json_schema_extra=cast(Any, _cli_metadata("--include-request-argument")),
)
output_model_type: OutputModelTypeName = Field(
default="pydantic_v2.BaseModel",
description="Model backend passed through to datamodel-code-generator.",
Expand Down
77 changes: 52 additions & 25 deletions fastapi_code_generator/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,37 +101,35 @@ def __str__(self) -> str: # pragma: no cover
return self.argument

@property
def argument(self) -> str: # pragma: no cover
def resolved_type_hint(self) -> UsefulStr:
if self.field is None:
type_hint = self.type_hint
else:
type_hint = (
UsefulStr(self.field.type_hint)
if not isinstance(self.field, list)
else UsefulStr(
f"Union[{', '.join(field.type_hint for field in self.field)}]"
)
return self.type_hint
return (
UsefulStr(self.field.type_hint)
if not isinstance(self.field, list)
else UsefulStr(
f"Union[{', '.join(field.type_hint for field in self.field)}]"
)
)

@property
def argument(self) -> str: # pragma: no cover
type_hint = self.resolved_type_hint
if self.default is None and self.required:
return f'{self.name}: {type_hint}'
return f'{self.name}: {type_hint} = {self.default}'

@property
def snakecase(self) -> str:
if self.field is None:
type_hint = self.type_hint
else:
type_hint = (
UsefulStr(self.field.type_hint)
if not isinstance(self.field, list)
else UsefulStr(
f"Union[{', '.join(field.type_hint for field in self.field)}]"
)
)
type_hint = self.resolved_type_hint
if self.default is None and self.required:
return f'{stringcase.snakecase(self.name)}: {type_hint}'
return f'{stringcase.snakecase(self.name)}: {type_hint} = {self.default}'

@property
def plain_parameter(self) -> str:
return self.snakecase


class Operation(CachedPropertyModel):
method: UsefulStr
Expand Down Expand Up @@ -191,20 +189,34 @@ def type(self) -> UsefulStr:
"""
return self.method

@cached_property
def _merged_arguments(self) -> List[Argument]:
return Operation.merge_arguments_with_union(self.arguments_list)

@property
def arguments(self) -> str: # pragma: no cover
sorted_arguments = Operation.merge_arguments_with_union(self.arguments_list)
return ", ".join(argument.argument for argument in sorted_arguments)
return ", ".join(argument.argument for argument in self._merged_arguments)

@property
def snake_case_arguments(self) -> str:
sorted_arguments = Operation.merge_arguments_with_union(self.arguments_list)
return ", ".join(argument.snakecase for argument in sorted_arguments)
return ", ".join(argument.snakecase for argument in self._merged_arguments)

@property
def plain_arguments(self) -> str:
return ", ".join(
stringcase.snakecase(argument.name) for argument in self._merged_arguments
)

@property
def plain_parameters(self) -> str:
return ", ".join(
argument.plain_parameter for argument in self._merged_arguments
)

@property
def imports(self) -> Imports:
imports = Imports()
for argument in Operation.merge_arguments_with_union(self.arguments_list):
for argument in self._merged_arguments:
if isinstance(argument.field, list):
for field in argument.field:
imports.append(field.data_type.import_)
Expand Down Expand Up @@ -274,6 +286,7 @@ def __init__(
custom_class_name_generator: Optional[Callable[[str], str]] = None,
field_extra_keys: Optional[Set[str]] = None,
field_include_all_keys: bool = False,
include_request_argument: bool = False,
use_annotated: bool = False,
):
super().__init__(
Expand Down Expand Up @@ -320,6 +333,7 @@ def __init__(
self._temporary_operation: Dict[str, Any] = {}
self.imports_for_fastapi: Imports = Imports()
self.data_types: List[DataType] = []
self.include_request_argument = include_request_argument

def parse_info(self) -> Optional[Dict[str, Any]]:
if not isinstance(self.raw_obj, dict): # pragma: no cover
Expand Down Expand Up @@ -442,6 +456,19 @@ def get_argument_list(
if request:
arguments.append(request)

if self.include_request_argument and not any(
argument.name == "request" for argument in arguments
):
arguments.insert(
0,
Argument(
name='request', # type: ignore
type_hint='Request', # type: ignore
required=True,
),
)
self.imports_for_fastapi.append(Import.from_full_path("fastapi.Request"))
Comment thread
coderabbitai[bot] marked this conversation as resolved.

positional_argument: bool = False
for argument in arguments:
if positional_argument and argument.required and argument.default is None:
Expand Down Expand Up @@ -502,7 +529,7 @@ def parse_request_body(
)
)
self.imports_for_fastapi.append(
Import.from_full_path('starlette.requests.Request')
Import.from_full_path('fastapi.Request')
)
elif media_type == 'application/octet-stream':
arguments.append(
Expand Down
24 changes: 24 additions & 0 deletions fastapi_code_generator/prompt_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,17 @@
'type': 'boolean',
'choices': [],
},
{
'name': 'include_request_argument',
'cli_flags': ['--include-request-argument'],
'description': 'Auto-inject a FastAPI Request argument in '
'generated operation signatures when not present.',
'required': False,
'default': False,
'multiple': False,
'type': 'boolean',
'choices': [],
},
{
'name': 'output_model_type',
'cli_flags': ['--output-model-type', '-d'],
Expand Down Expand Up @@ -212,6 +223,19 @@
],
'input_schema': 'openapi/custom_template_security/custom_security.yaml',
},
{
'options': ['--include-request-argument'],
'description': 'Auto-inject a FastAPI Request argument in generated '
'operation signatures when not present.',
'cli_args': [
'--input',
'openapi/default_template/simple.yaml',
'--output',
'app',
'--include-request-argument',
],
'input_schema': 'openapi/default_template/simple.yaml',
},
{
'options': ['--encoding'],
'description': 'Read the input schema using an explicit text ' 'encoding.',
Expand Down
1 change: 0 additions & 1 deletion tests/data/expected/openapi/coverage/model_options/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from typing import List, Optional, Union

from fastapi import FastAPI, Path, Query, Request
from starlette.requests import Request

from .custom_models import (
Error,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from typing import List, Optional, Union

from fastapi import FastAPI, Path, Query, Request
from starlette.requests import Request

from .models import (
Error,
Expand Down
Loading
Loading