datamodel-code-generator
datamodel-code-generator copied to clipboard
allOf {a, b.b1, b.b2} breaks model
Describe the bug
As part of a much bigger API where I needed to use "allOf" to construct a schema in a DRY way I encountered the following problem:
When you have a schema with "allOf" using three references with a special inner structure the generator produces a broken model.
The structure is
"allOf": [
{ "a": {}},
{ "b": { "b1": {}},
{ "b": { "b2": {}},
]
The broken model looks like this:
...
class BB1(BaseModel):
b: Optional[B1] = None
class BB2(BaseModel):
b: Optional[B2] = None
class Model(A, BB1, BB2):
pass
So, you cannot have both b.b1 and b.b2 in the data.
To Reproduce
Find everything at https://github.com/TimmFitschen/datamode-code-generator-issues-2413
Example schema (api.json):
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"allOf": [
{ "$ref": "#/$defs/Data" },
{ "$ref": "#/$defs/SelfLink" },
{ "$ref": "#/$defs/CollectionLink"}
],
"$defs": {
"Data": {
"properties": {
"data": {
"type": "string"
} } },
"SelfLink": {
"properties": {
"links": {
"properties": {
"self": {
"type": "string"
} } } } },
"CollectionLink": {
"properties": {
"links": {
"properties": {
"collection": {
"type": "string"
} } } } } }
}
Used commandline:
$ datamodel-codegen --input api.json --output model.py
The model (model.py):
from __future__ import annotations
from typing import Optional
from pydantic import BaseModel
class Data(BaseModel):
data: Optional[str] = None
class Links(BaseModel):
self: Optional[str] = None
class SelfLink(BaseModel):
links: Optional[Links] = None
class Links1(BaseModel):
collection: Optional[str] = None
class CollectionLink(BaseModel):
links: Optional[Links1] = None
class Model(Data, SelfLink, CollectionLink):
pass
Expected behavior
My failing test:
from model import Model
test_data = {
"data": "test",
"links": {
"self": "https://localhost:8000/data/test",
"collection": "https://localhost:8000/data"
}
}
def test_instanciation():
model = Model(**test_data)
assert model.links.self is not None
assert model.links.collection is not None
Version:
- OS: Linux
- Python version: 3.12
- datamodel-code-generator version: 0.30.2
Additional context
I was able to find a workaround which is ok for me and might give you an idea what it going wrong. I added another option to the "allOf" which is meaningless but triggers a different model generation:
...
"allOf": [
{ "$ref": "#/$defs/Data" },
{ "$ref": "#/$defs/SelfLink" },
{ "$ref": "#/$defs/CollectionLink"},
{ "properties": {
"links": {
"type": "object"
}
}
}
],
...
Now my model has
...
class Workaround(Data, SelfLink, CollectionLink):
links: Optional[Dict[str, Any]] = None
...
and the following test is passing:
...
def test_workaround():
model = Workaround(**test_data)
print(model.model_dump())
assert model.links["self"] is not None
assert model.links["collection"] is not None
Thank you for your effort! I hope this helps.
See code: https://github.com/TimmFitschen/datamode-code-generator-issues-2413