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 ORM (https://docs.sqlalchemy.org/en/20/orm/)
- 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.
from omymodels import create_models
ddl = """
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:
import datetime
from typing import Optional
from pydantic import BaseModel
class UserHistory(BaseModel):
runid: Optional[int]
job_id: Optional[int]
id: str
user: str
status: str
event_time: datetime.datetime
comment: strfrom omymodels import create_models
ddl = """
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__ import annotations
import datetime
from pydantic import BaseModel
class UserHistory(BaseModel):
runid: float | None = None
job_id: float | None = None
id: str
user: str
status: str
event_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:
import datetime
from dataclasses import dataclass
@dataclass
class UserHistory:
id: str
user: str
status: str
runid: int = None
job_id: int = None
event_time: datetime.datetime = datetime.datetime.now()
comment: str = 'none'GinoORM example. If you provide an input like:
CREATE TABLE "users" (
"id" SERIAL PRIMARY KEY,
"name" varchar,
"created_at" timestamp,
"updated_at" timestamp,
"country_code" int,
"default_language" int
);
CREATE TABLE "languages" (
"id" int PRIMARY KEY,
"code" varchar(2) NOT NULL,
"name" varchar NOT NULL
);
and you will get output:
from gino import Gino
db = Gino()
class Users(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())
class Languages(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:
from omymodels.models.gino import types
from omymodels import create_models
types.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 types
from omymodels.models.pydantic import types types_mapping
from omymodels import create_models
types.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:
from gino import Gino
db = 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:
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.schema import UniqueConstraint
from sqlalchemy import Index
from gino import Gino
db = Gino()
class Table(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.
from omymodels import create_models
ddl = """
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"]
# }
# }
# }
# }from omymodels import create_models_from_openapi3
schema = """
{
"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 v2
result = 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.
from omymodels import BaseGenerator, TypeConverter, register_generator, create_models
# Define type mapping
MY_TYPES = {
"varchar": "String",
"integer": "Integer",
"boolean": "Boolean",
"timestamp": "DateTime",
}
class MyGenerator(BaseGenerator):
def __init__(self):
super().__init__()
self.type_converter = TypeConverter(MY_TYPES)
def generate_model(self, table, singular=True, **kwargs):
class_name = table.name.title().replace("_", "")
lines = [f"class {class_name}(MyBaseModel):"]
for column in table.columns:
col_type = self.type_converter.convert(column.type)
lines.append(f" {column.name}: {col_type}")
return "\n".join(lines)
def create_header(self, tables, **kwargs):
return "from my_framework import MyBaseModel\n"
# Register and use
register_generator("my_framework", MyGenerator)
result = create_models(ddl, models_type="my_framework")from omymodels import register_generator
from omymodels.models.pydantic_v2.core import ModelGenerator as PydanticV2Generator
class CustomPydanticGenerator(PydanticV2Generator):
def create_header(self, *args, **kwargs):
header = super().create_header(*args, **kwargs)
return "from my_types import CustomType\n" + header
register_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 [email protected]
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/
v1.0.0
- Dropped support for Python 3.7 and 3.8
- Minimum required Python version is now 3.9
- Added support for Python 3.12 and 3.13
- Added
pydantic_v2models type with native Pydantic v2 syntax:- Uses
X | Noneinstead ofOptional[X] - Uses
dict | listfor JSON/JSONB types instead ofJson - Adds
from __future__ import annotationsfor Python 3.9 compatibility - Nullable fields automatically get
= Nonedefault
- Uses
- Added plugin system for custom generators - add your own model types without forking:
register_generator()- register custom generatorunregister_generator()- remove custom generatorlist_generators()- list all available generators- Base classes:
BaseGenerator,ORMGenerator,DataModelGenerator TypeConverterclass for type mappings- Entry points support for auto-discovery
- See examples:
example/custom_generator.py,example/extend_builtin_generator.py
- Added OpenAPI 3 (Swagger) schema support:
- Generate OpenAPI 3 schemas from DDL:
create_models(ddl, models_type="openapi3") - Convert OpenAPI 3 schemas to Python models:
create_models_from_openapi3(schema, models_type="pydantic_v2") - Supports JSON and YAML input (with pyyaml)
- Generate OpenAPI 3 schemas from DDL:
- Added tox configuration for local multi-version testing (py39-py313)
- Added pytest-cov for code coverage reporting
- Updated GitHub Actions workflow with latest action versions (checkout@v4, setup-python@v5)
- Added ARCHITECTURE.md with project documentation
- Updated documentation with Pydantic v2 examples
- Reorganized types module with TypeConverter class
- Updated py-models-parser to version 1.0.0
- Fixed
iterate_over_the_list()modifying list during iteration - Fixed meaningless condition in dataclass generator
v0.17.0
- fix character varying type - xnuinside#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.16.0
- Initial SQLModel Support
v0.15.1
- Foreign Key processing updates - xnuinside#55
- Move to simple-ddl-parser version 1.X
v0.14.0
- Python 3.11 support.
v0.13.0
- Added argument 'schema_global=' to support SQLAlchemy & Gino different table schemas xnuinside#41
v0.12.1
- current_timestamp function processed now same was as "now()" function from ddl
v0.12.0
- Now named arguments always went after positional. Fix for xnuinside#35
- Availability to disable auto-name convertion - xnuinside#36.
Now, if you want to keep names 1-to-1 as in your DDL file, you can set argument
no_auto_snake_case=Trueand O!MyModels will do nothing with the table or column names.
v0.11.1
- added bytes type to pydantic - xnuinside#31
- parser version updated to the latest
v0.11.0
- MSSQL column & tables names in [] now is parsed validly - xnuinside#28
- names like 'users_WorkSchedule' now converted correctly to PascalCase like UsersWorkSchedule
v0.10.1
- Update simple-ddl-parser version to 0.21.2
v0.10.0
- Meta models moved to separate package - https://github.com/xnuinside/table-meta
commonmodule renamed tofrom_ddl, but anyway please use public API as imports from main module:
from omymodels import create_models or from omymodels import convert_models
- Fixed bunch of bugs in converter, but it stil in 'beta'.
- Previously you can generate models if was any tables in ddl. Now you can also generate Enum models if in ddl you have only CREATE TYPE statements.
- String enums now in any models types will be inherit from (str, Enum)
- Added converter feature to convert one model type to another (excluding SQLAlchemy Core (Tables)). Now with more tests for supported models, but still in Beta with bucnh of issues.
v0.9.0 Features:
- Added beta models converter from one type of models to another. To use models convertor:
from omymodels import convert_models
models_from = """
class MaterialType(str, Enum):
article = "article"
video = "video"
@dataclass
class Material:
id: int
title: str
description: str
link: str
type: MaterialType
additional_properties: Union[dict, list]
created_at: datetime.datetime
updated_at: datetime.datetime
"""
result = convert_models(models_from, models_type="gino")
print(result)where models_type - type of models that you want to get as a result
- Now if O!MyModels does not know how to convert type - he just leave it as is.
Fixes:
- In Dataclass & Pydantic generators now Decimals & Floats converted to float (previously was int).
v0.8.4
- Now if tables was not found in input DDL - models generator raise NoTable error. if you want to have still silent exit if no tables, please use flag: exit_silent
v0.8.3
- Added fundamental concept of TableMetaModel - class that unifies metadata parsed from different classes/ORM models types/DDLs to one standard to allow easy way convert one models to another in next releases it will be used for converter from one type of models to another.
- Fixed issue: xnuinside#18 "NOW() not recognized as now()"
- Fixed issue: xnuinside#19 "Default value of now() always returns same time, use field for dataclass"
v0.8.1
- Parser version is updated (fixed several issues with generation)
- Fixed issue with Unique Constraint after schema in SQLAlchemy Core
v0.8.0
- Fix --defaults-off flag in cli
- Added support for SQLAlchemy Core Tables generating
- Added examples folder in github
omymodels/example - Fix issue with ForeignKey in SQLAlchemy
v0.7.0
- Added generation for SQLAlchemy models (defaults from DDLs are setting up as 'server_default')
- Added defaults for Pydantic models
- Added flag to generate Pydantic & Dataclass models WITHOUT defaults
defaults_off=True(by default it is False). And cli flag --defaults-off - Fixed issue with Enum types with lower case names in DDLs
- Fixed several issues with Dataclass generation (default with datetime & Enums)
- '"' do not remove from defaults now
v0.6.0
- O!MyModels now also can generate python Dataclass from DDL. Use argument models_type='dataclass' or if you use the cli flag --models_type dataclass or -m dataclass
- Added ForeignKey generation to GinoORM Models, added support for ondelete and onupdate
v0.5.0
- Added Enums/IntEnums types for Gino & Pydantic
- Added UUID type
- Added key
schema_globalin create_models method (by default schema_global = True). If you set schema_global=False schema if it exists in ddl will be defined for each table (model) in table args. This way you can have differen schemas per model (table). By default schema_global=True - this mean for all table only one schema and it is defined indb = Gino(schema="prefix--schema-name"). - If column is a primary key (primary_key=True) nullable argument not showed, because primary keys always are not null.
- To cli was added flag '--no-global-schema' to set schema in table_args.
v0.4.1
- Added correct work with table names contains multiple '-'
v0.4.0
- Added generation for Pydantic models from ddl
- Main method create_gino_models renamed to create_models
v0.3.0
- Generated Index for 'index' statement in table_args (not unique constrait as previously)
- Fix issue with column size as tuple (4,2)
v0.2.0
- Valid generating columns in models: autoincrement, default, type, arrays, unique, primary key and etc.
- Added creating table_args for indexes