Interactive Demo | Documentation | PyPI
Try the online O!MyModels converter or simply use it online: https://archon-omymodels-online.hf.space/ (A big thanks for that goes to https://github.com/archongum)
You can find usage examples in the example/ folder on GitHub: https://github.com/xnuinside/omymodels/tree/main/example
O! My Models (omymodels) is a library that allow you to generate different ORM & pure Python models from SQL DDL or convert one models type to another (exclude SQLAlchemy Table, it does not supported yet by py-models-parser).
Supported Models:
- SQLAlchemy 2.0 ORM (https://docs.sqlalchemy.org/en/20/orm/) - modern syntax with
Mappedandmapped_column - SQLAlchemy ORM (legacy style)
- SQLAlchemy Core (Tables) (https://docs.sqlalchemy.org/en/20/core/metadata.html)
- SQLModel (https://sqlmodel.tiangolo.com/) - combines SQLAlchemy and Pydantic
- GinoORM (https://python-gino.org/)
- Pydantic v1/v2 (https://docs.pydantic.dev/)
- Python Dataclasses (https://docs.python.org/3/library/dataclasses.html)
- Python Enum (https://docs.python.org/3/library/enum.html) - generated from DDL SQL Types
- OpenAPI 3 (Swagger) schemas (https://swagger.io/specification/)
pip install omymodels
By default method create_models generates GinoORM models. Use the argument models_type to specify output format:
'pydantic'- Pydantic v1 models (usesOptional[X])'pydantic_v2'- Pydantic v2 models (usesX | Nonesyntax,dict | listfor JSON)'sqlalchemy'- SQLAlchemy ORM models'sqlalchemy_core'- SQLAlchemy Core Tables'dataclass'- Python Dataclasses'sqlmodel'- SQLModel models'openapi3'- OpenAPI 3 (Swagger) schema definitions
A lot of examples in tests/ - https://github.com/xnuinside/omymodels/tree/main/tests.
fromomymodelsimportcreate_modelsddl="""CREATE table user_history ( runid decimal(21) null ,job_id decimal(21) null ,id varchar(100) not null ,user varchar(100) not null ,status varchar(10) not null ,event_time timestamp not null default now() ,comment varchar(1000) not null default 'none' ) ;"""result=create_models(ddl, models_type='pydantic')['code']
# output:importdatetimefromtypingimportOptionalfrompydanticimportBaseModelclassUserHistory(BaseModel):
runid: Optional[int]
job_id: Optional[int]
id: struser: strstatus: strevent_time: datetime.datetimecomment: strfromomymodelsimportcreate_modelsddl="""CREATE table user_history ( runid decimal(21) null ,job_id decimal(21) null ,id varchar(100) not null ,user varchar(100) not null ,status varchar(10) not null ,event_time timestamp not null default now() ,comment varchar(1000) not null default 'none' ) ;"""result=create_models(ddl, models_type='pydantic_v2')['code']
# output:from __future__ importannotationsimportdatetimefrompydanticimportBaseModelclassUserHistory(BaseModel):
runid: float|None=Nonejob_id: float|None=Noneid: struser: strstatus: strevent_time: datetime.datetime=datetime.datetime.now()
comment: str='none'Key differences in Pydantic v2 output:
- Uses
X | Noneinstead ofOptional[X] - Uses
dict | listfor JSON/JSONB types instead ofJson - Includes
from __future__ import annotationsfor Python 3.9 compatibility - Nullable fields automatically get
= Nonedefault
To generate Dataclasses from DDL use argument models_type='dataclass'
for example:
# (same DDL as in Pydantic sample)result=create_models(ddl, schema_global=False, models_type='dataclass')['code']
# and result will be: importdatetimefromdataclassesimportdataclass@dataclassclassUserHistory:
id: struser: strstatus: strrunid: int=Nonejob_id: int=Noneevent_time: datetime.datetime=datetime.datetime.now()
comment: str='none'GinoORM example. If you provide an input like:
CREATETABLE "users" (
"id"SERIALPRIMARY KEY,
"name"varchar,
"created_at"timestamp,
"updated_at"timestamp,
"country_code"int,
"default_language"int
);
CREATETABLE "languages" (
"id"intPRIMARY KEY,
"code"varchar(2) NOT NULL,
"name"varcharNOT NULL
);
and you will get output:
fromginoimportGinodb=Gino()
classUsers(db.Model):
__tablename__='users'id=db.Column(db.Integer(), autoincrement=True, primary_key=True)
name=db.Column(db.String())
created_at=db.Column(db.TIMESTAMP())
updated_at=db.Column(db.TIMESTAMP())
country_code=db.Column(db.Integer())
default_language=db.Column(db.Integer())
classLanguages(db.Model):
__tablename__='languages'id=db.Column(db.Integer(), primary_key=True)
code=db.Column(db.String(2))
name=db.Column(db.String())
omm path/to/your.ddl
# for example
omm tests/test_two_tables.sql
You can define target path where to save models with -t, --target flag:
# for example
omm tests/test_two_tables.sql -t test_path/test_models.py
If you want generate the Pydantic or Dataclasses models - just use flag -m or --models_type='pydantic' / --models_type='dataclass'
omm /path/to/your.ddl -m dataclass
# or
omm /path/to/your.ddl --models_type pydantic
Small library is used for parse DDL- https://github.com/xnuinside/simple-ddl-parser.
First of all, to parse types correct from DDL to models - they must be in types mypping, for Gino it exitst in this file:
omymodels/gino/types.py types_mapping
If you need to use fast type that not exist in mapping - just do a path before call code with types_mapping.update()
for example:
fromomymodels.models.ginoimporttypesfromomymodelsimportcreate_modelstypes.types_mapping.update({'your_type_from_ddl': 'db.TypeInGino'})
ddl="YOUR DDL with your custom your_type_from_ddl"models=create_models(ddl)
#### And similar for Pydantic typesfromomymodels.models.pydanticimporttypestypes_mappingfromomymodelsimportcreate_modelstypes.types_mapping.update({'your_type_from_ddl': 'db.TypeInGino'})
ddl="YOUR DDL with your custom your_type_from_ddl"models=create_models(ddl, models_type='pydantic')There is 2 ways how to define schema in Models:
- Globally in Gino() class and it will be like this:
fromginoimportGinodb=Gino(schema="schema_name")And this is a default way for put schema during generation - it takes first schema in tables and use it.
- But if you work with tables in different schemas, you need to define schema in each model in table_args. O!MyModels can do this also. Just use flag
--no-global-schemaif you use cli or put argument 'schema_global=False' to create_models() function if you use library from code. Like this:
ddl=""" CREATE TABLE "prefix--schema-name"."table" ( _id uuid PRIMARY KEY, one_more_id int ); create unique index table_pk on "prefix--schema-name"."table" (one_more_id) ; create index table_ix2 on "prefix--schema-name"."table" (_id) ; """result=create_models(ddl, schema_global=False)And result will be this:
fromsqlalchemy.dialects.postgresqlimportUUIDfromsqlalchemy.schemaimportUniqueConstraintfromsqlalchemyimportIndexfromginoimportGinodb=Gino()
classTable(db.Model):
__tablename__='table'_id=db.Column(UUID, primary_key=True)
one_more_id=db.Column(db.Integer())
__table_args__= (
UniqueConstraint(one_more_id, name='table_pk'),
Index('table_ix2', _id),
dict(schema="prefix--schema-name")
)O!MyModels supports bidirectional conversion with OpenAPI 3 schemas.
fromomymodelsimportcreate_modelsddl="""CREATE TABLE users ( id SERIAL PRIMARY KEY, username VARCHAR(100) NOT NULL, email VARCHAR(255), is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP);"""result=create_models(ddl, models_type="openapi3")
print(result["code"])
# Output:# {# "components": {# "schemas": {# "Users": {# "type": "object",# "properties": {# "id": {"type": "integer"},# "username": {"type": "string", "maxLength": 100},# "email": {"type": "string", "maxLength": 255},# "is_active": {"type": "boolean", "default": true},# "created_at": {"type": "string", "format": "date-time"}# },# "required": ["id", "username"]# }# }# }# }fromomymodelsimportcreate_models_from_openapi3schema="""{ "components": { "schemas": { "User": { "type": "object", "properties": { "id": {"type": "integer"}, "name": {"type": "string"}, "email": {"type": "string"}, "created_at": {"type": "string", "format": "date-time"} }, "required": ["id", "name"] } } }}"""# Convert to Pydantic v2result=create_models_from_openapi3(schema, models_type="pydantic_v2")
print(result)
# Output:# from __future__ import annotations## import datetime# from pydantic import BaseModel### class User(BaseModel):## id: int# name: str# email: str | None = None# created_at: datetime.datetime | None = NoneYAML schemas are also supported (requires pyyaml):
pip install pyyamlYou can add support for your own model types without forking the repository.
fromomymodelsimportBaseGenerator, TypeConverter, register_generator, create_models# Define type mappingMY_TYPES= {
"varchar": "String",
"integer": "Integer",
"boolean": "Boolean",
"timestamp": "DateTime",
}
classMyGenerator(BaseGenerator):
def__init__(self):
super().__init__()
self.type_converter=TypeConverter(MY_TYPES)
defgenerate_model(self, table, singular=True, **kwargs):
class_name=table.name.title().replace("_", "")
lines= [f"class {class_name}(MyBaseModel):"]
forcolumnintable.columns:
col_type=self.type_converter.convert(column.type)
lines.append(f" {column.name}: {col_type}")
return"\n".join(lines)
defcreate_header(self, tables, **kwargs):
return"from my_framework import MyBaseModel\n"# Register and useregister_generator("my_framework", MyGenerator)
result=create_models(ddl, models_type="my_framework")fromomymodelsimportregister_generatorfromomymodels.models.pydantic_v2.coreimportModelGeneratorasPydanticV2GeneratorclassCustomPydanticGenerator(PydanticV2Generator):
defcreate_header(self, *args, **kwargs):
header=super().create_header(*args, **kwargs)
return"from my_types import CustomType\n"+headerregister_generator("my_pydantic", CustomPydanticGenerator)See full examples in example/custom_generator.py and example/extend_builtin_generator.py.
- Add Sequence generation in Models (Gino, SQLAlchemy)
- Add support for Tortoise ORM (https://tortoise-orm.readthedocs.io/en/latest/)
- Add support for DjangoORM Models
- Add support for PyDAL Models (https://py4web.com/_documentation/static/en/chapter-07.html)
Please describe issue that you want to solve and open the PR, I will review it as soon as possible.
Any questions? Ping me in Telegram: https://t.me/xnuinside or mail xnuinside@gmail.com
If you see any bugs or have any suggestions - feel free to open the issue. Any help will be appritiated.
One more time, big 'thank you!' goes to https://github.com/archongum for Web-version: https://archon-omymodels-online.hf.space/
See CHANGELOG.md for full version history.
Breaking Changes:
- Dropped support for Python 3.7 and 3.8
- Minimum required Python version is now 3.9
New Features:
- Pydantic v2 support with native syntax (
X | None,dict | list) - OpenAPI 3 (Swagger) schema generation and conversion
- Plugin system for custom generators
- SQLModel array type support
- MySQL blob types support
Improvements:
- Simplified datetime imports
- Better Pydantic field handling (aliases, reserved names, generated columns)
- Enum functional syntax generation
See CHANGELOG.md for complete details and previous versions.