Repository: koxudaxi/datamodel-code-generator

Issue:
`update_forward_refs()` is not created for all necessary models
**Describe the bug**
`update_forward_refs()` is not called for all necessary models if I use inheritance in the models.

**To Reproduce**

Example schema:
```json
{
    "title": "PersonsBestFriend",
    "description": "This is the main model.",
    "type": "object",
    "properties": {
      "people": {
        "title": "People",
        "type": "array",
        "items": {
          "$ref": "#/definitions/Person"
        }
      },
      "dogs": {
        "title": "Dogs",
        "type": "array",
        "items": {
          "$ref": "#/definitions/Dog"
        }
      },
      "dog_base": {
        "$ref": "#/definitions/DogBase"
      },
      "dog_relationships": {
        "$ref": "#/definitions/DogRelationships"
      },
      "person_base": {
        "$ref": "#/definitions/PersonBase"
      },
      "person_relationships": {
        "$ref": "#/definitions/PersonRelationships"
      }
    },
    "definitions": {
      "Person": {
        "title": "Person",
        "allOf": [
            {"$ref": "#/definitions/PersonBase"},
            {"$ref": "#/definitions/PersonRelationships"}
        ]
      },
      "Dog": {
        "title": "Dog",
        "allOf": [
            {"$ref": "#/definitions/DogBase"},
            {"$ref": "#/definitions/DogRelationships"}
        ]
      },
      "DogBase": {
        "title": "DogBase",
        "type": "object",
        "properties": {
          "name": {
            "title": "Name",
            "type": "string"
          },
          "woof": {
            "title": "Woof",
            "default": true,
            "type": "boolean"
          }
        }
      },
      "DogRelationships": {
        "title": "DogRelationships",
        "type": "object",
        "properties": {
          "people": {
            "title": "People",
            "type": "array",
            "items": {
              "$ref": "#/definitions/Person"
            }
          }
        }
      },
      "PersonBase": {
        "title": "PersonBase",
        "type": "object",
        "properties": {
          "name": {
            "title": "Name",
            "type": "string"
          }
        }
      },
      "PersonRelationships": {
        "title": "PersonRelationships",
        "type": "object",
        "properties": {
          "people": {
            "title": "People",
            "type": "array",
            "items": {
              "$ref": "#/definitions/Person"
            }
          }
        }
      }
    }
  }

```

Used commandline:
```
$ datamodel-codegen --input pbf.json --output pbf.py
```

generates:
```python
# generated by datamodel-codegen:
#   filename:  mbf.json
#   timestamp: 2022-05-12T21:56:35+00:00

from __future__ import annotations

from typing import List, Optional

from pydantic import BaseModel, Field


class DogBase(BaseModel):
    name: Optional[str] = Field(None, title='Name')
    woof: Optional[bool] = Field(True, title='Woof')


class PersonBase(BaseModel):
    name: Optional[str] = Field(None, title='Name')


class PersonsBestFriend(BaseModel):
    people: Optional[List[Person]] = Field(None, title='People')
    dogs: Optional[List[Dog]] = Field(None, title='Dogs')
    dog_base: Optional[DogBase] = None
    dog_relationships: Optional[DogRelationships] = None
    person_base: Optional[PersonBase] = None
    person_relationships: Optional[PersonRelationships] = None


class DogRelationships(BaseModel):
    people: Optional[List[Person]] = Field(None, title='People')


class PersonRelationships(BaseModel):
    people: Optional[List[Person]] = Field(None, title='People')


class Dog(DogBase, DogRelationships):
    pass


class Person(PersonBase, PersonRelationships):
    pass


PersonsBestFriend.update_forward_refs()
DogRelationships.update_forward_refs()
PersonRelationships.update_forward_refs()
```

but if I call `PersonsBestFriend.schema()` I get:
```
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "pydantic/main.py", line 647, in pydantic.main.BaseModel.schema
  File "pydantic/schema.py", line 185, in pydantic.schema.model_schema
  File "pydantic/schema.py", line 617, in pydantic.schema.model_process_schema
  File "pydantic/schema.py", line 658, in pydantic.schema.model_type_schema
  File "pydantic/schema.py", line 258, in pydantic.schema.field_schema
  File "pydantic/schema.py", line 498, in pydantic.schema.field_type_schema
  File "pydantic/schema.py", line 925, in pydantic.schema.field_singleton_schema
  File "pydantic/schema.py", line 617, in pydantic.schema.model_process_schema
  File "pydantic/schema.py", line 658, in pydantic.schema.model_type_schema
  File "pydantic/schema.py", line 258, in pydantic.schema.field_schema
  File "pydantic/schema.py", line 563, in pydantic.schema.field_type_schema
  File "pydantic/schema.py", line 922, in pydantic.schema.field_singleton_schema
  File "/usr/lib/python3.8/abc.py", line 102, in __subclasscheck__
    return _abc_subclasscheck(cls, subclass)
TypeError: issubclass() arg 1 must be a class
```

**Expected behavior**
if I add `Dog.update_forward_refs()`, then I get expected behavior.

**Version:**
 - OS: Linux
 - Python version: 3.7
 - datamodel-code-generator version: 0.12.0

**Additional context**
Add any other context about the problem here.



Discussion:

Resolving patch:
diff --git a/datamodel_code_generator/parser/base.py b/datamodel_code_generator/parser/base.py
--- a/datamodel_code_generator/parser/base.py
+++ b/datamodel_code_generator/parser/base.py
@@ -159,6 +159,16 @@ def sort_data_models(
             unresolved_model = (
                 model.reference_classes - {model.path} - set(sorted_data_models)
             )
+            base_models = [
+                getattr(s.reference, "path", None) for s in model.base_classes
+            ]
+            update_action_parent = set(require_update_action_models).intersection(
+                base_models
+            )
+            if not unresolved_model and update_action_parent:
+                sorted_data_models[model.path] = model
+                require_update_action_models.append(model.path)
+                continue
             if not unresolved_model:
                 sorted_data_models[model.path] = model
                 continue


Test patch:
diff --git a/tests/data/expected/main/main_inheritance_forward_ref/output.py b/tests/data/expected/main/main_inheritance_forward_ref/output.py
new file mode 100644
--- /dev/null
+++ b/tests/data/expected/main/main_inheritance_forward_ref/output.py
@@ -0,0 +1,50 @@
+# generated by datamodel-codegen:
+#   filename:  inheritance_forward_ref.json
+#   timestamp: 2019-07-26T00:00:00+00:00
+
+from __future__ import annotations
+
+from typing import List, Optional
+
+from pydantic import BaseModel, Field
+
+
+class DogBase(BaseModel):
+    name: Optional[str] = Field(None, title='Name')
+    woof: Optional[bool] = Field(True, title='Woof')
+
+
+class PersonBase(BaseModel):
+    name: Optional[str] = Field(None, title='Name')
+
+
+class PersonsBestFriend(BaseModel):
+    people: Optional[List[Person]] = Field(None, title='People')
+    dogs: Optional[List[Dog]] = Field(None, title='Dogs')
+    dog_base: Optional[DogBase] = None
+    dog_relationships: Optional[DogRelationships] = None
+    person_base: Optional[PersonBase] = None
+    person_relationships: Optional[PersonRelationships] = None
+
+
+class DogRelationships(BaseModel):
+    people: Optional[List[Person]] = Field(None, title='People')
+
+
+class PersonRelationships(BaseModel):
+    people: Optional[List[Person]] = Field(None, title='People')
+
+
+class Dog(DogBase, DogRelationships):
+    pass
+
+
+class Person(PersonBase, PersonRelationships):
+    pass
+
+
+PersonsBestFriend.update_forward_refs()
+DogRelationships.update_forward_refs()
+PersonRelationships.update_forward_refs()
+Dog.update_forward_refs()
+Person.update_forward_refs()
diff --git a/tests/data/jsonschema/inheritance_forward_ref.json b/tests/data/jsonschema/inheritance_forward_ref.json
new file mode 100644
--- /dev/null
+++ b/tests/data/jsonschema/inheritance_forward_ref.json
@@ -0,0 +1,100 @@
+{
+    "title": "PersonsBestFriend",
+    "description": "This is the main model.",
+    "type": "object",
+    "properties": {
+      "people": {
+        "title": "People",
+        "type": "array",
+        "items": {
+          "$ref": "#/definitions/Person"
+        }
+      },
+      "dogs": {
+        "title": "Dogs",
+        "type": "array",
+        "items": {
+          "$ref": "#/definitions/Dog"
+        }
+      },
+      "dog_base": {
+        "$ref": "#/definitions/DogBase"
+      },
+      "dog_relationships": {
+        "$ref": "#/definitions/DogRelationships"
+      },
+      "person_base": {
+        "$ref": "#/definitions/PersonBase"
+      },
+      "person_relationships": {
+        "$ref": "#/definitions/PersonRelationships"
+      }
+    },
+    "definitions": {
+      "Person": {
+        "title": "Person",
+        "allOf": [
+            {"$ref": "#/definitions/PersonBase"},
+            {"$ref": "#/definitions/PersonRelationships"}
+        ]
+      },
+      "Dog": {
+        "title": "Dog",
+        "allOf": [
+            {"$ref": "#/definitions/DogBase"},
+            {"$ref": "#/definitions/DogRelationships"}
+        ]
+      },
+      "DogBase": {
+        "title": "DogBase",
+        "type": "object",
+        "properties": {
+          "name": {
+            "title": "Name",
+            "type": "string"
+          },
+          "woof": {
+            "title": "Woof",
+            "default": true,
+            "type": "boolean"
+          }
+        }
+      },
+      "DogRelationships": {
+        "title": "DogRelationships",
+        "type": "object",
+        "properties": {
+          "people": {
+            "title": "People",
+            "type": "array",
+            "items": {
+              "$ref": "#/definitions/Person"
+            }
+          }
+        }
+      },
+      "PersonBase": {
+        "title": "PersonBase",
+        "type": "object",
+        "properties": {
+          "name": {
+            "title": "Name",
+            "type": "string"
+          }
+        }
+      },
+      "PersonRelationships": {
+        "title": "PersonRelationships",
+        "type": "object",
+        "properties": {
+          "people": {
+            "title": "People",
+            "type": "array",
+            "items": {
+              "$ref": "#/definitions/Person"
+            }
+          }
+        }
+      }
+    }
+  }
diff --git a/tests/test_main.py b/tests/test_main.py
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -30,6 +30,31 @@
 TIMESTAMP = '1985-10-26T01:21:00-07:00'
 
 
+@freeze_time('2019-07-26')
+def test_main_inheritance_forward_ref():
+    with TemporaryDirectory() as output_dir:
+        output_file: Path = Path(output_dir) / 'output.py'
+        shutil.copy(DATA_PATH / 'pyproject.toml', Path(output_dir) / 'pyproject.toml')
+        return_code: Exit = main(
+            [
+                '--input',
+                str(JSON_SCHEMA_DATA_PATH / 'inheritance_forward_ref.json'),
+                '--output',
+                str(output_file),
+            ]
+        )
+        assert return_code == Exit.OK
+        assert (
+            output_file.read_text()
+            == (
+                EXPECTED_MAIN_PATH / 'main_inheritance_forward_ref' / 'output.py'
+            ).read_text()
+        )
+
+    with pytest.raises(SystemExit):
+        main()
+
+
 @freeze_time('2019-07-26')
 def test_main():
     with TemporaryDirectory() as output_dir: