sqlacodegen-1.1.6/0000775000175000017500000000000012525442313013713 5ustar alexalex00000000000000sqlacodegen-1.1.6/sqlacodegen/0000775000175000017500000000000012525442313016200 5ustar alexalex00000000000000sqlacodegen-1.1.6/sqlacodegen/__init__.py0000664000175000017500000000004012525442060020302 0ustar alexalex00000000000000version = __version__ = '1.1.6' sqlacodegen-1.1.6/sqlacodegen/codegen.py0000664000175000017500000006141612525441622020170 0ustar alexalex00000000000000"""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.py0000664000175000017500000000413112501317520017470 0ustar alexalex00000000000000""" """ 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/0000775000175000017500000000000012525442313017672 5ustar alexalex00000000000000sqlacodegen-1.1.6/sqlacodegen.egg-info/PKG-INFO0000664000175000017500000001252112525442313020770 0ustar alexalex00000000000000Metadata-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.txt0000664000175000017500000000063512525442313021562 0ustar alexalex00000000000000MANIFEST.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.pysqlacodegen-1.1.6/sqlacodegen.egg-info/dependency_links.txt0000664000175000017500000000000112525442313023740 0ustar alexalex00000000000000 sqlacodegen-1.1.6/sqlacodegen.egg-info/entry_points.txt0000664000175000017500000000006712525442313023173 0ustar alexalex00000000000000[console_scripts] sqlacodegen = sqlacodegen.main:main sqlacodegen-1.1.6/sqlacodegen.egg-info/not-zip-safe0000664000175000017500000000000112361651041022116 0ustar alexalex00000000000000 sqlacodegen-1.1.6/sqlacodegen.egg-info/pbr.json0000664000175000017500000000005712525442044021353 0ustar alexalex00000000000000{"is_release": false, "git_version": "dbb6ac7"}sqlacodegen-1.1.6/sqlacodegen.egg-info/requires.txt0000664000175000017500000000004512525442313022271 0ustar alexalex00000000000000SQLAlchemy >= 0.6.0 inflect >= 0.2.0 sqlacodegen-1.1.6/sqlacodegen.egg-info/top_level.txt0000664000175000017500000000001412525442313022417 0ustar alexalex00000000000000sqlacodegen sqlacodegen-1.1.6/test/0000775000175000017500000000000012525442313014672 5ustar alexalex00000000000000sqlacodegen-1.1.6/test/test_codegen.py0000664000175000017500000007451112525441324017720 0ustar alexalex00000000000000from __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.in0000664000175000017500000000002012361645031015441 0ustar alexalex00000000000000include tox.ini sqlacodegen-1.1.6/README.rst0000664000175000017500000000677012361645031015414 0ustar alexalex00000000000000This 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.py0000664000175000017500000000365312371643533015442 0ustar alexalex00000000000000import 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.ini0000664000175000017500000000102312525420624015223 0ustar alexalex00000000000000[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-INFO0000664000175000017500000001252112525442313015011 0ustar alexalex00000000000000Metadata-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.cfg0000664000175000017500000000043412525442313015535 0ustar alexalex00000000000000[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