Updated docs

This commit is contained in:
Konsta Vesterinen
2013-07-03 11:17:52 +03:00
parent bb526f283b
commit 5d29c26533
6 changed files with 112 additions and 2 deletions

View File

@@ -4,6 +4,12 @@ Changelog
Here you can see the full list of changes between each SQLAlchemy-Utils release. Here you can see the full list of changes between each SQLAlchemy-Utils release.
0.14.3 (2013-07-03)
^^^^^^^^^^^^^^^^^^^
- Added non_indexed_foreign_keys utility function
0.14.2 (2013-07-02) 0.14.2 (2013-07-02)
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^

View File

@@ -179,6 +179,8 @@ API Documentation
:members: :members:
.. autofunction:: sort_query .. autofunction:: sort_query
.. autofunction:: escape_like .. autofunction:: escape_like
.. autofunction:: non_indexed_foreign_keys
.. autofunction:: is_indexed_foreign_key
.. include:: ../CHANGES.rst .. include:: ../CHANGES.rst

View File

@@ -24,7 +24,7 @@ class PyTest(Command):
setup( setup(
name='SQLAlchemy-Utils', name='SQLAlchemy-Utils',
version='0.14.2', version='0.14.3',
url='https://github.com/kvesteri/sqlalchemy-utils', url='https://github.com/kvesteri/sqlalchemy-utils',
license='BSD', license='BSD',
author='Konsta Vesterinen', author='Konsta Vesterinen',

View File

@@ -1,8 +1,10 @@
from collections import defaultdict
from sqlalchemy.orm import defer from sqlalchemy.orm import defer
from sqlalchemy.orm.mapper import Mapper from sqlalchemy.orm.mapper import Mapper
from sqlalchemy.orm.query import _ColumnEntity from sqlalchemy.orm.query import _ColumnEntity
from sqlalchemy.orm.properties import ColumnProperty from sqlalchemy.orm.properties import ColumnProperty
from sqlalchemy.orm.util import AliasedInsp from sqlalchemy.orm.util import AliasedInsp
from sqlalchemy.schema import MetaData, Table, ForeignKeyConstraint
from sqlalchemy.sql.expression import desc, asc, Label from sqlalchemy.sql.expression import desc, asc, Label
@@ -241,3 +243,55 @@ def table_name(class_):
return class_.__tablename__ return class_.__tablename__
except AttributeError: except AttributeError:
return class_.__table__.name return class_.__table__.name
def non_indexed_foreign_keys(metadata, engine=None):
"""
Finds all non indexed foreign keys from all tables of given MetaData.
Very useful for optimizing postgresql database and finding out which
foreign keys need indexes.
:param metadata: MetaData object to inspect tables from
"""
reflected_metadata = MetaData()
if metadata.bind is None and engine is None:
raise Exception(
'Either pass a metadata object with bind or '
'pass engine as a second parameter'
)
constraints = defaultdict(list)
for table_name in metadata.tables.keys():
table = Table(
table_name,
reflected_metadata,
autoload=True,
autoload_with=metadata.bind or engine
)
for constraint in table.constraints:
if not isinstance(constraint, ForeignKeyConstraint):
continue
if not is_indexed_foreign_key(constraint):
constraints[table.name].append(constraint)
return dict(constraints)
def is_indexed_foreign_key(constraint):
"""
Whether or not given foreign key constraint's columns have been indexed.
:param constraint: ForeignKeyConstraint object to check the indexes
"""
for index in constraint.table.indexes:
index_column_names = set([
column.name for column in index.columns
])
if index_column_names == set(constraint.columns):
return True
return False

View File

@@ -23,7 +23,7 @@ class TestCase(object):
self.engine = create_engine('sqlite:///:memory:') self.engine = create_engine('sqlite:///:memory:')
self.connection = self.engine.connect() self.connection = self.engine.connect()
self.Base = declarative_base() self.Base = declarative_base()
self.Base2 = declarative_base()
self.create_models() self.create_models()
self.Base.metadata.create_all(self.connection) self.Base.metadata.create_all(self.connection)

View File

@@ -1,4 +1,6 @@
import sqlalchemy as sa
from sqlalchemy_utils import escape_like, defer_except from sqlalchemy_utils import escape_like, defer_except
from sqlalchemy_utils.functions import non_indexed_foreign_keys
from tests import TestCase from tests import TestCase
@@ -12,3 +14,49 @@ class TestDeferExcept(TestCase):
query = self.session.query(self.Article) query = self.session.query(self.Article)
query = defer_except(query, ['id']) query = defer_except(query, ['id'])
assert str(query) == 'SELECT article.id AS article_id \nFROM article' assert str(query) == 'SELECT article.id AS article_id \nFROM article'
class TestFindNonIndexedForeignKeys(TestCase):
def create_models(self):
class User(self.Base):
__tablename__ = 'user'
id = sa.Column(sa.Integer, autoincrement=True, primary_key=True)
name = sa.Column(sa.Unicode(255))
class Category(self.Base):
__tablename__ = 'category'
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.Unicode(255))
class Article(self.Base):
__tablename__ = 'article'
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.Unicode(255))
author_id = sa.Column(
sa.Integer, sa.ForeignKey(User.id), index=True
)
category_id = sa.Column(sa.Integer, sa.ForeignKey(Category.id))
category = sa.orm.relationship(
Category,
primaryjoin=category_id == Category.id,
backref=sa.orm.backref(
'articles',
)
)
self.User = User
self.Category = Category
self.Article = Article
def test_finds_all_non_indexed_fks(self):
fks = non_indexed_foreign_keys(self.Base.metadata, self.engine)
assert (
'article' in
fks
)
column_names = [
column_name for column_name in fks['article'][0].columns
]
assert 'category_id' in column_names
assert 'author_id' not in column_names