diff --git a/docs/index.rst b/docs/index.rst index 3306dcc..d1cbdf3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -54,379 +54,89 @@ Data types SQLAlchemy-Utils provides various new data types for SQLAlchemy. +.. module:: sqlalchemy_utils.types ChoiceType ^^^^^^^^^^ -ChoiceType offers way of having fixed set of choices for given column. Columns with ChoiceTypes are automatically coerced to Choice objects. +.. module:: sqlalchemy_utils.types.choice - -:: - - - class User(self.Base): - TYPES = [ - (u'admin', u'Admin'), - (u'regular-user', u'Regular user') - ] - - __tablename__ = 'user' - id = sa.Column(sa.Integer, primary_key=True) - name = sa.Column(sa.Unicode(255)) - type = sa.Column(ChoiceType(TYPES)) - - - user = User(type=u'admin') - user.type # Choice(type='admin', value=u'Admin') - - - -ChoiceType is very useful when the rendered values change based on user's locale: - -:: - - from babel import lazy_gettext as _ - - - class User(self.Base): - TYPES = [ - (u'admin', _(u'Admin')), - (u'regular-user', _(u'Regular user')) - ] - - __tablename__ = 'user' - id = sa.Column(sa.Integer, primary_key=True) - name = sa.Column(sa.Unicode(255)) - type = sa.Column(ChoiceType(TYPES)) - - - user = User(type=u'admin') - user.type # Choice(type='admin', value=u'Admin') - - print user.type # u'Admin' +.. autoclass:: ChoiceType ColorType ^^^^^^^^^ -ColorType provides a way for saving Color (from colour_ package) objects into database. -ColorType saves Color objects as strings on the way in and converts them back to objects when querying the database. +.. module:: sqlalchemy_utils.types.color -:: - - - from colour import Color - from sqlalchemy_utils import ColorType - - - class Document(Base): - __tablename__ = 'document' - id = sa.Column(sa.Integer, autoincrement=True) - name = sa.Column(sa.Unicode(50)) - background_color = sa.Column(ColorType) - - - document = Document() - document.background_color = Color('#F5F5F5') - session.commit() - - -Querying the database returns Color objects: - -:: - - document = session.query(Document).first() - - document.background_color.hex - # '#f5f5f5' - - -For more information about colour package and Color object, see https://github.com/vaab/colour +.. autoclass:: ColorType JSONType ^^^^^^^^ -JSONType offers way of saving JSON data structures to database. On PostgreSQL the underlying implementation of this data type is 'json' while on other databases its simply 'text'. +.. module:: sqlalchemy_utils.types.json -:: - - - from sqlalchemy_utils import JSONType - - - class Product(Base): - __tablename__ = 'product' - id = sa.Column(sa.Integer, autoincrement=True) - name = sa.Column(sa.Unicode(50)) - details = sa.Column(JSONType) - - - product = Product() - product.details = { - 'color': 'red', - 'type': 'car', - 'max-speed': '400 mph' - } - session.commit() +.. autoclass:: JSONType LocaleType ^^^^^^^^^^ -LocaleType saves Babel_ Locale objects into database. The Locale objects are converted to string on the way in and back to object on the way out. -In order to use LocaleType you need to install Babel_ first. - - -:: - - - from sqlalchemy_utils import LocaleType - from babel import Locale - - - class User(Base): - __tablename__ = 'user' - id = sa.Column(sa.Integer, autoincrement=True) - name = sa.Column(sa.Unicode(50)) - locale = sa.Column(LocaleType) - - - user = User() - user.locale = Locale('en_US') - session.commit() - - -Like many other types this type also supports scalar coercion: - -:: - - - user.locale = 'de_DE' - user.locale # Locale('de_DE') +.. module:: sqlalchemy_utils.types.locale +.. autoclass:: LocaleType NumberRangeType ^^^^^^^^^^^^^^^ -NumberRangeType provides way for saving range of numbers into database. - -Example :: - - - from sqlalchemy_utils import NumberRangeType, NumberRange - - - class Event(Base): - __tablename__ = 'user' - id = sa.Column(sa.Integer, autoincrement=True) - name = sa.Column(sa.Unicode(255)) - estimated_number_of_persons = sa.Column(NumberRangeType) - - - party = Event(name=u'party') - - # we estimate the party to contain minium of 10 persons and at max - # 100 persons - party.estimated_number_of_persons = NumberRange(10, 100) - - print party.estimated_number_of_persons - # '10-100' - - -NumberRange supports some arithmetic operators: -:: - - - meeting = Event(name=u'meeting') - - meeting.estimated_number_of_persons = NumberRange(20, 40) - - total = ( - meeting.estimated_number_of_persons + - party.estimated_number_of_persons - ) - print total - # '30-140' +.. module:: sqlalchemy_utils.types.number_range +.. autoclass:: NumberRangeType ScalarListType ^^^^^^^^^^^^^^ -ScalarListType type provides convenient way for saving multiple scalar values in one -column. ScalarListType works like list on python side and saves the result as comma-separated list -in the database (custom separators can also be used). - -Example :: - - - from sqlalchemy_utils import ScalarListType - - - class User(Base): - __tablename__ = 'user' - id = sa.Column(sa.Integer, autoincrement=True) - hobbies = sa.Column(ScalarListType()) - - - user = User() - user.hobbies = [u'football', u'ice_hockey'] - session.commit() - - -You can easily set up integer lists too: - -:: - - - from sqlalchemy_utils import ScalarListType - - - class Player(Base): - __tablename__ = 'player' - id = sa.Column(sa.Integer, autoincrement=True) - points = sa.Column(ScalarListType(int)) - - - player = Player() - player.points = [11, 12, 8, 80] - session.commit() +.. module:: sqlalchemy_utils.types.scalar_list +.. autoclass:: ScalarListType URLType ^^^^^^^ -URLType stores furl_ objects into database. - -:: - - from sqlalchemy_utils import URLType - from furl import furl - - - class User(Base): - __tablename__ = 'user' - - id = sa.Column(sa.Integer, primary_key=True) - website = sa.Column(URLType) - - - user = User(website=u'www.example.com') - - # website is coerced to furl object, hence all nice furl operations come - # available - user.website.args['some_argument'] = '12' - - print user.website - # www.example.com?some_argument=12 +.. module:: sqlalchemy_utils.types.url +.. autoclass:: URLType UUIDType ^^^^^^^^ -UUIDType will store a UUID in the database in a native format, if available, -or a 16-byte BINARY column or a 32-character CHAR column if not. -:: +.. module:: sqlalchemy_utils.types.uuid - from sqlalchemy_utils import UUIDType - import uuid - - class User(Base): - __tablename__ = 'user' - - # Pass `binary=False` to fallback to CHAR instead of BINARY - id = sa.Column(UUIDType(binary=False), primary_key=True) +.. autoclass:: UUIDType TimezoneType ^^^^^^^^^^^^ -TimezoneType provides a way for saving timezones (from either the pytz or the dateutil package) objects into database. -TimezoneType saves timezone objects as strings on the way in and converts them back to objects when querying the database. +.. module:: sqlalchemy_utils.types.timezone -:: - - from sqlalchemy_utils import UUIDType - - class User(Base): - __tablename__ = 'user' - - # Pass backend='pytz' to change it to use pytz (dateutil by default) - timezone = sa.Column(TimezoneType(backend='pytz')) +.. autoclass:: TimezoneType The generates decorator ----------------------- -Many times you may have generated property values. Usual cases include slugs from names or resized thumbnails from images. +.. module:: sqlalchemy_utils.decorators -SQLAlchemy-Utils provides a way to do this easily with `generates` decorator: - -:: - - - class Article(self.Base): - __tablename__ = 'article' - id = sa.Column(sa.Integer, primary_key=True) - name = sa.Column(sa.Unicode(255)) - slug = sa.Column(sa.Unicode(255)) - - @generates(slug) - def _create_slug(self): - return self.name.lower().replace(' ', '-') - - - article = self.Article() - article.name = u'some article name' - self.session.add(article) - self.session.flush() - assert article.slug == u'some-article-name' - - -You can also pass the attribute name as a string argument for `generates`: - -:: - - class Article(self.Base): - ... - - @generates('slug') - def _create_slug(self): - return self.name.lower().replace(' ', '-') - - -These property generators can even be defined outside classes: - -:: - - - class Article(self.Base): - __tablename__ = 'article' - id = sa.Column(sa.Integer, primary_key=True) - name = sa.Column(sa.Unicode(255)) - slug = sa.Column(sa.Unicode(255)) - - - @generates(Article.slug) - def _create_article_slug(self): - return self.name.lower().replace(' ', '-') - - -Or with lazy evaluated string argument: - -:: - - - @generates('Article.slug') - def _create_article_slug(self): - return self.name.lower().replace(' ', '-') +.. autofunction:: generates Generic Relationship @@ -478,10 +188,6 @@ Generic relationship is a form of relationship that supports creating a 1 to man # Find any events that are bound to users. session.query(Event).filter(Event.object.is_type(User)).all() - - -.. _Babel: http://babel.pocoo.org/ -.. _furl: https://github.com/gruns/furl .. _colour: https://github.com/vaab/colour diff --git a/sqlalchemy_utils/decorators.py b/sqlalchemy_utils/decorators.py index decb5eb..313a1b8 100644 --- a/sqlalchemy_utils/decorators.py +++ b/sqlalchemy_utils/decorators.py @@ -8,6 +8,73 @@ listeners_registered = False def generates(attr): + """ + Many times you may have generated property values. Usual cases include + slugs from names or resized thumbnails from images. + + SQLAlchemy-Utils provides a way to do this easily with `generates` + decorator: + + :: + + + class Article(self.Base): + __tablename__ = 'article' + id = sa.Column(sa.Integer, primary_key=True) + name = sa.Column(sa.Unicode(255)) + slug = sa.Column(sa.Unicode(255)) + + @generates(slug) + def _create_slug(self): + return self.name.lower().replace(' ', '-') + + + article = self.Article() + article.name = u'some article name' + self.session.add(article) + self.session.flush() + assert article.slug == u'some-article-name' + + + You can also pass the attribute name as a string argument for `generates`: + + :: + + class Article(self.Base): + ... + + @generates('slug') + def _create_slug(self): + return self.name.lower().replace(' ', '-') + + + These property generators can even be defined outside classes: + + :: + + + class Article(self.Base): + __tablename__ = 'article' + id = sa.Column(sa.Integer, primary_key=True) + name = sa.Column(sa.Unicode(255)) + slug = sa.Column(sa.Unicode(255)) + + + @generates(Article.slug) + def _create_article_slug(self): + return self.name.lower().replace(' ', '-') + + + Or with lazy evaluated string argument: + + :: + + + @generates('Article.slug') + def _create_article_slug(self): + return self.name.lower().replace(' ', '-') + """ + register_listeners() def wraps(func): diff --git a/sqlalchemy_utils/types/choice.py b/sqlalchemy_utils/types/choice.py index 7527f34..e7afd13 100644 --- a/sqlalchemy_utils/types/choice.py +++ b/sqlalchemy_utils/types/choice.py @@ -28,6 +28,57 @@ class Choice(object): class ChoiceType(types.TypeDecorator, ScalarCoercible): + """ + ChoiceType offers way of having fixed set of choices for given column. + Columns with ChoiceTypes are automatically coerced to Choice objects. + + + :: + + + class User(self.Base): + TYPES = [ + (u'admin', u'Admin'), + (u'regular-user', u'Regular user') + ] + + __tablename__ = 'user' + id = sa.Column(sa.Integer, primary_key=True) + name = sa.Column(sa.Unicode(255)) + type = sa.Column(ChoiceType(TYPES)) + + + user = User(type=u'admin') + user.type # Choice(type='admin', value=u'Admin') + + + + ChoiceType is very useful when the rendered values change based on user's + locale: + + :: + + from babel import lazy_gettext as _ + + + class User(self.Base): + TYPES = [ + (u'admin', _(u'Admin')), + (u'regular-user', _(u'Regular user')) + ] + + __tablename__ = 'user' + id = sa.Column(sa.Integer, primary_key=True) + name = sa.Column(sa.Unicode(255)) + type = sa.Column(ChoiceType(TYPES)) + + + user = User(type=u'admin') + user.type # Choice(type='admin', value=u'Admin') + + print user.type # u'Admin' + """ + impl = types.Unicode(255) def __init__(self, choices, impl=None): diff --git a/sqlalchemy_utils/types/color.py b/sqlalchemy_utils/types/color.py index 5fb4791..df97b97 100644 --- a/sqlalchemy_utils/types/color.py +++ b/sqlalchemy_utils/types/color.py @@ -12,8 +12,41 @@ except ImportError: class ColorType(types.TypeDecorator, ScalarCoercible): """ - Changes Color objects to a string representation on the way in and - changes them back to Color objects on the way out. + ColorType provides a way for saving Color (from colour_ package) objects + into database. ColorType saves Color objects as strings on the way in and + converts them back to objects when querying the database. + + :: + + + from colour import Color + from sqlalchemy_utils import ColorType + + + class Document(Base): + __tablename__ = 'document' + id = sa.Column(sa.Integer, autoincrement=True) + name = sa.Column(sa.Unicode(50)) + background_color = sa.Column(ColorType) + + + document = Document() + document.background_color = Color('#F5F5F5') + session.commit() + + + Querying the database returns Color objects: + + :: + + document = session.query(Document).first() + + document.background_color.hex + # '#f5f5f5' + + + For more information about colour package and Color object, see: + https://github.com/vaab/colour """ STORE_FORMAT = u'hex' impl = types.Unicode(20) diff --git a/sqlalchemy_utils/types/json.py b/sqlalchemy_utils/types/json.py index bcd2d65..7214d82 100644 --- a/sqlalchemy_utils/types/json.py +++ b/sqlalchemy_utils/types/json.py @@ -23,8 +23,32 @@ ischema_names['json'] = PostgresJSONType class JSONType(sa.types.TypeDecorator): - "Represents an immutable structure as a json-encoded string." + """ + JSONType offers way of saving JSON data structures to database. On + PostgreSQL the underlying implementation of this data type is 'json' while + on other databases its simply 'text'. + :: + + + from sqlalchemy_utils import JSONType + + + class Product(Base): + __tablename__ = 'product' + id = sa.Column(sa.Integer, autoincrement=True) + name = sa.Column(sa.Unicode(50)) + details = sa.Column(JSONType) + + + product = Product() + product.details = { + 'color': 'red', + 'type': 'car', + 'max-speed': '400 mph' + } + session.commit() + """ impl = sa.UnicodeText def __init__(self): diff --git a/sqlalchemy_utils/types/locale.py b/sqlalchemy_utils/types/locale.py index d2f5691..8346244 100644 --- a/sqlalchemy_utils/types/locale.py +++ b/sqlalchemy_utils/types/locale.py @@ -11,8 +11,40 @@ except ImportError: class LocaleType(types.TypeDecorator, ScalarCoercible): """ - Changes babel.Locale objects to a string representation on the way in and - changes them back to Locale objects on the way out. + LocaleType saves Babel_ Locale objects into database. The Locale objects + are converted to string on the way in and back to object on the way out. + + In order to use LocaleType you need to install Babel_ first. + + .. _Babel: http://babel.pocoo.org/ + + :: + + + from sqlalchemy_utils import LocaleType + from babel import Locale + + + class User(Base): + __tablename__ = 'user' + id = sa.Column(sa.Integer, autoincrement=True) + name = sa.Column(sa.Unicode(50)) + locale = sa.Column(LocaleType) + + + user = User() + user.locale = Locale('en_US') + session.commit() + + + Like many other types this type also supports scalar coercion: + + :: + + + user.locale = 'de_DE' + user.locale # Locale('de_DE') + """ impl = types.Unicode(10) diff --git a/sqlalchemy_utils/types/number_range.py b/sqlalchemy_utils/types/number_range.py index 2c68de1..cdfb648 100644 --- a/sqlalchemy_utils/types/number_range.py +++ b/sqlalchemy_utils/types/number_range.py @@ -12,6 +12,48 @@ class NumberRangeRawType(types.UserDefinedType): class NumberRangeType(types.TypeDecorator, ScalarCoercible): + """ + NumberRangeType provides way for saving range of numbers into database. + + Example :: + + + from sqlalchemy_utils import NumberRangeType, NumberRange + + + class Event(Base): + __tablename__ = 'user' + id = sa.Column(sa.Integer, autoincrement=True) + name = sa.Column(sa.Unicode(255)) + estimated_number_of_persons = sa.Column(NumberRangeType) + + + party = Event(name=u'party') + + # we estimate the party to contain minium of 10 persons and at max + # 100 persons + party.estimated_number_of_persons = NumberRange(10, 100) + + print party.estimated_number_of_persons + # '10-100' + + + NumberRange supports some arithmetic operators: + :: + + + meeting = Event(name=u'meeting') + + meeting.estimated_number_of_persons = NumberRange(20, 40) + + total = ( + meeting.estimated_number_of_persons + + party.estimated_number_of_persons + ) + print total + # '30-140' + """ + impl = NumberRangeRawType def process_bind_param(self, value, dialect): diff --git a/sqlalchemy_utils/types/scalar_list.py b/sqlalchemy_utils/types/scalar_list.py index b4f4a6a..41a2d80 100644 --- a/sqlalchemy_utils/types/scalar_list.py +++ b/sqlalchemy_utils/types/scalar_list.py @@ -8,6 +8,50 @@ class ScalarListException(Exception): class ScalarListType(types.TypeDecorator): + """ + ScalarListType type provides convenient way for saving multiple scalar + values in one column. ScalarListType works like list on python side and + saves the result as comma-separated list in the database (custom separators + can also be used). + + Example :: + + + from sqlalchemy_utils import ScalarListType + + + class User(Base): + __tablename__ = 'user' + id = sa.Column(sa.Integer, autoincrement=True) + hobbies = sa.Column(ScalarListType()) + + + user = User() + user.hobbies = [u'football', u'ice_hockey'] + session.commit() + + + You can easily set up integer lists too: + + :: + + + from sqlalchemy_utils import ScalarListType + + + class Player(Base): + __tablename__ = 'player' + id = sa.Column(sa.Integer, autoincrement=True) + points = sa.Column(ScalarListType(int)) + + + player = Player() + player.points = [11, 12, 8, 80] + session.commit() + + + """ + impl = sa.UnicodeText() def __init__(self, coerce_func=six.text_type, separator=u','): diff --git a/sqlalchemy_utils/types/timezone.py b/sqlalchemy_utils/types/timezone.py index 3c2dfda..43ebb47 100644 --- a/sqlalchemy_utils/types/timezone.py +++ b/sqlalchemy_utils/types/timezone.py @@ -6,8 +6,21 @@ from .scalar_coercible import ScalarCoercible class TimezoneType(types.TypeDecorator, ScalarCoercible): """ - Changes Timezone objects to a string representation on the way in and - changes them back to Timezone objects on the way out. + TimezoneType provides a way for saving timezones (from either the pytz or + the dateutil package) objects into database. TimezoneType saves timezone + objects as strings on the way in and converts them back to objects when + querying the database. + + + :: + + from sqlalchemy_utils import TimezoneType + + class User(Base): + __tablename__ = 'user' + + # Pass backend='pytz' to change it to use pytz (dateutil by default) + timezone = sa.Column(TimezoneType(backend='pytz')) """ impl = types.CHAR(50) diff --git a/sqlalchemy_utils/types/url.py b/sqlalchemy_utils/types/url.py index b5b7624..7dc5489 100644 --- a/sqlalchemy_utils/types/url.py +++ b/sqlalchemy_utils/types/url.py @@ -9,6 +9,34 @@ from .scalar_coercible import ScalarCoercible class URLType(types.TypeDecorator, ScalarCoercible): + """ + URLType stores furl_ objects into database. + + .. _furl: https://github.com/gruns/furl + + :: + + from sqlalchemy_utils import URLType + from furl import furl + + + class User(Base): + __tablename__ = 'user' + + id = sa.Column(sa.Integer, primary_key=True) + website = sa.Column(URLType) + + + user = User(website=u'www.example.com') + + # website is coerced to furl object, hence all nice furl operations come + # available + user.website.args['some_argument'] = '12' + + print user.website + # www.example.com?some_argument=12 + """ + impl = types.UnicodeText def process_bind_param(self, value, dialect): diff --git a/sqlalchemy_utils/types/uuid.py b/sqlalchemy_utils/types/uuid.py index 84542de..1ef201c 100644 --- a/sqlalchemy_utils/types/uuid.py +++ b/sqlalchemy_utils/types/uuid.py @@ -9,8 +9,18 @@ class UUIDType(types.TypeDecorator, ScalarCoercible): """ Stores a UUID in the database natively when it can and falls back to a BINARY(16) or a CHAR(32) when it can't. - """ + :: + + from sqlalchemy_utils import UUIDType + import uuid + + class User(Base): + __tablename__ = 'user' + + # Pass `binary=False` to fallback to CHAR instead of BINARY + id = sa.Column(UUIDType(binary=False), primary_key=True) + """ impl = types.BINARY(16) python_type = uuid.UUID