Skip to content

Commit 3914be8

Browse files
committed
Support ClassVar for Pydantic v2
1 parent 1b931d5 commit 3914be8

5 files changed

Lines changed: 49 additions & 1 deletion

File tree

src/datamodel_code_generator/model/pydantic_v2/base_model.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
IMPORT_VALIDATION_INFO,
3232
IMPORT_VALIDATOR_FUNCTION_WRAP_HANDLER,
3333
)
34+
from datamodel_code_generator.model.imports import IMPORT_CLASSVAR
3435
from datamodel_code_generator.reference import ModelResolver
3536
from datamodel_code_generator.types import chain_as_tuple
3637
from datamodel_code_generator.util import field_validator, model_validate, model_validator
@@ -187,11 +188,21 @@ def _has_discriminator_in_data_type(self) -> bool:
187188
"""Check if any nested DataType has a discriminator."""
188189
return any(dt.discriminator for dt in self.data_type.all_data_types)
189190

191+
@property
192+
def class_var_type_hint(self) -> str:
193+
return f"ClassVar[{self.type_hint}]"
194+
195+
@property
196+
def is_class_var(self) -> bool:
197+
return self.extras.get("x-is_classvar") is True
198+
190199
@property
191200
def imports(self) -> tuple[Import, ...]:
192201
"""Get all required imports including AliasChoices and Field for discriminator."""
193202
base_imports = super().imports
194203
extra_imports: list[Import] = []
204+
if self.is_class_var:
205+
extra_imports.append(IMPORT_CLASSVAR)
195206
if self.validation_aliases:
196207
from datamodel_code_generator.model.pydantic_v2.imports import IMPORT_ALIAS_CHOICES # noqa: PLC0415
197208

src/datamodel_code_generator/model/template/pydantic_v2/BaseModel.jinja2

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ class {{ class_name }}({{ base_class }}):{% if comment is defined %} # {{ comme
1616
{%- endfilter %}
1717
{%- endif %}
1818
{%- for field in fields %}
19-
{%- if not field.annotated and field.field %}
19+
{%- if field.is_class_var and field.represented_default %}
20+
{{ field.name }}: {{ field.class_var_type_hint }} = {{ field.represented_default }}
21+
{%- elif not field.annotated and field.field %}
2022
{{ field.name }}: {{ field.type_hint }} = {{ field.field }}
2123
{%- else %}
2224
{%- if field.annotated %}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# generated by datamodel-codegen:
2+
# filename: has_classvar_extra.json
3+
# timestamp: 2019-07-26T00:00:00+00:00
4+
5+
from __future__ import annotations
6+
7+
from typing import ClassVar
8+
9+
from pydantic import BaseModel, Field
10+
11+
12+
class Model(BaseModel):
13+
namespace: ClassVar[str | None] = 'test'
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"type": "object",
3+
"properties": {
4+
"namespace": {
5+
"type": "string",
6+
"x-is_classvar": true,
7+
"default": "test"
8+
}
9+
}
10+
}

tests/main/jsonschema/test_main_jsonschema.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7903,3 +7903,15 @@ def test_validators_requires_pydantic_v2(output_file: Path, tmp_path: Path, caps
79037903
capsys=capsys,
79047904
expected_stderr_contains="--validators option requires Pydantic v2",
79057905
)
7906+
7907+
7908+
def test_jsonschema_classvar_extra_pydantic_v2(output_file: Path) -> None:
7909+
"""Test default value handling."""
7910+
run_main_and_assert(
7911+
input_path=JSON_SCHEMA_DATA_PATH / "has_classvar_extra.json",
7912+
output_path=output_file,
7913+
input_file_type="jsonschema",
7914+
assert_func=assert_file_content,
7915+
expected_file="has_classvar_extra.py",
7916+
extra_args=["--output-model-type", "pydantic_v2.BaseModel", "--field-include-all-keys"],
7917+
)

0 commit comments

Comments
 (0)