From 2e2c0c2b98c514ac3a89e6a3cdd362f72a839f5f Mon Sep 17 00:00:00 2001 From: Kenneth Aasan Date: Mon, 4 Mar 2024 13:42:23 +0100 Subject: [PATCH] feat!: python pydantic follows v2 (#1851) --- docs/languages/Python.md | 6 +++- docs/migrations/version-3-to-4.md | 31 +++++++++++++++---- .../__snapshots__/index.spec.ts.snap | 4 +-- src/generators/python/presets/Pydantic.ts | 12 +++---- .../__snapshots__/Pydantic.spec.ts.snap | 2 +- 5 files changed, 39 insertions(+), 16 deletions(-) diff --git a/docs/languages/Python.md b/docs/languages/Python.md index 4fe3b21590..46870263d0 100644 --- a/docs/languages/Python.md +++ b/docs/languages/Python.md @@ -8,19 +8,23 @@ There are special use-cases that each language supports; this document pertains - [Generate Pydantic models](#generate-pydantic-models) - [Generate models with JSON Serializer and Deserializer methods](#generate-models-with-json-serializer-and-deserializer-methods) - * [Limitations](#limitations) + - [Limitations](#limitations) ## Generate Pydantic models + In some cases you might want to use [pydantic](https://pypi.org/project/pydantic/) data validation and settings management using Python type hints for the models. +Modelina follows Pydantic v2. You can find an example of its use [here](../../examples/generate-python-pydantic-models/index.ts) ## Generate models with JSON Serializer and Deserializer methods + Using the preset `PYTON_JSON_SERIALIZER`, you can generate `serializeToJson` method to convert model instance to JSON and `deserializeFromJson` method to convert JSON to model instance. ### Limitations + 1. Above preset doesn't unwrap properties of type `ConstrainedDictionaryModel` with `serialilzationType = unwrap` 2. The serialized JSON object will have the same property names as defined in the model object. diff --git a/docs/migrations/version-3-to-4.md b/docs/migrations/version-3-to-4.md index 3cb12456e8..31d7f0411d 100644 --- a/docs/migrations/version-3-to-4.md +++ b/docs/migrations/version-3-to-4.md @@ -1,9 +1,10 @@ # Migration from v3 to v4 + This document contain all the breaking changes and migrations guidelines for adapting your code to the new version. ## Fixed edge cases for camel case names -Naming such as object properties using camel case formatting had an edge case where if they contained a number followed by an underscore and a letter it would be incorrectly formatted. This has been fixed in this version, which might mean properties, model names, etc that use camel case might be renamed. +Naming such as object properties using camel case formatting had an edge case where if they contained a number followed by an underscore and a letter it would be incorrectly formatted. This has been fixed in this version, which might mean properties, model names, etc that use camel case might be renamed. This example contains such a string: @@ -34,7 +35,7 @@ interface AnonymousSchema_1 { ### Constant values are now properly rendered as const properties -This example used to generate a `string` with a getter and setter, but will now generate a const string that is initialized to the const value provided. +This example used to generate a `string` with a getter and setter, but will now generate a const string that is initialized to the const value provided. ```yaml type: object @@ -48,9 +49,9 @@ will generate ```csharp public class TestClass { - private const string property = "test"; - - public string Property + private const string property = "test"; + + public string Property { get { return property; } } @@ -62,7 +63,7 @@ Notice that `Property` no longer has a `set` method. This might break existing m ### DateTime and DateTimeOffset are now properly rendered based on specification format -In the previous version, `date-time` and `date` formats were rendered as `DateTime` and `DateTimeOffset` respectively. +In the previous version, `date-time` and `date` formats were rendered as `DateTime` and `DateTimeOffset` respectively. This has been changed to render `DateTimeOffset` for `date-time` and `DateTime` for `date` formats. This might break existing implementation and require manual changes. @@ -84,4 +85,22 @@ DateTime dateTime2 = modelinaModel.DateTime.LocalDateTime; Console.WriteLine(dateTime2); ``` +## Python + +### Pydantic now follows v2 instead of v1 + +Reference: https://docs.pydantic.dev/2.6/migration/ + +The schema description is now a description and not an alias: +```python +class Message(BaseModel): + identifier: str = Field(description='''The Identifier for the Message''') +``` + +In Modelina 3 this used is rendered as: + +```python +class Message(BaseModel): + identifier: str = Field(alias='''The Identifier for the Message''') +``` diff --git a/examples/generate-python-pydantic-models/__snapshots__/index.spec.ts.snap b/examples/generate-python-pydantic-models/__snapshots__/index.spec.ts.snap index 19d319f61c..1dc2f46024 100644 --- a/examples/generate-python-pydantic-models/__snapshots__/index.spec.ts.snap +++ b/examples/generate-python-pydantic-models/__snapshots__/index.spec.ts.snap @@ -3,8 +3,8 @@ exports[`Should be able to render python models and should log expected output to console: class-model 1`] = ` Array [ "class Root(BaseModel): - optionalField: Optional[str] = Field(alias='''this field is optional''', default=None) - requiredField: str = Field(alias='''this field is required''') + optionalField: Optional[str] = Field(description='''this field is optional''', default=None) + requiredField: str = Field(description='''this field is required''') noDescription: Optional[str] = Field(default=None) options: Optional[Options] = Field(default=None) ", diff --git a/src/generators/python/presets/Pydantic.ts b/src/generators/python/presets/Pydantic.ts index 39e2b7f052..0a8258f396 100644 --- a/src/generators/python/presets/Pydantic.ts +++ b/src/generators/python/presets/Pydantic.ts @@ -33,15 +33,15 @@ const PYTHON_PYDANTIC_CLASS_PRESET: ClassPresetType = { type = `Optional[${type}]`; } - const alias = params.property.property.originalInput['description'] - ? `alias='''${params.property.property.originalInput['description']}'''` + const description = params.property.property.originalInput['description'] + ? `description='''${params.property.property.originalInput['description']}'''` : ''; const defaultValue = params.property.required ? '' : 'default=None'; - if (alias && defaultValue) { - return `${propertyName}: ${type} = Field(${alias}, ${defaultValue})`; - } else if (alias) { - return `${propertyName}: ${type} = Field(${alias})`; + if (description && defaultValue) { + return `${propertyName}: ${type} = Field(${description}, ${defaultValue})`; + } else if (description) { + return `${propertyName}: ${type} = Field(${description})`; } return `${propertyName}: ${type} = Field(${defaultValue})`; }, diff --git a/test/generators/python/presets/__snapshots__/Pydantic.spec.ts.snap b/test/generators/python/presets/__snapshots__/Pydantic.spec.ts.snap index 5c77bd41f9..4cba95efa5 100644 --- a/test/generators/python/presets/__snapshots__/Pydantic.spec.ts.snap +++ b/test/generators/python/presets/__snapshots__/Pydantic.spec.ts.snap @@ -2,7 +2,7 @@ exports[`PYTHON_PYDANTIC_PRESET should render pydantic for class 1`] = ` "class Test(BaseModel): - prop: Optional[str] = Field(alias='''test + prop: Optional[str] = Field(description='''test multi line description''', default=None)