From 919ecc67610c1bfc8b2eb94658240355f0324b3a Mon Sep 17 00:00:00 2001 From: Peter DeVita Date: Fri, 2 Aug 2024 17:37:38 -0400 Subject: [PATCH] Fix primary key always being marked as nullable in schema (fixes #1227) --- ninja/orm/fields.py | 2 +- tests/test_orm_metaclass.py | 12 +++--- tests/test_orm_schemas.py | 76 ++++++++++++++++++++++--------------- 3 files changed, 53 insertions(+), 37 deletions(-) diff --git a/ninja/orm/fields.py b/ninja/orm/fields.py index 58ad478e..d89406cc 100644 --- a/ninja/orm/fields.py +++ b/ninja/orm/fields.py @@ -150,7 +150,7 @@ def get_schema_field( internal_type = field.get_internal_type() python_type = TYPES[internal_type] - if field.primary_key or blank or null or optional: + if blank or null or optional: default = None nullable = True diff --git a/tests/test_orm_metaclass.py b/tests/test_orm_metaclass.py index 58838a15..8cd6bafc 100644 --- a/tests/test_orm_metaclass.py +++ b/tests/test_orm_metaclass.py @@ -46,10 +46,10 @@ class Meta: "title": "SampleSchema2", "type": "object", "properties": { - "id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "ID"}, + "id": {"title": "ID", "type": "integer"}, "firstname": {"title": "Firstname", "type": "string"}, }, - "required": ["firstname"], + "required": ["id", "firstname"], } @@ -119,9 +119,9 @@ class Meta: fields = "__all__" fields_optional = "__all__" - assert OptSchema.json_schema().get("required") == ["extra"] + assert OptSchema.json_schema().get("required") == ["id", "extra"] assert OptSchema.json_schema()["properties"] == { - "id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "ID"}, + "id": {"title": "ID", "type": "integer"}, "title": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Title"}, "other": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Other"}, "extra": {"title": "Extra", "type": "integer"}, @@ -164,14 +164,14 @@ class Meta: "title": "SomeSchema", "type": "object", "properties": { - "id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "ID"}, + "id": {"title": "ID", "type": "integer"}, "field1": {"title": "Field1", "type": "string"}, "field2": { "anyOf": [{"type": "string"}, {"type": "null"}], "title": "Field2", }, }, - "required": ["field1"], + "required": ["id", "field1"], } diff --git a/tests/test_orm_schemas.py b/tests/test_orm_schemas.py index b1f11641..bd473dcc 100644 --- a/tests/test_orm_schemas.py +++ b/tests/test_orm_schemas.py @@ -32,12 +32,12 @@ class Meta: "title": "ChildModel", "type": "object", "properties": { - "id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "ID"}, + "id": {"title": "ID", "type": "integer"}, "parent_field": {"type": "string", "title": "Parent Field"}, "parentmodel_ptr_id": {"type": "integer", "title": "Parentmodel Ptr"}, "child_field": {"type": "string", "title": "Child Field"}, }, - "required": ["parent_field", "parentmodel_ptr_id", "child_field"], + "required": ["id", "parent_field", "parentmodel_ptr_id", "child_field"], } @@ -86,7 +86,7 @@ class Meta: "title": "AllFields", "type": "object", "properties": { - "id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "ID"}, + "id": {"title": "ID", "type": "integer"}, "bigintegerfield": {"title": "Bigintegerfield", "type": "integer"}, "binaryfield": { "title": "Binaryfield", @@ -156,6 +156,7 @@ class Meta: "hstorefield": {"type": "object", "title": "Hstorefield"}, }, "required": [ + "id", "bigintegerfield", "binaryfield", "booleanfield", @@ -208,10 +209,7 @@ class Meta: SchemaCls = create_schema(ModelAltAuto) # print(SchemaCls.json_schema()) assert SchemaCls.json_schema()["properties"] == { - "altautofield": { - "anyOf": [{"type": "integer"}, {"type": "null"}], - "title": "Altautofield", - } + "altautofield": {"title": "Altautofield", "type": "integer"} } @@ -229,14 +227,14 @@ class Meta: "title": "ModelNewFields", "type": "object", "properties": { - "id": {"title": "ID", "anyOf": [{"type": "integer"}, {"type": "null"}]}, + "id": {"title": "ID", "type": "integer"}, "jsonfield": {"title": "Jsonfield", "type": "object"}, "positivebigintegerfield": { "title": "Positivebigintegerfield", "type": "integer", }, }, - "required": ["jsonfield", "positivebigintegerfield"], + "required": ["id", "jsonfield", "positivebigintegerfield"], } obj = Schema(id=1, jsonfield={"any": "data"}, positivebigintegerfield=1) @@ -268,7 +266,7 @@ class Meta: "title": "TestSchema", "type": "object", "properties": { - "id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "ID"}, + "id": {"title": "ID", "type": "integer"}, "onetoonefield_id": {"title": "Onetoonefield", "type": "integer"}, "foreignkey_id": { "anyOf": [{"type": "integer"}, {"type": "null"}], @@ -280,7 +278,7 @@ class Meta: "items": {"type": "integer"}, }, }, - "required": ["onetoonefield_id", "manytomanyfield"], + "required": ["id", "onetoonefield_id", "manytomanyfield"], } SchemaClsDeep = create_schema(TestModel, name="TestSchemaDeep", depth=1) @@ -288,7 +286,7 @@ class Meta: assert SchemaClsDeep.json_schema() == { "type": "object", "properties": { - "id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "ID"}, + "id": {"title": "ID", "type": "integer"}, "onetoonefield": { "title": "Onetoonefield", "description": "", @@ -306,20 +304,17 @@ class Meta: "description": "", }, }, - "required": ["onetoonefield", "manytomanyfield"], + "required": ["id", "onetoonefield", "manytomanyfield"], "title": "TestSchemaDeep", "$defs": { "Related": { "title": "Related", "type": "object", "properties": { - "id": { - "anyOf": [{"type": "integer"}, {"type": "null"}], - "title": "ID", - }, + "id": {"title": "ID", "type": "integer"}, "charfield": {"type": "string", "title": "Charfield"}, }, - "required": ["charfield"], + "required": ["id", "charfield"], } }, } @@ -339,7 +334,7 @@ class Meta: "title": "MyModel", "type": "object", "properties": { - "id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "ID"}, + "id": {"title": "ID", "type": "integer"}, "default_static": { "default": "hello", "title": "Default Static", @@ -347,6 +342,7 @@ class Meta: }, "default_dynamic": {"title": "Default Dynamic", "type": "string"}, }, + "required": ["id"], } @@ -388,11 +384,11 @@ class Meta: assert Schema3.json_schema() == { "type": "object", "properties": { - "id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "ID"}, + "id": {"title": "ID", "type": "integer"}, "f1": {"type": "string", "title": "F1"}, "f2": {"type": "string", "title": "F2"}, }, - "required": ["f1", "f2"], + "required": ["id", "f1", "f2"], "title": "SampleModel3", } @@ -437,10 +433,10 @@ def test_with_relations(): "title": "Category", "type": "object", "properties": { - "id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "ID"}, + "id": {"title": "ID", "type": "integer"}, "title": {"title": "Title", "maxLength": 100, "type": "string"}, }, - "required": ["title"], + "required": ["id", "title"], } @@ -491,12 +487,12 @@ class Meta: assert Schema1.json_schema() == { "type": "object", "properties": { - "id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "ID"}, + "id": {"title": "ID", "type": "integer"}, "f1": {"type": "string", "title": "F1"}, "f2": {"type": "string", "title": "F2"}, "custom": {"type": "integer", "title": "Custom"}, }, - "required": ["f1", "f2", "custom"], + "required": ["id", "f1", "f2", "custom"], "title": "SmallModel", } @@ -506,11 +502,11 @@ class Meta: assert Schema2.json_schema() == { "type": "object", "properties": { - "id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "ID"}, + "id": {"title": "ID", "type": "integer"}, "f1": {"type": "integer", "title": "F1"}, "f2": {"type": "string", "title": "F2"}, }, - "required": ["f1", "f2"], + "required": ["id", "f1", "f2"], "title": "SmallModel2", } @@ -569,12 +565,32 @@ class Meta: app_label = "tests" Schema = create_schema(SomeReqFieldModel) - assert Schema.json_schema()["required"] == ["some_field", "other_field"] + assert Schema.json_schema()["required"] == ["id", "some_field", "other_field"] Schema = create_schema(SomeReqFieldModel, optional_fields=["some_field"]) - assert Schema.json_schema()["required"] == ["other_field"] + assert Schema.json_schema()["required"] == ["id", "other_field"] Schema = create_schema( SomeReqFieldModel, optional_fields=["some_field", "other_field", "optional"] ) - assert Schema.json_schema().get("required") is None + assert Schema.json_schema()["required"] == ["id"] + + +def test_optional_primary_key(): + class ModelWithOptionalPK(models.Model): + id = models.AutoField(primary_key=True, null=True) + name = models.CharField() + + class Meta: + app_label = "tests" + + Schema = create_schema(ModelWithOptionalPK) + assert Schema.json_schema() == { + "properties": { + "id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Id"}, + "name": {"title": "Name", "type": "string"}, + }, + "required": ["name"], + "title": "ModelWithOptionalPK", + "type": "object", + }