More docs added

This commit is contained in:
Konsta Vesterinen
2013-11-04 16:15:12 +02:00
parent d6504601e2
commit a95902f276

View File

@@ -1,21 +1,43 @@
""" """
SQLAlchemy-Utils provides way of automatically calculating aggregate values of related models and saving them to parent model. SQLAlchemy-Utils provides way of automatically calculating aggregate values of
related models and saving them to parent model.
This solution is inspired by RoR counter cache and especially counter_culture_. This solution is inspired by RoR counter cache,
`counter_culture`_ and `stackoverflow reply by Michael Bayer`_.
Why?
----
Many times you may have situations where you need to calculate dynamically some
aggregate value for given model. Some simple examples include:
- Number of products in a catalog
- Average rating for movie
- Latest forum post
- Total price of orders for given customer
Now all these aggregates can be elegantly implemented with SQLAlchemy
column_property_ function. However when your data grows calculating these
values on the fly might start to hurt the performance of your application. The
more aggregates you are using the more performance penalty you get.
This module provides way of calculating these values automatically and
efficiently at the time of modification rather than on the fly.
Features
--------
.. _counter_culter:: https://github.com/magnusvk/counter_culture * Automatically updates aggregate columns when aggregated values change
* Supports aggregate values through arbitrary number levels of relations
* Highly optimized: uses single query per transaction per aggregate column
* Aggregated columns can be of any data type and use any selectable scalar expression
Non-atomic implementation: .. _column_property: http://docs.sqlalchemy.org/en/latest/orm/mapper_config.html#using-column-property
.. _counter_culture: https://github.com/magnusvk/counter_culture
http://stackoverflow.com/questions/13693872/ .. _stackoverflow reply by Michael Bayer:
http://stackoverflow.com/questions/13693872/
We should avoid deadlocks:
http://mina.naguib.ca/blog/2010/11/22/postgresql-foreign-key-deadlocks.html
Simple aggregates Simple aggregates
@@ -84,11 +106,15 @@ Custom aggregate expressions
id = sa.Column(sa.Integer, primary_key=True) id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.Unicode(255)) name = sa.Column(sa.Unicode(255))
price = sa.Column(sa.Numeric) price = sa.Column(sa.Numeric)
monthly_license_price = sa.Column(sa.Numeric)
catalog_id = sa.Column(sa.Integer, sa.ForeignKey(Catalog.id)) catalog_id = sa.Column(sa.Integer, sa.ForeignKey(Catalog.id))
Now the net_worth column of Catalog model will be automatically whenever:
* A new product is added to the catalog
* A product is deleted from the catalog
* The price of catalog product is changed
:: ::
@@ -96,18 +122,95 @@ Custom aggregate expressions
from decimal import Decimal from decimal import Decimal
product1 = Product(name='Some product', price=Decimal(1000))
product2 = Product(name='Some other product', price=Decimal(500))
catalog = Catalog( catalog = Catalog(
name=u'My first catalog' name=u'My first catalog'
products=[ products=[
Product(name='Some product', price=Decimal(1000)), product1,
Product(name='Some other product', price=Decimal(500)) product2
] ]
) )
session.add(catalog) session.add(catalog)
session.commit() session.commit()
session.refresh(catalog)
catalog.net_worth # 1500 catalog.net_worth # 1500
session.delete(product2)
session.commit()
session.refresh(catalog)
catalog.net_worth # 1000
product1.price = 2000
session.commit()
session.refresh(catalog)
catalog.net_worth # 2000
Multi-level aggregates
----------------------
::
from sqlalchemy_utils import aggregated_attr
class Catalog(Base):
__tablename__ = 'catalog'
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.Unicode(255))
@aggregated_attr('categories.products')
def net_worth(self):
return sa.Column(sa.Integer)
@aggregated_attr.expression
def net_worth(self):
return sa.func.sum(Product.price)
categories = sa.orm.relationship('Product')
class Category(Base):
__tablename__ = 'category'
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.Unicode(255))
catalog_id = sa.Column(sa.Integer, sa.ForeignKey(Catalog.id))
products = sa.orm.relationship('Product')
class Product(Base):
__tablename__ = 'product'
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.Unicode(255))
price = sa.Column(sa.Numeric)
category_id = sa.Column(sa.Integer, sa.ForeignKey(Category.id))
TODO
----
* Special consideration should be given to `deadlocks`_.
.. _deadlocks:
http://mina.naguib.ca/blog/2010/11/22/postgresql-foreign-key-deadlocks.html
""" """