sqlacodegen-1.1.6/ 0000775 0001750 0001750 00000000000 12525442313 013713 5 ustar alex alex 0000000 0000000 sqlacodegen-1.1.6/sqlacodegen/ 0000775 0001750 0001750 00000000000 12525442313 016200 5 ustar alex alex 0000000 0000000 sqlacodegen-1.1.6/sqlacodegen/__init__.py 0000664 0001750 0001750 00000000040 12525442060 020302 0 ustar alex alex 0000000 0000000 version = __version__ = '1.1.6'
sqlacodegen-1.1.6/sqlacodegen/codegen.py 0000664 0001750 0001750 00000061416 12525441622 020170 0 ustar alex alex 0000000 0000000 """Contains the code generation logic and helper functions."""
from __future__ import unicode_literals, division, print_function, absolute_import
from collections import defaultdict
from inspect import ArgSpec
from keyword import iskeyword
import inspect
import sys
import re
from sqlalchemy import (Enum, ForeignKeyConstraint, PrimaryKeyConstraint, CheckConstraint, UniqueConstraint, Table,
Column)
from sqlalchemy.schema import ForeignKey
from sqlalchemy.util import OrderedDict
from sqlalchemy.types import Boolean, String
import sqlalchemy
try:
from sqlalchemy.sql.expression import text, TextClause
except ImportError:
# SQLAlchemy < 0.8
from sqlalchemy.sql.expression import text, _TextClause as TextClause
_re_boolean_check_constraint = re.compile(r"(?:(?:.*?)\.)?(.*?) IN \(0, 1\)")
_re_column_name = re.compile(r'(?:(["`]?)(?:.*)\1\.)?(["`]?)(.*)\2')
_re_enum_check_constraint = re.compile(r"(?:(?:.*?)\.)?(.*?) IN \((.+)\)")
_re_enum_item = re.compile(r"'(.*?)(? String)
for column in table.columns:
cls = column.type.__class__
for supercls in cls.__mro__:
if hasattr(supercls, '__visit_name__'):
cls = supercls
if supercls.__name__ != supercls.__name__.upper() and not supercls.__name__.startswith('_'):
break
column.type = column.type.adapt(cls)
def add_imports(self, collector):
if self.table.columns:
collector.add_import(Column)
for column in self.table.columns:
collector.add_import(column.type)
if column.server_default:
collector.add_literal_import('sqlalchemy', 'text')
for constraint in sorted(self.table.constraints, key=_get_constraint_sort_key):
if isinstance(constraint, ForeignKeyConstraint):
if len(constraint.columns) > 1:
collector.add_literal_import('sqlalchemy', 'ForeignKeyConstraint')
else:
collector.add_literal_import('sqlalchemy', 'ForeignKey')
elif isinstance(constraint, UniqueConstraint):
if len(constraint.columns) > 1:
collector.add_literal_import('sqlalchemy', 'UniqueConstraint')
elif not isinstance(constraint, PrimaryKeyConstraint):
collector.add_import(constraint)
for index in self.table.indexes:
if len(index.columns) > 1:
collector.add_import(index)
class ModelTable(Model):
def add_imports(self, collector):
super(ModelTable, self).add_imports(collector)
collector.add_import(Table)
def render(self):
text = 't_{0} = Table(\n {0!r}, metadata,\n'.format(self.table.name)
for column in self.table.columns:
text += ' {0},\n'.format(_render_column(column, True))
for constraint in sorted(self.table.constraints, key=_get_constraint_sort_key):
if isinstance(constraint, PrimaryKeyConstraint):
continue
if isinstance(constraint, (ForeignKeyConstraint, UniqueConstraint)) and len(constraint.columns) == 1:
continue
text += ' {0},\n'.format(_render_constraint(constraint))
for index in self.table.indexes:
if len(index.columns) > 1:
text += ' {0},\n'.format(_render_index(index))
if self.schema:
text += " schema='{0}',\n".format(self.schema)
return text.rstrip('\n,') + '\n)'
class ModelClass(Model):
parent_name = 'Base'
def __init__(self, table, association_tables, inflect_engine, detect_joined):
super(ModelClass, self).__init__(table)
self.name = self._tablename_to_classname(table.name, inflect_engine)
self.children = []
self.attributes = OrderedDict()
# Assign attribute names for columns
for column in table.columns:
self._add_attribute(column.name, column)
# Add many-to-one relationships
pk_column_names = set(col.name for col in table.primary_key.columns)
for constraint in sorted(table.constraints, key=_get_constraint_sort_key):
if isinstance(constraint, ForeignKeyConstraint):
target_cls = self._tablename_to_classname(constraint.elements[0].column.table.name, inflect_engine)
if (detect_joined and self.parent_name == 'Base' and
set(_get_column_names(constraint)) == pk_column_names):
self.parent_name = target_cls
else:
relationship_ = ManyToOneRelationship(self.name, target_cls, constraint, inflect_engine)
self._add_attribute(relationship_.preferred_name, relationship_)
# Add many-to-many relationships
for association_table in association_tables:
fk_constraints = [c for c in association_table.constraints if isinstance(c, ForeignKeyConstraint)]
fk_constraints.sort(key=_get_constraint_sort_key)
target_cls = self._tablename_to_classname(fk_constraints[1].elements[0].column.table.name, inflect_engine)
relationship_ = ManyToManyRelationship(self.name, target_cls, association_table)
self._add_attribute(relationship_.preferred_name, relationship_)
@staticmethod
def _tablename_to_classname(tablename, inflect_engine):
camel_case_name = ''.join(part[:1].upper() + part[1:] for part in tablename.split('_'))
return inflect_engine.singular_noun(camel_case_name) or camel_case_name
def _add_attribute(self, attrname, value):
attrname = tempname = _convert_to_valid_identifier(attrname)
counter = 1
while tempname in self.attributes:
tempname = attrname + str(counter)
counter += 1
self.attributes[tempname] = value
return tempname
def add_imports(self, collector):
super(ModelClass, self).add_imports(collector)
if any(isinstance(value, Relationship) for value in self.attributes.values()):
collector.add_literal_import('sqlalchemy.orm', 'relationship')
for child in self.children:
child.add_imports(collector)
def render(self):
text = 'class {0}({1}):\n'.format(self.name, self.parent_name)
text += ' __tablename__ = {0!r}\n'.format(self.table.name)
# Render constraints and indexes as __table_args__
table_args = []
for constraint in sorted(self.table.constraints, key=_get_constraint_sort_key):
if isinstance(constraint, PrimaryKeyConstraint):
continue
if isinstance(constraint, (ForeignKeyConstraint, UniqueConstraint)) and len(constraint.columns) == 1:
continue
table_args.append(_render_constraint(constraint))
for index in self.table.indexes:
if len(index.columns) > 1:
table_args.append(_render_index(index))
table_kwargs = {}
if self.schema:
table_kwargs['schema'] = self.schema
kwargs_items = ', '.join('{0!r}: {1!r}'.format(key, table_kwargs[key]) for key in table_kwargs)
kwargs_items = '{{{0}}}'.format(kwargs_items) if kwargs_items else None
if table_kwargs and not table_args:
text += ' __table_args__ = {0}\n'.format(kwargs_items)
elif table_args:
if kwargs_items:
table_args.append(kwargs_items)
if len(table_args) == 1:
table_args[0] += ','
text += ' __table_args__ = (\n {0}\n )\n'.format(',\n '.join(table_args))
# Render columns
text += '\n'
for attr, column in self.attributes.items():
if isinstance(column, Column):
show_name = attr != column.name
text += ' {0} = {1}\n'.format(attr, _render_column(column, show_name))
# Render relationships
if any(isinstance(value, Relationship) for value in self.attributes.values()):
text += '\n'
for attr, relationship in self.attributes.items():
if isinstance(relationship, Relationship):
text += ' {0} = {1}\n'.format(attr, relationship.render())
# Render subclasses
for child_class in self.children:
text += '\n\n' + child_class.render()
return text
class Relationship(object):
def __init__(self, source_cls, target_cls):
super(Relationship, self).__init__()
self.source_cls = source_cls
self.target_cls = target_cls
self.kwargs = OrderedDict()
def render(self):
text = 'relationship('
args = [repr(self.target_cls)]
if 'secondaryjoin' in self.kwargs:
text += '\n '
delimiter, end = ',\n ', '\n )'
else:
delimiter, end = ', ', ')'
args.extend([key + '=' + value for key, value in self.kwargs.items()])
return text + delimiter.join(args) + end
class ManyToOneRelationship(Relationship):
def __init__(self, source_cls, target_cls, constraint, inflect_engine):
super(ManyToOneRelationship, self).__init__(source_cls, target_cls)
column_names = _get_column_names(constraint)
colname = column_names[0]
tablename = constraint.elements[0].column.table.name
if not colname.endswith('_id'):
self.preferred_name = inflect_engine.singular_noun(tablename) or tablename
else:
self.preferred_name = colname[:-3]
# Add uselist=False to One-to-One relationships
if any(isinstance(c, (PrimaryKeyConstraint, UniqueConstraint)) and
set(col.name for col in c.columns) == set(column_names)
for c in constraint.table.constraints):
self.kwargs['uselist'] = 'False'
# Handle self referential relationships
if source_cls == target_cls:
self.preferred_name = 'parent' if not colname.endswith('_id') else colname[:-3]
pk_col_names = [col.name for col in constraint.table.primary_key]
self.kwargs['remote_side'] = '[{0}]'.format(', '.join(pk_col_names))
# If the two tables share more than one foreign key constraint,
# SQLAlchemy needs an explicit primaryjoin to figure out which column(s) to join with
common_fk_constraints = _get_common_fk_constraints(constraint.table, constraint.elements[0].column.table)
if len(common_fk_constraints) > 1:
self.kwargs['primaryjoin'] = "'{0}.{1} == {2}.{3}'".format(source_cls, column_names[0], target_cls,
constraint.elements[0].column.name)
class ManyToManyRelationship(Relationship):
def __init__(self, source_cls, target_cls, assocation_table):
super(ManyToManyRelationship, self).__init__(source_cls, target_cls)
self.kwargs['secondary'] = repr(assocation_table.name)
constraints = [c for c in assocation_table.constraints if isinstance(c, ForeignKeyConstraint)]
constraints.sort(key=_get_constraint_sort_key)
colname = _get_column_names(constraints[1])[0]
tablename = constraints[1].elements[0].column.table.name
self.preferred_name = tablename if not colname.endswith('_id') else colname[:-3] + 's'
# Handle self referential relationships
if source_cls == target_cls:
self.preferred_name = 'parents' if not colname.endswith('_id') else colname[:-3] + 's'
pri_pairs = zip(_get_column_names(constraints[0]), constraints[0].elements)
sec_pairs = zip(_get_column_names(constraints[1]), constraints[1].elements)
pri_joins = ['{0}.{1} == {2}.c.{3}'.format(source_cls, elem.column.name, assocation_table.name, col)
for col, elem in pri_pairs]
sec_joins = ['{0}.{1} == {2}.c.{3}'.format(target_cls, elem.column.name, assocation_table.name, col)
for col, elem in sec_pairs]
self.kwargs['primaryjoin'] = (
repr('and_({0})'.format(', '.join(pri_joins))) if len(pri_joins) > 1 else repr(pri_joins[0]))
self.kwargs['secondaryjoin'] = (
repr('and_({0})'.format(', '.join(sec_joins))) if len(sec_joins) > 1 else repr(sec_joins[0]))
class CodeGenerator(object):
header = '# coding: utf-8'
footer = ''
def __init__(self, metadata, noindexes=False, noconstraints=False, nojoined=False, noinflect=False,
noclasses=False):
super(CodeGenerator, self).__init__()
if noinflect:
inflect_engine = _DummyInflectEngine()
else:
import inflect
inflect_engine = inflect.engine()
# Pick association tables from the metadata into their own set, don't process them normally
links = defaultdict(lambda: [])
association_tables = set()
for table in metadata.tables.values():
# Link tables have exactly two foreign key constraints and all columns are involved in them
fk_constraints = [constr for constr in table.constraints if isinstance(constr, ForeignKeyConstraint)]
if len(fk_constraints) == 2 and all(col.foreign_keys for col in table.columns):
association_tables.add(table.name)
tablename = sorted(fk_constraints, key=_get_constraint_sort_key)[0].elements[0].column.table.name
links[tablename].append(table)
# Iterate through the tables and create model classes when possible
self.models = []
self.collector = ImportCollector()
classes = {}
for table in sorted(metadata.tables.values(), key=lambda t: (t.schema or '', t.name)):
# Support for Alembic and sqlalchemy-migrate -- never expose the schema version tables
if table.name in ('alembic_version', 'migrate_version'):
continue
if noindexes:
table.indexes.clear()
if noconstraints:
table.constraints = set([table.primary_key])
table.foreign_keys.clear()
for col in table.columns:
col.foreign_keys.clear()
else:
# Detect check constraints for boolean and enum columns
for constraint in table.constraints.copy():
if isinstance(constraint, CheckConstraint):
sqltext = _get_compiled_expression(constraint.sqltext)
# Turn any integer-like column with a CheckConstraint like "column IN (0, 1)" into a Boolean
match = _re_boolean_check_constraint.match(sqltext)
if match:
colname = _re_column_name.match(match.group(1)).group(3)
table.constraints.remove(constraint)
table.c[colname].type = Boolean()
continue
# Turn any string-type column with a CheckConstraint like "column IN (...)" into an Enum
match = _re_enum_check_constraint.match(sqltext)
if match:
colname = _re_column_name.match(match.group(1)).group(3)
items = match.group(2)
if isinstance(table.c[colname].type, String):
table.constraints.remove(constraint)
if not isinstance(table.c[colname].type, Enum):
options = _re_enum_item.findall(items)
table.c[colname].type = Enum(*options, native_enum=False)
continue
# Only form model classes for tables that have a primary key and are not association tables
if noclasses or not table.primary_key or table.name in association_tables:
model = ModelTable(table)
else:
model = ModelClass(table, links[table.name], inflect_engine, not nojoined)
classes[model.name] = model
self.models.append(model)
model.add_imports(self.collector)
# Nest inherited classes in their superclasses to ensure proper ordering
for model in classes.values():
if model.parent_name != 'Base':
classes[model.parent_name].children.append(model)
self.models.remove(model)
# Add either the MetaData or declarative_base import depending on whether there are mapped classes or not
if not any(isinstance(model, ModelClass) for model in self.models):
self.collector.add_literal_import('sqlalchemy', 'MetaData')
else:
self.collector.add_literal_import('sqlalchemy.ext.declarative', 'declarative_base')
def render(self, outfile=sys.stdout):
print(self.header, file=outfile)
# Render the collected imports
print(self.collector.render() + '\n\n', file=outfile)
if any(isinstance(model, ModelClass) for model in self.models):
print('Base = declarative_base()\nmetadata = Base.metadata', file=outfile)
else:
print('metadata = MetaData()', file=outfile)
# Render the model tables and classes
for model in self.models:
print('\n\n' + model.render().rstrip('\n'), file=outfile)
if self.footer:
print(self.footer, file=outfile)
sqlacodegen-1.1.6/sqlacodegen/main.py 0000664 0001750 0001750 00000004131 12501317520 017470 0 ustar alex alex 0000000 0000000 """ """
from __future__ import unicode_literals, division, print_function, absolute_import
import argparse
import codecs
import sys
from sqlalchemy.engine import create_engine
from sqlalchemy.schema import MetaData
from sqlacodegen.codegen import CodeGenerator
import sqlacodegen
def main():
parser = argparse.ArgumentParser(description='Generates SQLAlchemy model code from an existing database.')
parser.add_argument('url', nargs='?', help='SQLAlchemy url to the database')
parser.add_argument('--version', action='store_true', help="print the version number and exit")
parser.add_argument('--schema', help='load tables from an alternate schema')
parser.add_argument('--tables', help='tables to process (comma-separated, default: all)')
parser.add_argument('--noviews', action='store_true', help="ignore views")
parser.add_argument('--noindexes', action='store_true', help='ignore indexes')
parser.add_argument('--noconstraints', action='store_true', help='ignore constraints')
parser.add_argument('--nojoined', action='store_true', help="don't autodetect joined table inheritance")
parser.add_argument('--noinflect', action='store_true', help="don't try to convert tables names to singular form")
parser.add_argument('--noclasses', action='store_true', help="don't generate classes, only tables")
parser.add_argument('--outfile', help='file to write output to (default: stdout)')
args = parser.parse_args()
if args.version:
print(sqlacodegen.version)
return
if not args.url:
print('You must supply a url\n', file=sys.stderr)
parser.print_help()
return
engine = create_engine(args.url)
metadata = MetaData(engine)
tables = args.tables.split(',') if args.tables else None
metadata.reflect(engine, args.schema, not args.noviews, tables)
outfile = codecs.open(args.outfile, 'w', encoding='utf-8') if args.outfile else sys.stdout
generator = CodeGenerator(metadata, args.noindexes, args.noconstraints, args.nojoined, args.noinflect,
args.noclasses)
generator.render(outfile)
sqlacodegen-1.1.6/sqlacodegen.egg-info/ 0000775 0001750 0001750 00000000000 12525442313 017672 5 ustar alex alex 0000000 0000000 sqlacodegen-1.1.6/sqlacodegen.egg-info/PKG-INFO 0000664 0001750 0001750 00000012521 12525442313 020770 0 ustar alex alex 0000000 0000000 Metadata-Version: 1.1
Name: sqlacodegen
Version: 1.1.6
Summary: Automatic model code generator for SQLAlchemy
Home-page: http://pypi.python.org/pypi/sqlacodegen/
Author: Alex Gronholm
Author-email: sqlacodegen@nextday.fi
License: MIT
Description: This is a tool that reads the structure of an existing database and generates
the appropriate SQLAlchemy model code, using the declarative style if
possible.
This tool was written as a replacement for
`sqlautocode `_, which was suffering
from several issues (including, but not limited to, incompatibility with
Python 3 and the latest SQLAlchemy version).
Features
========
* Supports SQLAlchemy 0.6.x - 0.9.x
* Produces declarative code that almost looks like it was hand written
* Produces `PEP 8 `_ compliant code
* Accurately determines relationships, including many-to-many, one-to-one
* Automatically detects joined table inheritance
* Excellent test coverage
Usage instructions
==================
Installation
------------
To install, do::
pip install sqlacodegen
or, failing that::
easy_install sqlacodegen
Example usage
-------------
At the minimum, you have to give sqlacodegen a database URL.
The URL is passed directly to SQLAlchemy's
`create_engine() `_
method so please refer to
`SQLAlchemy's documentation `_
for instructions on how to construct a proper URL.
Examples::
sqlacodegen postgresql:///some_local_db
sqlacodegen mysql+oursql://user:password@localhost/dbname
sqlacodegen sqlite:///database.db
To see the full list of options::
sqlacodegen --help
Model class naming logic
------------------------
The table name (which is assumed to be in English) is converted to singular
form using the "inflect" library. Then, every underscore is removed while
transforming the next letter to upper case. For example, ``sales_invoices``
becomes ``SalesInvoice``.
Relationship detection logic
----------------------------
Relationships are detected based on existing foreign key constraints as
follows:
* **many-to-one**: a foreign key constraint exists on the table
* **one-to-one**: same as **many-to-one**, but a unique constraint exists on
the column(s) involved
* **many-to-many**: an association table is found to exist between two tables
A table is considered an association table if it satisfies all of the
following conditions:
#. has exactly two foreign key constraints
#. all its columns are involved in said constraints
Relationship naming logic
-------------------------
Relationships are typically named based on the opposite class name.
For example, if an ``Employee`` class has a column named ``employer`` which
has a foreign key to ``Company.id``, the relationship is named ``company``.
A special case for single column many-to-one and one-to-one relationships,
however, is if the column is named like ``employer_id``. Then the
relationship is named ``employer`` due to that ``_id`` suffix.
If more than one relationship would be created with the same name, the
latter ones are appended numeric suffixes, starting from 1.
Source code
===========
The source can be browsed at `Bitbucket
`_.
Reporting bugs
==============
A `bug tracker `_
is provided by bitbucket.org.
Getting help
============
If you have problems or other questions, you can either:
* Ask on the `SQLAlchemy Google group
`_, or
* Ask on the ``#sqlalchemy`` channel on
`Freenode IRC `_
Keywords: sqlalchemy
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Environment :: Console
Classifier: Topic :: Database
Classifier: Topic :: Software Development :: Code Generators
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
sqlacodegen-1.1.6/sqlacodegen.egg-info/SOURCES.txt 0000664 0001750 0001750 00000000635 12525442313 021562 0 ustar alex alex 0000000 0000000 MANIFEST.in
README.rst
setup.cfg
setup.py
tox.ini
sqlacodegen/__init__.py
sqlacodegen/codegen.py
sqlacodegen/main.py
sqlacodegen.egg-info/PKG-INFO
sqlacodegen.egg-info/SOURCES.txt
sqlacodegen.egg-info/dependency_links.txt
sqlacodegen.egg-info/entry_points.txt
sqlacodegen.egg-info/not-zip-safe
sqlacodegen.egg-info/pbr.json
sqlacodegen.egg-info/requires.txt
sqlacodegen.egg-info/top_level.txt
test/test_codegen.py sqlacodegen-1.1.6/sqlacodegen.egg-info/dependency_links.txt 0000664 0001750 0001750 00000000001 12525442313 023740 0 ustar alex alex 0000000 0000000
sqlacodegen-1.1.6/sqlacodegen.egg-info/entry_points.txt 0000664 0001750 0001750 00000000067 12525442313 023173 0 ustar alex alex 0000000 0000000 [console_scripts]
sqlacodegen = sqlacodegen.main:main
sqlacodegen-1.1.6/sqlacodegen.egg-info/not-zip-safe 0000664 0001750 0001750 00000000001 12361651041 022116 0 ustar alex alex 0000000 0000000
sqlacodegen-1.1.6/sqlacodegen.egg-info/pbr.json 0000664 0001750 0001750 00000000057 12525442044 021353 0 ustar alex alex 0000000 0000000 {"is_release": false, "git_version": "dbb6ac7"} sqlacodegen-1.1.6/sqlacodegen.egg-info/requires.txt 0000664 0001750 0001750 00000000045 12525442313 022271 0 ustar alex alex 0000000 0000000 SQLAlchemy >= 0.6.0
inflect >= 0.2.0
sqlacodegen-1.1.6/sqlacodegen.egg-info/top_level.txt 0000664 0001750 0001750 00000000014 12525442313 022417 0 ustar alex alex 0000000 0000000 sqlacodegen
sqlacodegen-1.1.6/test/ 0000775 0001750 0001750 00000000000 12525442313 014672 5 ustar alex alex 0000000 0000000 sqlacodegen-1.1.6/test/test_codegen.py 0000664 0001750 0001750 00000074511 12525441324 017720 0 ustar alex alex 0000000 0000000 from __future__ import unicode_literals, division, print_function, absolute_import
from io import StringIO
import sys
import re
from sqlalchemy.engine import create_engine
from sqlalchemy.schema import (MetaData, Table, Column, CheckConstraint, UniqueConstraint, Index, ForeignKey,
ForeignKeyConstraint)
from sqlalchemy.sql.expression import text
from sqlalchemy.types import INTEGER, SMALLINT, VARCHAR, NUMERIC
from sqlalchemy.dialects.postgresql.base import BIGINT, DOUBLE_PRECISION, BOOLEAN, ENUM
from sqlalchemy.dialects.mysql.base import TINYINT
from sqlalchemy.dialects.mysql import base as mysql
from sqlacodegen.codegen import CodeGenerator
if sys.version_info < (3,):
unicode_re = re.compile(r"u('|\")(.*?)(? 0'),
UniqueConstraint('id', 'number')
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import CheckConstraint, Column, Integer, MetaData, Table, UniqueConstraint
metadata = MetaData()
t_simple_items = Table(
'simple_items', metadata,
Column('id', Integer),
Column('number', Integer),
CheckConstraint('number > 0'),
UniqueConstraint('id', 'number')
)
"""
def test_constraints_class(self):
Table(
'simple_items', self.metadata,
Column('id', INTEGER, primary_key=True),
Column('number', INTEGER),
CheckConstraint('number > 0'),
UniqueConstraint('id', 'number')
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import CheckConstraint, Column, Integer, UniqueConstraint
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class SimpleItem(Base):
__tablename__ = 'simple_items'
__table_args__ = (
CheckConstraint('number > 0'),
UniqueConstraint('id', 'number')
)
id = Column(Integer, primary_key=True)
number = Column(Integer)
"""
def test_noindexes_table(self):
simple_items = Table(
'simple_items', self.metadata,
Column('number', INTEGER),
CheckConstraint('number > 2')
)
simple_items.indexes.add(Index('idx_number', simple_items.c.number))
assert self.generate_code(noindexes=True) == """\
# coding: utf-8
from sqlalchemy import CheckConstraint, Column, Integer, MetaData, Table
metadata = MetaData()
t_simple_items = Table(
'simple_items', metadata,
Column('number', Integer),
CheckConstraint('number > 2')
)
"""
def test_noconstraints_table(self):
simple_items = Table(
'simple_items', self.metadata,
Column('number', INTEGER),
CheckConstraint('number > 2')
)
simple_items.indexes.add(Index('idx_number', simple_items.c.number))
assert self.generate_code(noconstraints=True) == """\
# coding: utf-8
from sqlalchemy import Column, Integer, MetaData, Table
metadata = MetaData()
t_simple_items = Table(
'simple_items', metadata,
Column('number', Integer, index=True)
)
"""
def test_indexes_table(self):
simple_items = Table(
'simple_items', self.metadata,
Column('id', INTEGER),
Column('number', INTEGER),
Column('text', VARCHAR)
)
simple_items.indexes.add(Index('idx_number', simple_items.c.number))
simple_items.indexes.add(Index('idx_text_number', simple_items.c.text, simple_items.c.number, unique=True))
simple_items.indexes.add(Index('idx_text', simple_items.c.text, unique=True))
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, Index, Integer, MetaData, String, Table
metadata = MetaData()
t_simple_items = Table(
'simple_items', metadata,
Column('id', Integer),
Column('number', Integer, index=True),
Column('text', String, unique=True),
Index('idx_text_number', 'text', 'number', unique=True)
)
"""
def test_indexes_class(self):
simple_items = Table(
'simple_items', self.metadata,
Column('id', INTEGER, primary_key=True),
Column('number', INTEGER),
Column('text', VARCHAR)
)
simple_items.indexes.add(Index('idx_number', simple_items.c.number))
simple_items.indexes.add(Index('idx_text_number', simple_items.c.text, simple_items.c.number))
simple_items.indexes.add(Index('idx_text', simple_items.c.text, unique=True))
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, Index, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class SimpleItem(Base):
__tablename__ = 'simple_items'
__table_args__ = (
Index('idx_text_number', 'text', 'number'),
)
id = Column(Integer, primary_key=True)
number = Column(Integer, index=True)
text = Column(String, unique=True)
"""
def test_onetomany(self):
Table(
'simple_items', self.metadata,
Column('id', INTEGER, primary_key=True),
Column('container_id', INTEGER),
ForeignKeyConstraint(['container_id'], ['simple_containers.id']),
)
Table(
'simple_containers', self.metadata,
Column('id', INTEGER, primary_key=True)
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class SimpleContainer(Base):
__tablename__ = 'simple_containers'
id = Column(Integer, primary_key=True)
class SimpleItem(Base):
__tablename__ = 'simple_items'
id = Column(Integer, primary_key=True)
container_id = Column(ForeignKey('simple_containers.id'))
container = relationship('SimpleContainer')
"""
def test_onetomany_selfref(self):
Table(
'simple_items', self.metadata,
Column('id', INTEGER, primary_key=True),
Column('parent_item_id', INTEGER),
ForeignKeyConstraint(['parent_item_id'], ['simple_items.id'])
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class SimpleItem(Base):
__tablename__ = 'simple_items'
id = Column(Integer, primary_key=True)
parent_item_id = Column(ForeignKey('simple_items.id'))
parent_item = relationship('SimpleItem', remote_side=[id])
"""
def test_onetomany_selfref_multi(self):
Table(
'simple_items', self.metadata,
Column('id', INTEGER, primary_key=True),
Column('parent_item_id', INTEGER),
Column('top_item_id', INTEGER),
ForeignKeyConstraint(['parent_item_id'], ['simple_items.id']),
ForeignKeyConstraint(['top_item_id'], ['simple_items.id'])
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class SimpleItem(Base):
__tablename__ = 'simple_items'
id = Column(Integer, primary_key=True)
parent_item_id = Column(ForeignKey('simple_items.id'))
top_item_id = Column(ForeignKey('simple_items.id'))
parent_item = relationship('SimpleItem', remote_side=[id], primaryjoin='SimpleItem.parent_item_id == SimpleItem.id')
top_item = relationship('SimpleItem', remote_side=[id], primaryjoin='SimpleItem.top_item_id == SimpleItem.id')
"""
def test_onetomany_composite(self):
Table(
'simple_items', self.metadata,
Column('id', INTEGER, primary_key=True),
Column('container_id1', INTEGER),
Column('container_id2', INTEGER),
ForeignKeyConstraint(['container_id1', 'container_id2'], ['simple_containers.id1', 'simple_containers.id2'],
ondelete='CASCADE', onupdate='CASCADE')
)
Table(
'simple_containers', self.metadata,
Column('id1', INTEGER, primary_key=True),
Column('id2', INTEGER, primary_key=True)
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, ForeignKeyConstraint, Integer
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class SimpleContainer(Base):
__tablename__ = 'simple_containers'
id1 = Column(Integer, primary_key=True, nullable=False)
id2 = Column(Integer, primary_key=True, nullable=False)
class SimpleItem(Base):
__tablename__ = 'simple_items'
__table_args__ = (
ForeignKeyConstraint(['container_id1', 'container_id2'], ['simple_containers.id1', 'simple_containers.id2'], \
ondelete='CASCADE', onupdate='CASCADE'),
)
id = Column(Integer, primary_key=True)
container_id1 = Column(Integer)
container_id2 = Column(Integer)
simple_container = relationship('SimpleContainer')
"""
def test_onetomany_multiref(self):
Table(
'simple_items', self.metadata,
Column('id', INTEGER, primary_key=True),
Column('parent_container_id', INTEGER),
Column('top_container_id', INTEGER),
ForeignKeyConstraint(['parent_container_id'], ['simple_containers.id']),
ForeignKeyConstraint(['top_container_id'], ['simple_containers.id'])
)
Table(
'simple_containers', self.metadata,
Column('id', INTEGER, primary_key=True)
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class SimpleContainer(Base):
__tablename__ = 'simple_containers'
id = Column(Integer, primary_key=True)
class SimpleItem(Base):
__tablename__ = 'simple_items'
id = Column(Integer, primary_key=True)
parent_container_id = Column(ForeignKey('simple_containers.id'))
top_container_id = Column(ForeignKey('simple_containers.id'))
parent_container = relationship('SimpleContainer', \
primaryjoin='SimpleItem.parent_container_id == SimpleContainer.id')
top_container = relationship('SimpleContainer', primaryjoin='SimpleItem.top_container_id == SimpleContainer.id')
"""
def test_onetoone(self):
Table(
'simple_items', self.metadata,
Column('id', INTEGER, primary_key=True),
Column('other_item_id', INTEGER),
ForeignKeyConstraint(['other_item_id'], ['other_items.id']),
UniqueConstraint('other_item_id')
)
Table(
'other_items', self.metadata,
Column('id', INTEGER, primary_key=True)
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class OtherItem(Base):
__tablename__ = 'other_items'
id = Column(Integer, primary_key=True)
class SimpleItem(Base):
__tablename__ = 'simple_items'
id = Column(Integer, primary_key=True)
other_item_id = Column(ForeignKey('other_items.id'), unique=True)
other_item = relationship('OtherItem', uselist=False)
"""
def test_onetomany_noinflect(self):
Table(
'oglkrogk', self.metadata,
Column('id', INTEGER, primary_key=True),
Column('fehwiuhfiwID', INTEGER),
ForeignKeyConstraint(['fehwiuhfiwID'], ['fehwiuhfiw.id']),
)
Table(
'fehwiuhfiw', self.metadata,
Column('id', INTEGER, primary_key=True)
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class Fehwiuhfiw(Base):
__tablename__ = 'fehwiuhfiw'
id = Column(Integer, primary_key=True)
class Oglkrogk(Base):
__tablename__ = 'oglkrogk'
id = Column(Integer, primary_key=True)
fehwiuhfiwID = Column(ForeignKey('fehwiuhfiw.id'))
fehwiuhfiw = relationship('Fehwiuhfiw')
"""
def test_manytomany(self):
Table(
'simple_items', self.metadata,
Column('id', INTEGER, primary_key=True)
)
Table(
'simple_containers', self.metadata,
Column('id', INTEGER, primary_key=True)
)
Table(
'container_items', self.metadata,
Column('item_id', INTEGER),
Column('container_id', INTEGER),
ForeignKeyConstraint(['item_id'], ['simple_items.id']),
ForeignKeyConstraint(['container_id'], ['simple_containers.id'])
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, ForeignKey, Integer, Table
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
t_container_items = Table(
'container_items', metadata,
Column('item_id', ForeignKey('simple_items.id')),
Column('container_id', ForeignKey('simple_containers.id'))
)
class SimpleContainer(Base):
__tablename__ = 'simple_containers'
id = Column(Integer, primary_key=True)
items = relationship('SimpleItem', secondary='container_items')
class SimpleItem(Base):
__tablename__ = 'simple_items'
id = Column(Integer, primary_key=True)
"""
def test_manytomany_selfref(self):
Table(
'simple_items', self.metadata,
Column('id', INTEGER, primary_key=True)
)
Table(
'child_items', self.metadata,
Column('parent_id', INTEGER),
Column('child_id', INTEGER),
ForeignKeyConstraint(['parent_id'], ['simple_items.id']),
ForeignKeyConstraint(['child_id'], ['simple_items.id'])
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, ForeignKey, Integer, Table
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
t_child_items = Table(
'child_items', metadata,
Column('parent_id', ForeignKey('simple_items.id')),
Column('child_id', ForeignKey('simple_items.id'))
)
class SimpleItem(Base):
__tablename__ = 'simple_items'
id = Column(Integer, primary_key=True)
parents = relationship(
'SimpleItem',
secondary='child_items',
primaryjoin='SimpleItem.id == child_items.c.child_id',
secondaryjoin='SimpleItem.id == child_items.c.parent_id'
)
"""
def test_manytomany_composite(self):
Table(
'simple_items', self.metadata,
Column('id1', INTEGER, primary_key=True),
Column('id2', INTEGER, primary_key=True)
)
Table(
'simple_containers', self.metadata,
Column('id1', INTEGER, primary_key=True),
Column('id2', INTEGER, primary_key=True)
)
Table(
'container_items', self.metadata,
Column('item_id1', INTEGER),
Column('item_id2', INTEGER),
Column('container_id1', INTEGER),
Column('container_id2', INTEGER),
ForeignKeyConstraint(['item_id1', 'item_id2'], ['simple_items.id1', 'simple_items.id2']),
ForeignKeyConstraint(['container_id1', 'container_id2'], ['simple_containers.id1', 'simple_containers.id2'])
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, ForeignKeyConstraint, Integer, Table
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
t_container_items = Table(
'container_items', metadata,
Column('item_id1', Integer),
Column('item_id2', Integer),
Column('container_id1', Integer),
Column('container_id2', Integer),
ForeignKeyConstraint(['container_id1', 'container_id2'], ['simple_containers.id1', 'simple_containers.id2']),
ForeignKeyConstraint(['item_id1', 'item_id2'], ['simple_items.id1', 'simple_items.id2'])
)
class SimpleContainer(Base):
__tablename__ = 'simple_containers'
id1 = Column(Integer, primary_key=True, nullable=False)
id2 = Column(Integer, primary_key=True, nullable=False)
simple_items = relationship('SimpleItem', secondary='container_items')
class SimpleItem(Base):
__tablename__ = 'simple_items'
id1 = Column(Integer, primary_key=True, nullable=False)
id2 = Column(Integer, primary_key=True, nullable=False)
"""
def test_joined_inheritance(self):
Table(
'simple_sub_items', self.metadata,
Column('simple_items_id', INTEGER, primary_key=True),
Column('data3', INTEGER),
ForeignKeyConstraint(['simple_items_id'], ['simple_items.super_item_id'])
)
Table(
'simple_super_items', self.metadata,
Column('id', INTEGER, primary_key=True),
Column('data1', INTEGER)
)
Table(
'simple_items', self.metadata,
Column('super_item_id', INTEGER, primary_key=True),
Column('data2', INTEGER),
ForeignKeyConstraint(['super_item_id'], ['simple_super_items.id'])
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class SimpleSuperItem(Base):
__tablename__ = 'simple_super_items'
id = Column(Integer, primary_key=True)
data1 = Column(Integer)
class SimpleItem(SimpleSuperItem):
__tablename__ = 'simple_items'
super_item_id = Column(ForeignKey('simple_super_items.id'), primary_key=True)
data2 = Column(Integer)
class SimpleSubItem(SimpleItem):
__tablename__ = 'simple_sub_items'
simple_items_id = Column(ForeignKey('simple_items.super_item_id'), primary_key=True)
data3 = Column(Integer)
"""
def test_no_inflect(self):
Table(
'simple_items', self.metadata,
Column('id', INTEGER, primary_key=True)
)
assert self.generate_code(noinflect=True) == """\
# coding: utf-8
from sqlalchemy import Column, Integer
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class SimpleItems(Base):
__tablename__ = 'simple_items'
id = Column(Integer, primary_key=True)
"""
def test_no_classes(self):
Table(
'simple_items', self.metadata,
Column('id', INTEGER, primary_key=True)
)
assert self.generate_code(noclasses=True) == """\
# coding: utf-8
from sqlalchemy import Column, Integer, MetaData, Table
metadata = MetaData()
t_simple_items = Table(
'simple_items', metadata,
Column('id', Integer, primary_key=True)
)
"""
def test_table_kwargs(self):
Table(
'simple_items', self.metadata,
Column('id', INTEGER, primary_key=True),
schema='testschema'
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, Integer
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class SimpleItem(Base):
__tablename__ = 'simple_items'
__table_args__ = {'schema': 'testschema'}
id = Column(Integer, primary_key=True)
"""
def test_table_args_kwargs(self):
simple_items = Table(
'simple_items', self.metadata,
Column('id', INTEGER, primary_key=True),
Column('name', VARCHAR),
schema='testschema'
)
simple_items.indexes.add(Index('testidx', simple_items.c.id, simple_items.c.name))
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, Index, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class SimpleItem(Base):
__tablename__ = 'simple_items'
__table_args__ = (
Index('testidx', 'id', 'name'),
{'schema': 'testschema'}
)
id = Column(Integer, primary_key=True)
name = Column(String)
"""
def test_schema_table(self):
Table(
'simple_items', self.metadata,
Column('name', VARCHAR),
schema='testschema'
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, MetaData, String, Table
metadata = MetaData()
t_simple_items = Table(
'simple_items', metadata,
Column('name', String),
schema='testschema'
)
"""
def test_schema_boolean(self):
Table(
'simple_items', self.metadata,
Column('bool1', INTEGER),
CheckConstraint('testschema.simple_items.bool1 IN (0, 1)'),
schema='testschema'
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Boolean, Column, MetaData, Table
metadata = MetaData()
t_simple_items = Table(
'simple_items', metadata,
Column('bool1', Boolean),
schema='testschema'
)
"""
def test_foreign_key_options(self):
Table(
'simple_items', self.metadata,
Column('name', VARCHAR, ForeignKey('simple_items.name', ondelete='CASCADE', onupdate='CASCADE',
deferrable=True, initially='DEFERRED'))
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, ForeignKey, MetaData, String, Table
metadata = MetaData()
t_simple_items = Table(
'simple_items', metadata,
Column('name', String, ForeignKey('simple_items.name', ondelete='CASCADE', onupdate='CASCADE', \
deferrable=True, initially='DEFERRED'))
)
"""
def test_foreign_key_schema(self):
Table(
'simple_items', self.metadata,
Column('id', INTEGER, primary_key=True),
Column('other_item_id', INTEGER),
ForeignKeyConstraint(['other_item_id'], ['otherschema.other_items.id'])
)
Table(
'other_items', self.metadata,
Column('id', INTEGER, primary_key=True),
schema='otherschema'
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, ForeignKey, Integer
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class SimpleItem(Base):
__tablename__ = 'simple_items'
id = Column(Integer, primary_key=True)
other_item_id = Column(ForeignKey('otherschema.other_items.id'))
other_item = relationship('OtherItem')
class OtherItem(Base):
__tablename__ = 'other_items'
__table_args__ = {'schema': 'otherschema'}
id = Column(Integer, primary_key=True)
"""
def test_pk_default(self):
Table(
'simple_items', self.metadata,
Column('id', INTEGER, primary_key=True, server_default=text('uuid_generate_v4()'))
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, Integer, text
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class SimpleItem(Base):
__tablename__ = 'simple_items'
id = Column(Integer, primary_key=True, server_default=text("uuid_generate_v4()"))
"""
def test_server_default_multiline(self):
Table(
'simple_items', self.metadata,
Column('id', INTEGER, primary_key=True, server_default=text("""\
/*Comment*/
/*Next line*/
something()"""))
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, Integer, text
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class SimpleItem(Base):
__tablename__ = 'simple_items'
id = Column(Integer, primary_key=True, server_default=text(\"""\\
/*Comment*/
/*Next line*/
something()\"""))
"""
def test_invalid_attribute_names(self):
Table(
'simple_items', self.metadata,
Column('id-test', INTEGER, primary_key=True),
Column('4test', INTEGER),
Column('_4test', INTEGER),
Column('def', INTEGER)
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, Integer
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class SimpleItem(Base):
__tablename__ = 'simple_items'
id_test = Column('id-test', Integer, primary_key=True)
_4test = Column('4test', Integer)
_4test1 = Column('_4test', Integer)
_def = Column('def', Integer)
"""
def test_pascal(self):
Table(
'CustomerAPIPreference', self.metadata,
Column('id', INTEGER, primary_key=True)
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, Integer
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class CustomerAPIPreference(Base):
__tablename__ = 'CustomerAPIPreference'
id = Column(Integer, primary_key=True)
"""
def test_underscore(self):
Table(
'customer_api_preference', self.metadata,
Column('id', INTEGER, primary_key=True)
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, Integer
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class CustomerApiPreference(Base):
__tablename__ = 'customer_api_preference'
id = Column(Integer, primary_key=True)
"""
def test_pascal_underscore(self):
Table(
'customer_API_Preference', self.metadata,
Column('id', INTEGER, primary_key=True)
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, Integer
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class CustomerAPIPreference(Base):
__tablename__ = 'customer_API_Preference'
id = Column(Integer, primary_key=True)
"""
def test_pascal_multiple_underscore(self):
Table(
'customer_API__Preference', self.metadata,
Column('id', INTEGER, primary_key=True)
)
assert self.generate_code() == """\
# coding: utf-8
from sqlalchemy import Column, Integer
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
metadata = Base.metadata
class CustomerAPIPreference(Base):
__tablename__ = 'customer_API__Preference'
id = Column(Integer, primary_key=True)
"""
sqlacodegen-1.1.6/MANIFEST.in 0000664 0001750 0001750 00000000020 12361645031 015441 0 ustar alex alex 0000000 0000000 include tox.ini
sqlacodegen-1.1.6/README.rst 0000664 0001750 0001750 00000006770 12361645031 015414 0 ustar alex alex 0000000 0000000 This is a tool that reads the structure of an existing database and generates
the appropriate SQLAlchemy model code, using the declarative style if
possible.
This tool was written as a replacement for
`sqlautocode `_, which was suffering
from several issues (including, but not limited to, incompatibility with
Python 3 and the latest SQLAlchemy version).
Features
========
* Supports SQLAlchemy 0.6.x - 0.9.x
* Produces declarative code that almost looks like it was hand written
* Produces `PEP 8 `_ compliant code
* Accurately determines relationships, including many-to-many, one-to-one
* Automatically detects joined table inheritance
* Excellent test coverage
Usage instructions
==================
Installation
------------
To install, do::
pip install sqlacodegen
or, failing that::
easy_install sqlacodegen
Example usage
-------------
At the minimum, you have to give sqlacodegen a database URL.
The URL is passed directly to SQLAlchemy's
`create_engine() `_
method so please refer to
`SQLAlchemy's documentation `_
for instructions on how to construct a proper URL.
Examples::
sqlacodegen postgresql:///some_local_db
sqlacodegen mysql+oursql://user:password@localhost/dbname
sqlacodegen sqlite:///database.db
To see the full list of options::
sqlacodegen --help
Model class naming logic
------------------------
The table name (which is assumed to be in English) is converted to singular
form using the "inflect" library. Then, every underscore is removed while
transforming the next letter to upper case. For example, ``sales_invoices``
becomes ``SalesInvoice``.
Relationship detection logic
----------------------------
Relationships are detected based on existing foreign key constraints as
follows:
* **many-to-one**: a foreign key constraint exists on the table
* **one-to-one**: same as **many-to-one**, but a unique constraint exists on
the column(s) involved
* **many-to-many**: an association table is found to exist between two tables
A table is considered an association table if it satisfies all of the
following conditions:
#. has exactly two foreign key constraints
#. all its columns are involved in said constraints
Relationship naming logic
-------------------------
Relationships are typically named based on the opposite class name.
For example, if an ``Employee`` class has a column named ``employer`` which
has a foreign key to ``Company.id``, the relationship is named ``company``.
A special case for single column many-to-one and one-to-one relationships,
however, is if the column is named like ``employer_id``. Then the
relationship is named ``employer`` due to that ``_id`` suffix.
If more than one relationship would be created with the same name, the
latter ones are appended numeric suffixes, starting from 1.
Source code
===========
The source can be browsed at `Bitbucket
`_.
Reporting bugs
==============
A `bug tracker `_
is provided by bitbucket.org.
Getting help
============
If you have problems or other questions, you can either:
* Ask on the `SQLAlchemy Google group
`_, or
* Ask on the ``#sqlalchemy`` channel on
`Freenode IRC `_
sqlacodegen-1.1.6/setup.py 0000664 0001750 0001750 00000003653 12371643533 015442 0 ustar alex alex 0000000 0000000 import sys
import os.path
from setuptools import setup, find_packages
from setuptools.command.test import test as TestCommand
import sqlacodegen
class PyTest(TestCommand):
def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = []
self.test_suite = True
def run_tests(self):
import pytest
errno = pytest.main(self.test_args)
sys.exit(errno)
extra_requirements = ()
if sys.version_info < (2, 7):
extra_requirements = ('argparse',)
here = os.path.dirname(__file__)
readme_path = os.path.join(here, 'README.rst')
readme = open(readme_path).read()
setup(
name='sqlacodegen',
description='Automatic model code generator for SQLAlchemy',
long_description=readme,
version=sqlacodegen.__version__,
author='Alex Gronholm',
author_email='sqlacodegen@nextday.fi',
url='http://pypi.python.org/pypi/sqlacodegen/',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Environment :: Console',
'Topic :: Database',
'Topic :: Software Development :: Code Generators',
'Programming Language :: Python',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4'
],
keywords='sqlalchemy',
license='MIT',
packages=find_packages(exclude=['tests']),
install_requires=(
'SQLAlchemy >= 0.6.0',
'inflect >= 0.2.0'
) + extra_requirements,
tests_require=['pytest', 'pytest-pep8'],
cmdclass={'test': PyTest},
zip_safe=False,
entry_points={
'console_scripts': [
'sqlacodegen=sqlacodegen.main:main'
]
}
)
sqlacodegen-1.1.6/tox.ini 0000664 0001750 0001750 00000001023 12525420624 015223 0 ustar alex alex 0000000 0000000 [tox]
envlist = py26,py27,py32,py33,py34,py26-sqla09,py26-sqla08,py26-sqla07,py26-sqla06,pep8
[testenv]
deps=pytest
commands=py.test []
[testenv:py26-sqla09]
basepython=python2.6
deps=pytest
sqlalchemy<=0.9.99
[testenv:py26-sqla08]
basepython=python2.6
deps=pytest
sqlalchemy<=0.8.99
[testenv:py26-sqla07]
basepython=python2.6
deps=pytest
sqlalchemy<=0.7.99
[testenv:py26-sqla06]
basepython=python2.6
deps=pytest
sqlalchemy==0.6.0
[testenv:pep8]
deps=pytest
pytest-pep8
commands=py.test --pep8 -m pep8 []
sqlacodegen-1.1.6/PKG-INFO 0000664 0001750 0001750 00000012521 12525442313 015011 0 ustar alex alex 0000000 0000000 Metadata-Version: 1.1
Name: sqlacodegen
Version: 1.1.6
Summary: Automatic model code generator for SQLAlchemy
Home-page: http://pypi.python.org/pypi/sqlacodegen/
Author: Alex Gronholm
Author-email: sqlacodegen@nextday.fi
License: MIT
Description: This is a tool that reads the structure of an existing database and generates
the appropriate SQLAlchemy model code, using the declarative style if
possible.
This tool was written as a replacement for
`sqlautocode `_, which was suffering
from several issues (including, but not limited to, incompatibility with
Python 3 and the latest SQLAlchemy version).
Features
========
* Supports SQLAlchemy 0.6.x - 0.9.x
* Produces declarative code that almost looks like it was hand written
* Produces `PEP 8 `_ compliant code
* Accurately determines relationships, including many-to-many, one-to-one
* Automatically detects joined table inheritance
* Excellent test coverage
Usage instructions
==================
Installation
------------
To install, do::
pip install sqlacodegen
or, failing that::
easy_install sqlacodegen
Example usage
-------------
At the minimum, you have to give sqlacodegen a database URL.
The URL is passed directly to SQLAlchemy's
`create_engine() `_
method so please refer to
`SQLAlchemy's documentation `_
for instructions on how to construct a proper URL.
Examples::
sqlacodegen postgresql:///some_local_db
sqlacodegen mysql+oursql://user:password@localhost/dbname
sqlacodegen sqlite:///database.db
To see the full list of options::
sqlacodegen --help
Model class naming logic
------------------------
The table name (which is assumed to be in English) is converted to singular
form using the "inflect" library. Then, every underscore is removed while
transforming the next letter to upper case. For example, ``sales_invoices``
becomes ``SalesInvoice``.
Relationship detection logic
----------------------------
Relationships are detected based on existing foreign key constraints as
follows:
* **many-to-one**: a foreign key constraint exists on the table
* **one-to-one**: same as **many-to-one**, but a unique constraint exists on
the column(s) involved
* **many-to-many**: an association table is found to exist between two tables
A table is considered an association table if it satisfies all of the
following conditions:
#. has exactly two foreign key constraints
#. all its columns are involved in said constraints
Relationship naming logic
-------------------------
Relationships are typically named based on the opposite class name.
For example, if an ``Employee`` class has a column named ``employer`` which
has a foreign key to ``Company.id``, the relationship is named ``company``.
A special case for single column many-to-one and one-to-one relationships,
however, is if the column is named like ``employer_id``. Then the
relationship is named ``employer`` due to that ``_id`` suffix.
If more than one relationship would be created with the same name, the
latter ones are appended numeric suffixes, starting from 1.
Source code
===========
The source can be browsed at `Bitbucket
`_.
Reporting bugs
==============
A `bug tracker `_
is provided by bitbucket.org.
Getting help
============
If you have problems or other questions, you can either:
* Ask on the `SQLAlchemy Google group
`_, or
* Ask on the ``#sqlalchemy`` channel on
`Freenode IRC `_
Keywords: sqlalchemy
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Environment :: Console
Classifier: Topic :: Database
Classifier: Topic :: Software Development :: Code Generators
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.2
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
sqlacodegen-1.1.6/setup.cfg 0000664 0001750 0001750 00000000434 12525442313 015535 0 ustar alex alex 0000000 0000000 [pep8]
max-line-length = 120
exclude = .tox
[pytest]
pep8maxlinelength = 120
addopts = --tb=short
[wheel]
universal = 1
[metadata]
requires-dist =
SQLAlchemy >= 0.6.0
inflect >= 0.2.0
argparse; python_version < '2.7'
[egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0