├── .gitignore ├── README.md ├── cheatsheet ├── __init__.py ├── alembic.ini ├── alembic │ ├── README │ ├── env.py │ ├── script.py.mako │ └── versions │ │ └── e3cf0c878e94_migration.py └── models │ ├── __init__.py │ └── basics.py ├── main.py ├── poetry.lock └── pyproject.toml /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SQLAlchemy cheat sheet 2 | 3 | ## Running examples 4 | 5 | To run the examples, change the `CONNECTION_STRING` in `cheatsheet/__init__.py`. Also change the connection string in `cheatsheet/alembic/alembic.ini` for running Alembic migrations. `mysql-connector` is included as dependency for running the examples against MySQL or MariaDb. 6 | 7 | This project uses [Poetry](https://python-poetry.org/) to manage dependencies, so install it and then run: 8 | 9 | ```bash 10 | poetry shell 11 | ``` 12 | 13 | Then apply Alembic migrations: 14 | ```bash 15 | cd cheatsheet && alembic upgrade head 16 | ``` 17 | 18 | ## Working with Alembic 19 | 20 | Note: to run the commands in this project, switch to `cheatsheet` directory: 21 | 22 | ```bash 23 | cd cheatsheet 24 | ``` 25 | 26 | When beginning a new project, Alembic needs to be initialized. It will create `alembic_version` database table with `alembic_num` column to store information about applied migrations: 27 | 28 | ```bash 29 | alembic init alembic 30 | ``` 31 | 32 | After that, a migration based on changes in the SQLAlchemy model can be generated ("migration" would become the name for the migration): 33 | 34 | ```bash 35 | alembic revision --autogenerate -m "migration" 36 | ``` 37 | 38 | The new migration file will be stored in `alembic/versions` as a Python file and can be further modified before it is applied. It is a good idea to review the changes, since Alembic might not pick up all changes perfectly. 39 | 40 | Generated migrations needs to be applied to the database. Alembic will only apply migrations that haven't been applied before: 41 | 42 | ```bash 43 | # head refers to the latest migration, 44 | # but we can provide a different "target" migration here 45 | alembic upgrade head 46 | ``` 47 | 48 | ## Alternatives to using Alembic 49 | 50 | ### nomad 51 | 52 | In case Alembic doesn't work well for us, we can use a simple migration tool [nomad](https://pypi.org/project/nomad/) that works with plain Python scripts and SQL files. 53 | 54 | One possible workflow could be to use Alembic to only generate initial migration SQL based on the diff between our models and a database, but store and run migrations using nomad. 55 | 56 | ## Creating Session object 57 | 58 | `Session` is basic object that is tied to our SQL connection and will allow us to run various ORM commands as a *Unit of Work*. 59 | 60 | Session is created once in `cheatsheet/__init__.py` like this: 61 | 62 | ```python 63 | from sqlalchemy.orm import sessionmaker 64 | from sqlalchemy import create_engine 65 | Session = sessionmaker() 66 | engine = create_engine(CONNECTION_STRING) 67 | Session.configure(bind=engine) 68 | ``` 69 | 70 | ## Defining SQLAlchemy models 71 | 72 | There are two basic ways to define our models in SQLAlchemy: 73 | - subclassing from `Base` class 74 | - using classical mapping through `MetaData` object 75 | 76 | ## Basic attributes 77 | 78 | ## Relationships 79 | 80 | Relationships are defined using `relationship` function. 81 | 82 | When we define relationships, we need to choose a loading strategy that SQLAlchemy will use to load relevant objects into memory. There are two basic loading strategies: 83 | - lazy loading (default) 84 | - eager loading 85 | 86 | ## Column property 87 | 88 | Column properties can be used for automatically computed columns. 89 | 90 | For instance, if we have a `Poll` with multiple `Voter`s, we can automatically expose the number 91 | of voters in the `Poll` object: 92 | 93 | ```python 94 | import sqlalchemy as sa 95 | from sqlalchemy import select, func 96 | from sqlalchemy.orm import column_property 97 | from sqlalchemy.sql.functions import coalesce 98 | 99 | class Voter(Base): 100 | id = Column(sa.BigInteger, autoincrement=True, primary_key=True, index=True) 101 | poll_id = Column(sa.BigInteger, ForeignKey("polls.id"), nullable=False) 102 | 103 | class Poll(Base): 104 | id = Column(sa.BigInteger, autoincrement=True, primary_key=True, index=True) 105 | voters = sa.relationship("Voter", backref="poll") 106 | # coalesce will handle the situation where there aren't any 107 | # voters yet associated with the poll 108 | n_voters = column_property( 109 | select([coalesce(func.count(Voter.id), 0)]) 110 | .correlate_except(Voter) 111 | ) 112 | ``` 113 | 114 | ## Querying data 115 | 116 | ## Querying data with joins 117 | 118 | ## Querying data with aggregations 119 | 120 | ## Inserting new data 121 | 122 | ## Inserting data in bulk 123 | 124 | ## Updating data 125 | 126 | ## Updating data in bulk -------------------------------------------------------------------------------- /cheatsheet/__init__.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.orm import sessionmaker 2 | from sqlalchemy import create_engine 3 | 4 | 5 | HOSTNAME = 'localhost' 6 | DATABASE = 'cheatsheet' 7 | USER = 'root' 8 | PASSWORD = 'root' 9 | CONNECTION_STRING = f'mysql+mysqlconnector://{USER}:{PASSWORD}@{HOSTNAME}:3306/{DATABASE}' 10 | 11 | 12 | Session = sessionmaker() 13 | engine = create_engine(CONNECTION_STRING) 14 | Session.configure(bind=engine) -------------------------------------------------------------------------------- /cheatsheet/alembic.ini: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # path to migration scripts 5 | script_location = alembic 6 | 7 | # template used to generate migration files 8 | # file_template = %%(rev)s_%%(slug)s 9 | 10 | # timezone to use when rendering the date 11 | # within the migration file as well as the filename. 12 | # string value is passed to dateutil.tz.gettz() 13 | # leave blank for localtime 14 | # timezone = 15 | 16 | # max length of characters to apply to the 17 | # "slug" field 18 | # truncate_slug_length = 40 19 | 20 | # set to 'true' to run the environment during 21 | # the 'revision' command, regardless of autogenerate 22 | # revision_environment = false 23 | 24 | # set to 'true' to allow .pyc and .pyo files without 25 | # a source .py file to be detected as revisions in the 26 | # versions/ directory 27 | # sourceless = false 28 | 29 | # version location specification; this defaults 30 | # to alembic/versions. When using multiple version 31 | # directories, initial revisions must be specified with --version-path 32 | # version_locations = %(here)s/bar %(here)s/bat alembic/versions 33 | 34 | # the output encoding used when revision files 35 | # are written from script.py.mako 36 | # output_encoding = utf-8 37 | 38 | sqlalchemy.url = mysql+mysqlconnector://root:root@localhost:3306/cheatsheet 39 | 40 | 41 | [post_write_hooks] 42 | # post_write_hooks defines scripts or Python functions that are run 43 | # on newly generated revision scripts. See the documentation for further 44 | # detail and examples 45 | 46 | # format using "black" - use the console_scripts runner, against the "black" entrypoint 47 | # hooks=black 48 | # black.type=console_scripts 49 | # black.entrypoint=black 50 | # black.options=-l 79 51 | 52 | # Logging configuration 53 | [loggers] 54 | keys = root,sqlalchemy,alembic 55 | 56 | [handlers] 57 | keys = console 58 | 59 | [formatters] 60 | keys = generic 61 | 62 | [logger_root] 63 | level = WARN 64 | handlers = console 65 | qualname = 66 | 67 | [logger_sqlalchemy] 68 | level = WARN 69 | handlers = 70 | qualname = sqlalchemy.engine 71 | 72 | [logger_alembic] 73 | level = INFO 74 | handlers = 75 | qualname = alembic 76 | 77 | [handler_console] 78 | class = StreamHandler 79 | args = (sys.stderr,) 80 | level = NOTSET 81 | formatter = generic 82 | 83 | [formatter_generic] 84 | format = %(levelname)-5.5s [%(name)s] %(message)s 85 | datefmt = %H:%M:%S 86 | -------------------------------------------------------------------------------- /cheatsheet/alembic/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /cheatsheet/alembic/env.py: -------------------------------------------------------------------------------- 1 | from logging.config import fileConfig 2 | 3 | from sqlalchemy import engine_from_config 4 | from sqlalchemy import pool 5 | 6 | from alembic import context 7 | 8 | # this is the Alembic Config object, which provides 9 | # access to the values within the .ini file in use. 10 | config = context.config 11 | 12 | # Interpret the config file for Python logging. 13 | # This line sets up loggers basically. 14 | fileConfig(config.config_file_name) 15 | 16 | # add your model's MetaData object here 17 | # for 'autogenerate' support 18 | # from myapp import mymodel 19 | # target_metadata = mymodel.Base.metadata 20 | 21 | import sys 22 | sys.path = ['', '..'] + sys.path[1:] 23 | 24 | from cheatsheet.models.basics import Base 25 | target_metadata = Base.metadata 26 | 27 | # other values from the config, defined by the needs of env.py, 28 | # can be acquired: 29 | # my_important_option = config.get_main_option("my_important_option") 30 | # ... etc. 31 | 32 | 33 | def run_migrations_offline(): 34 | """Run migrations in 'offline' mode. 35 | 36 | This configures the context with just a URL 37 | and not an Engine, though an Engine is acceptable 38 | here as well. By skipping the Engine creation 39 | we don't even need a DBAPI to be available. 40 | 41 | Calls to context.execute() here emit the given string to the 42 | script output. 43 | 44 | """ 45 | url = config.get_main_option("sqlalchemy.url") 46 | context.configure( 47 | url=url, 48 | target_metadata=target_metadata, 49 | literal_binds=True, 50 | dialect_opts={"paramstyle": "named"}, 51 | ) 52 | 53 | with context.begin_transaction(): 54 | context.run_migrations() 55 | 56 | 57 | def run_migrations_online(): 58 | """Run migrations in 'online' mode. 59 | 60 | In this scenario we need to create an Engine 61 | and associate a connection with the context. 62 | 63 | """ 64 | connectable = engine_from_config( 65 | config.get_section(config.config_ini_section), 66 | prefix="sqlalchemy.", 67 | poolclass=pool.NullPool, 68 | ) 69 | 70 | with connectable.connect() as connection: 71 | context.configure( 72 | connection=connection, target_metadata=target_metadata 73 | ) 74 | 75 | with context.begin_transaction(): 76 | context.run_migrations() 77 | 78 | 79 | if context.is_offline_mode(): 80 | run_migrations_offline() 81 | else: 82 | run_migrations_online() 83 | -------------------------------------------------------------------------------- /cheatsheet/alembic/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | ${imports if imports else ""} 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = ${repr(up_revision)} 14 | down_revision = ${repr(down_revision)} 15 | branch_labels = ${repr(branch_labels)} 16 | depends_on = ${repr(depends_on)} 17 | 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /cheatsheet/alembic/versions/e3cf0c878e94_migration.py: -------------------------------------------------------------------------------- 1 | """create world 2 | 3 | Revision ID: e3cf0c878e94 4 | Revises: 5 | Create Date: 2020-02-02 17:36:20.239836 6 | 7 | """ 8 | from alembic import op 9 | import sqlalchemy as sa 10 | 11 | 12 | # revision identifiers, used by Alembic. 13 | revision = 'e3cf0c878e94' 14 | down_revision = None 15 | branch_labels = None 16 | depends_on = None 17 | 18 | 19 | def upgrade(): 20 | # ### commands auto generated by Alembic - please adjust! ### 21 | op.create_table('world', 22 | sa.Column('id', sa.BigInteger(), nullable=False), 23 | sa.PrimaryKeyConstraint('id') 24 | ) 25 | # ### end Alembic commands ### 26 | 27 | 28 | def downgrade(): 29 | # ### commands auto generated by Alembic - please adjust! ### 30 | op.drop_table('world') 31 | # ### end Alembic commands ### 32 | -------------------------------------------------------------------------------- /cheatsheet/models/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stribny/python-sqlalchemy/454bb73b08af8b9cfcdf757413ec77dd47527741/cheatsheet/models/__init__.py -------------------------------------------------------------------------------- /cheatsheet/models/basics.py: -------------------------------------------------------------------------------- 1 | import sqlalchemy as sa 2 | from sqlalchemy.ext.declarative import declarative_base 3 | from cheatsheet import Session 4 | 5 | 6 | # to declare SQLAlchemy models declaratively, inherit from Base 7 | Base = declarative_base() 8 | 9 | 10 | # Big integer support for SQLite: 11 | # from sqlalchemy.dialects import sqlite 12 | # from sqlalchemy.sql.sqltypes import BigInteger 13 | # BigInt = BigInteger().with_variant(sqlite.INTEGER(), "sqlite") 14 | 15 | 16 | class World(Base): 17 | """Defines various different data types""" 18 | # how the table is named in the database 19 | __tablename__ = 'world' 20 | 21 | # primary key is nullable=False and indexed by default 22 | id = sa.Column(sa.BigInteger, primary_key=True) -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import typer 2 | from cheatsheet.models import basics 3 | 4 | 5 | app = typer.Typer() 6 | 7 | 8 | @app.command() 9 | def init(): 10 | ... 11 | 12 | 13 | @app.command() 14 | def gen_migrations(): 15 | # https://alembic.sqlalchemy.org/en/latest/api/commands.html 16 | ... 17 | 18 | 19 | if __name__ == "__main__": 20 | app() -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | category = "main" 3 | description = "A database migration tool for SQLAlchemy." 4 | name = "alembic" 5 | optional = false 6 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 7 | version = "1.3.3" 8 | 9 | [package.dependencies] 10 | Mako = "*" 11 | SQLAlchemy = ">=1.1.0" 12 | python-dateutil = "*" 13 | python-editor = ">=0.3" 14 | 15 | [[package]] 16 | category = "main" 17 | description = "Composable command line interface toolkit" 18 | name = "click" 19 | optional = false 20 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 21 | version = "7.0" 22 | 23 | [[package]] 24 | category = "main" 25 | description = "Cross-platform colored terminal text." 26 | name = "colorama" 27 | optional = false 28 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 29 | version = "0.4.3" 30 | 31 | [[package]] 32 | category = "main" 33 | description = "A super-fast templating language that borrows the best ideas from the existing templating languages." 34 | name = "mako" 35 | optional = false 36 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 37 | version = "1.1.1" 38 | 39 | [package.dependencies] 40 | MarkupSafe = ">=0.9.2" 41 | 42 | [package.extras] 43 | babel = ["babel"] 44 | lingua = ["lingua"] 45 | 46 | [[package]] 47 | category = "main" 48 | description = "Safely add untrusted strings to HTML/XML markup." 49 | name = "markupsafe" 50 | optional = false 51 | python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" 52 | version = "1.1.1" 53 | 54 | [[package]] 55 | category = "main" 56 | description = "MySQL driver written in Python" 57 | name = "mysql-connector" 58 | optional = false 59 | python-versions = "*" 60 | version = "2.2.9" 61 | 62 | [[package]] 63 | category = "main" 64 | description = "A drop-in replacement for pprint that's actually pretty" 65 | name = "pprintpp" 66 | optional = false 67 | python-versions = "*" 68 | version = "0.4.0" 69 | 70 | [[package]] 71 | category = "main" 72 | description = "Extensions to the standard Python datetime module" 73 | name = "python-dateutil" 74 | optional = false 75 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 76 | version = "2.8.1" 77 | 78 | [package.dependencies] 79 | six = ">=1.5" 80 | 81 | [[package]] 82 | category = "main" 83 | description = "Programmatically open an editor, capture the result." 84 | name = "python-editor" 85 | optional = false 86 | python-versions = "*" 87 | version = "1.0.4" 88 | 89 | [[package]] 90 | category = "main" 91 | description = "Render rich text, tables, syntax highlighting, markdown and more to the terminal" 92 | name = "rich" 93 | optional = false 94 | python-versions = ">=3.6,<4.0" 95 | version = "0.3.2" 96 | 97 | [package.dependencies] 98 | colorama = ">=0.4.3,<0.5.0" 99 | pprintpp = ">=0.4.0,<0.5.0" 100 | typing-extensions = ">=3.7.4,<4.0.0" 101 | 102 | [[package]] 103 | category = "main" 104 | description = "Python 2 and 3 compatibility utilities" 105 | name = "six" 106 | optional = false 107 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 108 | version = "1.14.0" 109 | 110 | [[package]] 111 | category = "main" 112 | description = "Database Abstraction Library" 113 | name = "sqlalchemy" 114 | optional = false 115 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 116 | version = "1.3.13" 117 | 118 | [package.extras] 119 | mssql = ["pyodbc"] 120 | mssql_pymssql = ["pymssql"] 121 | mssql_pyodbc = ["pyodbc"] 122 | mysql = ["mysqlclient"] 123 | oracle = ["cx-oracle"] 124 | postgresql = ["psycopg2"] 125 | postgresql_pg8000 = ["pg8000"] 126 | postgresql_psycopg2binary = ["psycopg2-binary"] 127 | postgresql_psycopg2cffi = ["psycopg2cffi"] 128 | pymysql = ["pymysql"] 129 | 130 | [[package]] 131 | category = "main" 132 | description = "Typer, build great CLIs. Easy to code. Based on Python type hints." 133 | name = "typer" 134 | optional = false 135 | python-versions = ">=3.6" 136 | version = "0.0.8" 137 | 138 | [package.dependencies] 139 | click = ">=7.0.0,<8.0.0" 140 | 141 | [package.extras] 142 | all = ["colorama", "click-completion"] 143 | doc = ["mkdocs", "mkdocs-material", "markdown-include"] 144 | test = ["click-completion", "pytest (>=4.4.0)", "pytest-cov", "coverage", "pytest-xdist", "pytest-sugar", "mypy", "black", "isort"] 145 | 146 | [[package]] 147 | category = "main" 148 | description = "Backported and Experimental Type Hints for Python 3.5+" 149 | name = "typing-extensions" 150 | optional = false 151 | python-versions = "*" 152 | version = "3.7.4.1" 153 | 154 | [metadata] 155 | content-hash = "a2c099a0c3877e155a230548bf0d5883a86ef3903b8329ed4577eb2fa6cc3a58" 156 | python-versions = "^3.7" 157 | 158 | [metadata.files] 159 | alembic = [ 160 | {file = "alembic-1.3.3.tar.gz", hash = "sha256:d412982920653db6e5a44bfd13b1d0db5685cbaaccaf226195749c706e1e862a"}, 161 | ] 162 | click = [ 163 | {file = "Click-7.0-py2.py3-none-any.whl", hash = "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13"}, 164 | {file = "Click-7.0.tar.gz", hash = "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"}, 165 | ] 166 | colorama = [ 167 | {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, 168 | {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, 169 | ] 170 | mako = [ 171 | {file = "Mako-1.1.1.tar.gz", hash = "sha256:2984a6733e1d472796ceef37ad48c26f4a984bb18119bb2dbc37a44d8f6e75a4"}, 172 | ] 173 | markupsafe = [ 174 | {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, 175 | {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, 176 | {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"}, 177 | {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"}, 178 | {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"}, 179 | {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"}, 180 | {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"}, 181 | {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"}, 182 | {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"}, 183 | {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"}, 184 | {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"}, 185 | {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"}, 186 | {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, 187 | {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, 188 | {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, 189 | {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, 190 | {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, 191 | {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, 192 | {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, 193 | {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, 194 | {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, 195 | {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, 196 | {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, 197 | {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, 198 | {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, 199 | {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, 200 | {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, 201 | {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, 202 | ] 203 | mysql-connector = [ 204 | {file = "mysql-connector-2.2.9.tar.gz", hash = "sha256:1733e6ce52a049243de3264f1fbc22a852cb35458c4ad739ba88189285efdf32"}, 205 | ] 206 | pprintpp = [ 207 | {file = "pprintpp-0.4.0-py2.py3-none-any.whl", hash = "sha256:b6b4dcdd0c0c0d75e4d7b2f21a9e933e5b2ce62b26e1a54537f9651ae5a5c01d"}, 208 | {file = "pprintpp-0.4.0.tar.gz", hash = "sha256:ea826108e2c7f49dc6d66c752973c3fc9749142a798d6b254e1e301cfdbc6403"}, 209 | ] 210 | python-dateutil = [ 211 | {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, 212 | {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, 213 | ] 214 | python-editor = [ 215 | {file = "python-editor-1.0.4.tar.gz", hash = "sha256:51fda6bcc5ddbbb7063b2af7509e43bd84bfc32a4ff71349ec7847713882327b"}, 216 | {file = "python_editor-1.0.4-py2-none-any.whl", hash = "sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8"}, 217 | {file = "python_editor-1.0.4-py2.7.egg", hash = "sha256:ea87e17f6ec459e780e4221f295411462e0d0810858e055fc514684350a2f522"}, 218 | {file = "python_editor-1.0.4-py3-none-any.whl", hash = "sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d"}, 219 | {file = "python_editor-1.0.4-py3.5.egg", hash = "sha256:c3da2053dbab6b29c94e43c486ff67206eafbe7eb52dbec7390b5e2fb05aac77"}, 220 | ] 221 | rich = [ 222 | {file = "rich-0.3.2-py3-none-any.whl", hash = "sha256:282c2e4873bbd3c1400cddf1b819bf1f67371a7628b2ff7398c5438bdd28b7fb"}, 223 | {file = "rich-0.3.2.tar.gz", hash = "sha256:d5f4262b9d00854b9ada5b944c9357c1251c5b8c441f839cb90362d3e3ff2093"}, 224 | ] 225 | six = [ 226 | {file = "six-1.14.0-py2.py3-none-any.whl", hash = "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"}, 227 | {file = "six-1.14.0.tar.gz", hash = "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a"}, 228 | ] 229 | sqlalchemy = [ 230 | {file = "SQLAlchemy-1.3.13.tar.gz", hash = "sha256:64a7b71846db6423807e96820993fa12a03b89127d278290ca25c0b11ed7b4fb"}, 231 | ] 232 | typer = [ 233 | {file = "typer-0.0.8-py3-none-any.whl", hash = "sha256:ada3c007a2d21cd0006a459670fbc76dde8a3bea10298167f41723f356115a6b"}, 234 | {file = "typer-0.0.8.tar.gz", hash = "sha256:50cbb5659cd359eeae807d970fcb898589529d440c70af126c6ea8425443387d"}, 235 | ] 236 | typing-extensions = [ 237 | {file = "typing_extensions-3.7.4.1-py2-none-any.whl", hash = "sha256:910f4656f54de5993ad9304959ce9bb903f90aadc7c67a0bef07e678014e892d"}, 238 | {file = "typing_extensions-3.7.4.1-py3-none-any.whl", hash = "sha256:cf8b63fedea4d89bab840ecbb93e75578af28f76f66c35889bd7065f5af88575"}, 239 | {file = "typing_extensions-3.7.4.1.tar.gz", hash = "sha256:091ecc894d5e908ac75209f10d5b4f118fbdb2eb1ede6a63544054bb1edb41f2"}, 240 | ] 241 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "python-sqlalchemy" 3 | version = "0.0.1" 4 | description = "" 5 | authors = ["Petr Stribny