Updated docs
This commit is contained in:
@@ -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)
|
||||||
^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
|
||||||
|
2
setup.py
2
setup.py
@@ -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',
|
||||||
|
@@ -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
|
||||||
|
@@ -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)
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user