diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 3df876c..46423de 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,11 @@ +**v0.16.0** + +### Updates +1. fix character varying type - https://github.com/xnuinside/omymodels/issues/59 +2. sqlalchemy import removed from generation in sqlmodels if it is not used +3. = Field() - is not placed in SQLModel if there is no defaults or other settings to the field + + **v0.15.1** ## Updates 1. Foreign Key processing updates - https://github.com/xnuinside/omymodels/pull/55 diff --git a/README.md b/README.md index c68c9a4..53bd511 100644 --- a/README.md +++ b/README.md @@ -307,6 +307,14 @@ If you see any bugs or have any suggestions - feel free to open the issue. Any h One more time, big 'thank you!' goes to https://github.com/archongum for Web-version: https://archon-omymodels-online.hf.space/ ## Changelog +**v0.16.0** + +### Updates +1. fix character varying type - https://github.com/xnuinside/omymodels/issues/59 +2. sqlalchemy import removed from generation in sqlmodels if it is not used +3. = Field() - is not placed in SQLModel if there is no defaults or other settings to the field + + **v0.15.1** ## Updates 1. Foreign Key processing updates - https://github.com/xnuinside/omymodels/pull/55 diff --git a/docs/README.rst b/docs/README.rst index eb8d6e4..b26a1f1 100644 --- a/docs/README.rst +++ b/docs/README.rst @@ -330,6 +330,16 @@ One more time, big 'thank you!' goes to https://github.com/archongum for Web-ver Changelog --------- +**v0.16.0** + +Updates +^^^^^^^ + + +#. fix character varying type - https://github.com/xnuinside/omymodels/issues/59 +#. sqlalchemy import removed from generation in sqlmodels if it is not used +#. = Field() - is not placed in SQLModel if there is no defaults or other settings to the field + **v0.15.1** Updates diff --git a/omymodels/cli.py b/omymodels/cli.py index bc328a8..82fbcdd 100644 --- a/omymodels/cli.py +++ b/omymodels/cli.py @@ -13,7 +13,8 @@ def version(**kwargs): def cli(): omm_cli = argparse.ArgumentParser( - description="O! My Models. Create GinoORM models from SQL DDL" + description="O! My Models. " + "Create SQLModels, SQLAlchemy, GinoORM and other models from SQL DDL or another models" ) omm_cli.add_argument( diff --git a/omymodels/from_ddl.py b/omymodels/from_ddl.py index 3207b7c..8db44a5 100644 --- a/omymodels/from_ddl.py +++ b/omymodels/from_ddl.py @@ -153,7 +153,9 @@ def generate_models_file( schema_global=schema_global, defaults_off=defaults_off, ) - header += generator.create_header(data["tables"], schema=schema_global) + header += generator.create_header( + data["tables"], schema=schema_global, models_str=models_str + ) else: models_type = "enum" output = render_jinja2_template(models_type, models_str, header) diff --git a/omymodels/models/enum/core.py b/omymodels/models/enum/core.py index ed06805..a975f1c 100644 --- a/omymodels/models/enum/core.py +++ b/omymodels/models/enum/core.py @@ -74,7 +74,7 @@ def create_types(self) -> str: self.custom_types = {x.name: ("db.Enum", x.name) for x in self.types} return types_str - def create_header(self) -> str: + def create_header(self, *args, **kwargs) -> str: self.enum_imports = list(self.enum_imports) self.enum_imports.sort() return enum_import.format(enums=", ".join(self.enum_imports)) + "\n" diff --git a/omymodels/models/gino/core.py b/omymodels/models/gino/core.py index 7de81bd..281c873 100644 --- a/omymodels/models/gino/core.py +++ b/omymodels/models/gino/core.py @@ -59,7 +59,9 @@ def generate_model( # create sequence return model - def create_header(self, tables: List[Dict], schema: bool = False) -> str: + def create_header( + self, tables: List[Dict], schema: bool = False, *args, **kwargs + ) -> str: """header of the file - imports & gino init""" header = "" if "func" in self.state: diff --git a/omymodels/models/sqlalchemy/core.py b/omymodels/models/sqlalchemy/core.py index a23b2e0..1c01f15 100644 --- a/omymodels/models/sqlalchemy/core.py +++ b/omymodels/models/sqlalchemy/core.py @@ -64,7 +64,9 @@ def generate_model( model = logic.add_table_args(self, model, table, schema_global) return model - def create_header(self, tables: List[Dict], schema: bool = False) -> str: + def create_header( + self, tables: List[Dict], schema: bool = False, *args, **kwargs + ) -> str: """header of the file - imports & sqlalchemy init""" header = "" if "func" in self.state: diff --git a/omymodels/models/sqlalchemy_core/core.py b/omymodels/models/sqlalchemy_core/core.py index 2c97a37..3f2c365 100644 --- a/omymodels/models/sqlalchemy_core/core.py +++ b/omymodels/models/sqlalchemy_core/core.py @@ -234,7 +234,9 @@ def generate_model(self, data: Dict, *args, **kwargs) -> str: model += index return model - def create_header(self, tables: List[Dict], schema: bool = False) -> str: + def create_header( + self, tables: List[Dict], schema: bool = False, *args, **kwargs + ) -> str: """header of the file - imports & sqlalchemy init""" header = "" if "func" in self.state: diff --git a/omymodels/models/sqlmodel/core.py b/omymodels/models/sqlmodel/core.py index 70d63cd..f4710f7 100644 --- a/omymodels/models/sqlmodel/core.py +++ b/omymodels/models/sqlmodel/core.py @@ -119,7 +119,6 @@ def generate_model( **kwargs, ) -> str: """method to prepare one Model defention - name & tablename & columns""" - model = "" model = st.model_template.format( model_name=create_class_name(table.name, singular, exceptions), @@ -133,26 +132,31 @@ def generate_model( col_str = st.column_template.format( column_name=column.name.replace(" ", "_"), column_type=pydantic_type_str ) - - col_str = logic.setup_column_attributes( - column, table.primary_key, col_str, table, schema_global, st, self + attrs_col_str = logic.setup_column_attributes( + column, table.primary_key, "", table, schema_global, st, self ) if column_type["sa"]: sa_type = types.add_size_to_orm_column(column_type["sa"], column) - col_str += st.sa_type.format(satype=sa_type) - col_str += ")\n" - - col_str = col_str.replace("(, ", "(") - + attrs_col_str += st.sa_type.format(satype=sa_type) + if attrs_col_str: + attrs_col_str = attrs_col_str.replace(",", "", 1).strip() + col_str += st.field_template.format(attr_data=attrs_col_str) + col_str += "\n" model += col_str if table.indexes or table.alter or table.checks or not schema_global: model = self.add_table_args(model, table, schema_global) return model - def create_header(self, tables: List[Dict], schema: bool = False) -> str: + def create_header( + self, + tables: List[Dict], + models_str: str, + schema: bool = False, + ) -> str: """header of the file - imports & sqlalchemy init""" header = "" - header += st.sqlalchemy_import # Do we always need this import? + if "sa." in models_str: + header += st.sqlalchemy_import # Do we always need this import? if "func" in self.state: header += st.sql_alchemy_func_import + "\n" if self.postgresql_dialect_cols: diff --git a/omymodels/models/sqlmodel/sqlmodel.jinja2 b/omymodels/models/sqlmodel/sqlmodel.jinja2 index a0867d8..ef841ab 100644 --- a/omymodels/models/sqlmodel/sqlmodel.jinja2 +++ b/omymodels/models/sqlmodel/sqlmodel.jinja2 @@ -2,6 +2,4 @@ import datetime import decimal from typing import Optional from sqlmodel import Field, SQLModel - -{{ headers }} -{{ models }} \ No newline at end of file +{{ headers }}{{ models }} \ No newline at end of file diff --git a/omymodels/models/sqlmodel/templates.py b/omymodels/models/sqlmodel/templates.py index 3c68247..31bd645 100644 --- a/omymodels/models/sqlmodel/templates.py +++ b/omymodels/models/sqlmodel/templates.py @@ -17,7 +17,8 @@ class {model_name}(SQLModel, table=True):\n """ # columns defenition -column_template = """ {column_name}: {column_type} = Field(""" +column_template = """ {column_name}: {column_type}""" +field_template = """ = Field({attr_data})""" required = "" default = ", sa_column_kwargs={{'server_default': {default}}}" pk_template = ", default=None, primary_key=True" diff --git a/omymodels/types.py b/omymodels/types.py index 2e271c6..8eac93f 100644 --- a/omymodels/types.py +++ b/omymodels/types.py @@ -8,7 +8,7 @@ "str", "varchar", "character", - "character_vying", + "character varying", "varying", "char", "string", diff --git a/one.ddl b/one.ddl new file mode 100644 index 0000000..ded5bca --- /dev/null +++ b/one.ddl @@ -0,0 +1,3 @@ +CREATE TABLE `option` ( + FIELD1 VARCHAR(256), +) ; \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 41f559f..ed6403e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "omymodels" -version = "0.15.1" +version = "0.16.0" description = "O! My Models (omymodels) is a library to generate Python Models for SQLAlchemy (ORM & Core), GinoORM, Pydantic, Pydal tables & Python Dataclasses from SQL DDL. And convert one models to another." authors = ["Iuliia Volkova "] license = "MIT" @@ -32,14 +32,10 @@ table-meta = "^0.1.5" [tool.poetry.dev-dependencies] pytest = "^7.4" -m2r = "^0.3.1" [tool.poetry.scripts] omm = 'omymodels.cli:main' -[tool.poetry.group.dev.dependencies] -twine = "^4.0.2" - [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" diff --git a/tests/functional/generator/test_sqlmodel.py b/tests/functional/generator/test_sqlmodel.py index 42d0bd3..32b0e48 100644 --- a/tests/functional/generator/test_sqlmodel.py +++ b/tests/functional/generator/test_sqlmodel.py @@ -6,7 +6,6 @@ def test_with_enums(): import decimal from typing import Optional from sqlmodel import Field, SQLModel - from enum import Enum import sqlalchemy as sa from sqlalchemy.sql import func @@ -14,7 +13,6 @@ def test_with_enums(): from pydantic import Json, UUID4 - class MaterialType(str, Enum): article = 'article' @@ -26,13 +24,13 @@ class Material(SQLModel, table=True): __tablename__ = 'material' id: Optional[int] = Field(default=None, primary_key=True) - title: str = Field() + title: str description: Optional[str] = Field(sa_type=sa.Text()) - link: str = Field() + link: str type: Optional[MaterialType] = Field(sa_type=sa.Enum(MaterialType)) additional_properties: Optional[Json] = Field(sa_column_kwargs={'server_default': '{"key": "value"}'}, sa_type=JSON()) created_at: Optional[datetime.datetime] = Field(sa_column_kwargs={'server_default': func.now()}) - updated_at: Optional[datetime.datetime] = Field() + updated_at: Optional[datetime.datetime] """ # noqa: E501 ddl = """ CREATE TYPE "material_type" AS ENUM ( @@ -61,20 +59,17 @@ def test_foreign_keys(): from typing import Optional from sqlmodel import Field, SQLModel -import sqlalchemy as sa - - class Materials(SQLModel, table=True): __tablename__ = 'materials' id: Optional[int] = Field(default=None, primary_key=True) - title: str = Field() - description: Optional[str] = Field() - link: Optional[str] = Field() - created_at: Optional[datetime.datetime] = Field() - updated_at: Optional[datetime.datetime] = Field() + title: str + description: Optional[str] + link: Optional[str] + created_at: Optional[datetime.datetime] + updated_at: Optional[datetime.datetime] class MaterialAttachments(SQLModel, table=True): @@ -90,10 +85,10 @@ class Attachments(SQLModel, table=True): __tablename__ = 'attachments' id: Optional[int] = Field(default=None, primary_key=True) - title: Optional[str] = Field() - description: Optional[str] = Field() - created_at: Optional[datetime.datetime] = Field() - updated_at: Optional[datetime.datetime] = Field() + title: Optional[str] + description: Optional[str] + created_at: Optional[datetime.datetime] + updated_at: Optional[datetime.datetime] """ ddl = """ @@ -139,20 +134,17 @@ def test_foreign_keys_defined_inline(): from typing import Optional from sqlmodel import Field, SQLModel -import sqlalchemy as sa - - class Materials(SQLModel, table=True): __tablename__ = 'materials' id: Optional[int] = Field(default=None, primary_key=True) - title: str = Field() - description: Optional[str] = Field() - link: Optional[str] = Field() - created_at: Optional[datetime.datetime] = Field() - updated_at: Optional[datetime.datetime] = Field() + title: str + description: Optional[str] + link: Optional[str] + created_at: Optional[datetime.datetime] + updated_at: Optional[datetime.datetime] class MaterialAttachments(SQLModel, table=True): @@ -168,10 +160,10 @@ class Attachments(SQLModel, table=True): __tablename__ = 'attachments' id: Optional[int] = Field(default=None, primary_key=True) - title: Optional[str] = Field() - description: Optional[str] = Field() - created_at: Optional[datetime.datetime] = Field() - updated_at: Optional[datetime.datetime] = Field() + title: Optional[str] + description: Optional[str] + created_at: Optional[datetime.datetime] + updated_at: Optional[datetime.datetime] """ ddl = """ @@ -212,20 +204,17 @@ def test_multi_col_pk_and_fk(): import decimal from typing import Optional from sqlmodel import Field, SQLModel - -import sqlalchemy as sa from sqlalchemy.sql import func - class Complexpk(SQLModel, table=True): __tablename__ = 'complexpk' complex_id: Optional[int] = Field(default=None, primary_key=True) date_part: Optional[datetime.datetime] = Field(sa_column_kwargs={'server_default': func.now()}, default=None, primary_key=True) - title: str = Field() - description: Optional[str] = Field() + title: str + description: Optional[str] class LinkedTo(SQLModel, table=True): @@ -235,7 +224,7 @@ class LinkedTo(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) complexpk_complex_id: Optional[int] = Field(foreign_key='complexpk.complex_id') complexpk_date_part: Optional[int] = Field(foreign_key='complexpk.date_part') - comment: Optional[str] = Field() + comment: Optional[str] """ # noqa: E501 ddl = """ @@ -267,7 +256,6 @@ def test_upper_name_produces_the_same_result(): import decimal from typing import Optional from sqlmodel import Field, SQLModel - from enum import Enum import sqlalchemy as sa from sqlalchemy.sql import func @@ -275,7 +263,6 @@ def test_upper_name_produces_the_same_result(): from pydantic import Json, UUID4 - class MaterialType(str, Enum): article = 'article' @@ -287,13 +274,13 @@ class Material(SQLModel, table=True): __tablename__ = 'material' id: Optional[int] = Field(default=None, primary_key=True) - title: str = Field() + title: str description: Optional[str] = Field(sa_type=sa.Text()) - link: str = Field() + link: str type: Optional[MaterialType] = Field(sa_type=sa.Enum(MaterialType)) additional_properties: Optional[Json] = Field(sa_column_kwargs={'server_default': '{"key": "value"}'}, sa_type=JSON()) created_at: Optional[datetime.datetime] = Field(sa_column_kwargs={'server_default': func.now()}) - updated_at: Optional[datetime.datetime] = Field() + updated_at: Optional[datetime.datetime] """ # noqa: E501 ddl = """ CREATE TYPE "material_type" AS ENUM ( @@ -322,9 +309,6 @@ def test_foreign_keys_in_different_schema(): from typing import Optional from sqlmodel import Field, SQLModel -import sqlalchemy as sa - - class Table1(SQLModel, table=True): @@ -371,3 +355,27 @@ class Table2(SQLModel, table=True): """ result = create_models(ddl, schema_global=False, models_type="sqlmodel")["code"] assert result == expected + + +def test_sqlmodel_varying(): + ddl = """ + CREATE TABLE qwe ( + id integer NOT NULL, + name character varying(255), + ); + """ + result = create_models(ddl, models_type="sqlmodel")["code"] + expected = """import datetime +import decimal +from typing import Optional +from sqlmodel import Field, SQLModel + + +class Qwe(SQLModel, table=True): + + __tablename__ = 'qwe' + + id: int + name: Optional[str] +""" + assert expected == result