├── README.md ├── ch01.ipynb ├── ch02.ipynb ├── ch03.ipynb ├── ch03a.ipynb ├── ch03b.ipynb ├── ch04 ├── app.py ├── db.py ├── test_app.py └── test_mock_app.py ├── ch05 ├── .ipynb_checkpoints │ └── CH05-checkpoint.ipynb ├── CH05.ipynb └── Chinook_Sqlite.sqlite ├── ch06.ipynb ├── ch07.ipynb ├── ch08a.ipynb ├── ch08b.ipynb ├── ch09 ├── app.py ├── db.py ├── test_app.py └── test_mock_app.py ├── ch10 ├── .ipynb_checkpoints │ └── ch10-checkpoint.ipynb ├── Chinook_Sqlite.sqlite └── ch10.ipynb ├── ch11 ├── alembic.ini ├── alembic │ ├── README │ ├── __init__.py │ ├── env.py │ ├── script.py.mako │ └── versions │ │ ├── 2e6a6cc63e9_renaming_cookies_to_new_cookies.py │ │ ├── 34044511331_added_cookie_model.py │ │ └── 8a8a9d067_empty_init.py ├── alembictest.db ├── app │ ├── __init__.py │ └── db.py └── migration.sql ├── ch14 ├── Association Proxy - Part 1.ipynb ├── Association Proxy - Part 2.ipynb ├── Chinook_Sqlite.sqlite ├── Hybrid Properties.ipynb └── db.py └── requirements.txt /README.md: -------------------------------------------------------------------------------- 1 | Essential SQLAlchemy, 2e 2 | ========== 3 | 4 | This is the example code that accompanies Essential SQLAlchemy, 2e by Jason Myers and Rick Copeland (9781491916469). 5 | 6 | Click the Download Zip button to the right to download example code. 7 | 8 | Visit the catalog page [here](http://shop.oreilly.com/product/0636920035800.do). 9 | 10 | See an error? Report it [here](http://oreilly.com/catalog/errata.csp?isbn=0636920035800), or simply fork and send us a pull request. 11 | -------------------------------------------------------------------------------- /ch01.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": false 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from sqlalchemy import MetaData\n", 12 | "metadata = MetaData()" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": 2, 18 | "metadata": { 19 | "collapsed": false 20 | }, 21 | "outputs": [], 22 | "source": [ 23 | "from datetime import datetime" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 3, 29 | "metadata": { 30 | "collapsed": false 31 | }, 32 | "outputs": [], 33 | "source": [ 34 | "from sqlalchemy import Table, Column, Integer, Numeric, String, ForeignKey, DateTime" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": 4, 40 | "metadata": { 41 | "collapsed": false 42 | }, 43 | "outputs": [], 44 | "source": [ 45 | "from sqlalchemy import create_engine" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": 5, 51 | "metadata": { 52 | "collapsed": false 53 | }, 54 | "outputs": [], 55 | "source": [ 56 | "engine = create_engine('sqlite:///:memory:')" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": 6, 62 | "metadata": { 63 | "collapsed": false 64 | }, 65 | "outputs": [], 66 | "source": [ 67 | "cookies = Table('cookies', metadata,\n", 68 | " Column('cookie_id', Integer(), primary_key=True),\n", 69 | " Column('cookie_name', String(50), index=True),\n", 70 | " Column('cookie_recipe_url', String(255)),\n", 71 | " Column('cookie_sku', String(55)),\n", 72 | " Column('quantity', Integer()),\n", 73 | " Column('unit_cost', Numeric(12, 2))\n", 74 | ")" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": 7, 80 | "metadata": { 81 | "collapsed": false 82 | }, 83 | "outputs": [], 84 | "source": [ 85 | "users = Table('users', metadata,\n", 86 | " Column('user_id', Integer(), primary_key=True),\n", 87 | " Column('username', String(15), nullable=False, unique=True),\n", 88 | " Column('email_address', String(255), nullable=False),\n", 89 | " Column('phone', String(20), nullable=False),\n", 90 | " Column('password', String(25), nullable=False),\n", 91 | " Column('created_on', DateTime(), default=datetime.now),\n", 92 | " Column('updated_on', DateTime(), default=datetime.now, onupdate=datetime.now)\n", 93 | ")" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": 8, 99 | "metadata": { 100 | "collapsed": false 101 | }, 102 | "outputs": [], 103 | "source": [ 104 | "orders = Table('orders', metadata,\n", 105 | " Column('order_id', Integer(), primary_key=True),\n", 106 | " Column('user_id', ForeignKey('users.user_id')),\n", 107 | ")\n", 108 | "\n", 109 | "line_items = Table('line_items', metadata,\n", 110 | " Column('line_items_id', Integer(), primary_key=True),\n", 111 | " Column('order_id', ForeignKey('orders.order_id')),\n", 112 | " Column('cookie_id', ForeignKey('cookies.cookie_id')),\n", 113 | " Column('quantity', Integer()),\n", 114 | " Column('extended_cost', Numeric(12, 2))\n", 115 | ")" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": 9, 121 | "metadata": { 122 | "collapsed": false 123 | }, 124 | "outputs": [], 125 | "source": [ 126 | "metadata.create_all(engine)" 127 | ] 128 | } 129 | ], 130 | "metadata": { 131 | "kernelspec": { 132 | "display_name": "Python 2", 133 | "language": "python", 134 | "name": "python2" 135 | }, 136 | "language_info": { 137 | "codemirror_mode": { 138 | "name": "ipython", 139 | "version": 2 140 | }, 141 | "file_extension": ".py", 142 | "mimetype": "text/x-python", 143 | "name": "python", 144 | "nbconvert_exporter": "python", 145 | "pygments_lexer": "ipython2", 146 | "version": "2.7.10" 147 | } 148 | }, 149 | "nbformat": 4, 150 | "nbformat_minor": 0 151 | } 152 | -------------------------------------------------------------------------------- /ch03a.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 6, 6 | "metadata": { 7 | "collapsed": false 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from datetime import datetime\n", 12 | "\n", 13 | "from sqlalchemy import (MetaData, Table, Column, Integer, Numeric, String,\n", 14 | " DateTime, ForeignKey, Boolean, create_engine, CheckConstraint)\n", 15 | "metadata = MetaData()\n", 16 | "\n", 17 | "cookies = Table('cookies', metadata,\n", 18 | " Column('cookie_id', Integer(), primary_key=True),\n", 19 | " Column('cookie_name', String(50), index=True),\n", 20 | " Column('cookie_recipe_url', String(255)),\n", 21 | " Column('cookie_sku', String(55)),\n", 22 | " Column('quantity', Integer()),\n", 23 | " Column('unit_cost', Numeric(12, 2)),\n", 24 | " CheckConstraint('quantity > 0', name='quantity_positive')\n", 25 | ")\n", 26 | "\n", 27 | "users = Table('users', metadata,\n", 28 | " Column('user_id', Integer(), primary_key=True),\n", 29 | " Column('username', String(15), nullable=False, unique=True),\n", 30 | " Column('email_address', String(255), nullable=False),\n", 31 | " Column('phone', String(20), nullable=False),\n", 32 | " Column('password', String(25), nullable=False),\n", 33 | " Column('created_on', DateTime(), default=datetime.now),\n", 34 | " Column('updated_on', DateTime(), default=datetime.now, onupdate=datetime.now)\n", 35 | ")\n", 36 | "\n", 37 | "orders = Table('orders', metadata,\n", 38 | " Column('order_id', Integer()),\n", 39 | " Column('user_id', ForeignKey('users.user_id')),\n", 40 | " Column('shipped', Boolean(), default=False)\n", 41 | ")\n", 42 | "\n", 43 | "line_items = Table('line_items', metadata,\n", 44 | " Column('line_items_id', Integer(), primary_key=True),\n", 45 | " Column('order_id', ForeignKey('orders.order_id')),\n", 46 | " Column('cookie_id', ForeignKey('cookies.cookie_id')),\n", 47 | " Column('quantity', Integer()),\n", 48 | " Column('extended_cost', Numeric(12, 2))\n", 49 | ")\n", 50 | "\n", 51 | "engine = create_engine('sqlite:///:memory:')\n", 52 | "metadata.create_all(engine)\n", 53 | "connection = engine.connect()" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 7, 59 | "metadata": { 60 | "collapsed": false 61 | }, 62 | "outputs": [], 63 | "source": [ 64 | "from sqlalchemy import select, insert\n", 65 | "ins = insert(users).values(\n", 66 | " username=\"cookiemon\",\n", 67 | " email_address=\"mon@cookie.com\",\n", 68 | " phone=\"111-111-1111\",\n", 69 | " password=\"password\"\n", 70 | ")\n", 71 | "result = connection.execute(ins)" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 8, 77 | "metadata": { 78 | "collapsed": false 79 | }, 80 | "outputs": [ 81 | { 82 | "name": "stdout", 83 | "output_type": "stream", 84 | "text": [ 85 | "cookiemon\n" 86 | ] 87 | }, 88 | { 89 | "ename": "AttributeError", 90 | "evalue": "Could not locate column in row for column 'password'", 91 | "output_type": "error", 92 | "traceback": [ 93 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 94 | "\u001b[0;31mAttributeError\u001b[0m Traceback (most recent call last)", 95 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mresult\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mresults\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;32mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0musername\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0;32mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpassword\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 96 | "\u001b[0;31mAttributeError\u001b[0m: Could not locate column in row for column 'password'" 97 | ] 98 | } 99 | ], 100 | "source": [ 101 | "s = select([users.c.username])\n", 102 | "results = connection.execute(s)\n", 103 | "for result in results:\n", 104 | " print(result.username)\n", 105 | " print(result.password)" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": 4, 111 | "metadata": { 112 | "collapsed": false 113 | }, 114 | "outputs": [ 115 | { 116 | "data": { 117 | "text/plain": [ 118 | "[(u'cookiemon',)]" 119 | ] 120 | }, 121 | "execution_count": 4, 122 | "metadata": {}, 123 | "output_type": "execute_result" 124 | } 125 | ], 126 | "source": [ 127 | "s = select([users.c.username])\n", 128 | "connection.execute(s).fetchall()" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": 5, 134 | "metadata": { 135 | "collapsed": false 136 | }, 137 | "outputs": [ 138 | { 139 | "ename": "IntegrityError", 140 | "evalue": "(sqlite3.IntegrityError) UNIQUE constraint failed: users.username [SQL: u'INSERT INTO users (username, email_address, phone, password, created_on, updated_on) VALUES (?, ?, ?, ?, ?, ?)'] [parameters: ('cookiemon', 'damon@cookie.com', '111-111-1111', 'password', '2015-06-27 22:08:54.699258', '2015-06-27 22:08:54.699266')]", 141 | "output_type": "error", 142 | "traceback": [ 143 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 144 | "\u001b[0;31mIntegrityError\u001b[0m Traceback (most recent call last)", 145 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mpassword\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"password\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 6\u001b[0m )\n\u001b[0;32m----> 7\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mconnection\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mins\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 146 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc\u001b[0m in \u001b[0;36mexecute\u001b[0;34m(self, object, *multiparams, **params)\u001b[0m\n\u001b[1;32m 912\u001b[0m type(object))\n\u001b[1;32m 913\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 914\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mmeth\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmultiparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 915\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 916\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_execute_function\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmultiparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 147 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book/lib/python2.7/site-packages/sqlalchemy/sql/elements.pyc\u001b[0m in \u001b[0;36m_execute_on_connection\u001b[0;34m(self, connection, multiparams, params)\u001b[0m\n\u001b[1;32m 321\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 322\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_execute_on_connection\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconnection\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmultiparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 323\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mconnection\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_execute_clauseelement\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmultiparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 324\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 325\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0munique_params\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0moptionaldict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 148 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc\u001b[0m in \u001b[0;36m_execute_clauseelement\u001b[0;34m(self, elem, multiparams, params)\u001b[0m\n\u001b[1;32m 1008\u001b[0m \u001b[0mcompiled_sql\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1009\u001b[0m \u001b[0mdistilled_params\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1010\u001b[0;31m \u001b[0mcompiled_sql\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdistilled_params\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1011\u001b[0m )\n\u001b[1;32m 1012\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_has_events\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mengine\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_has_events\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 149 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc\u001b[0m in \u001b[0;36m_execute_context\u001b[0;34m(self, dialect, constructor, statement, parameters, *args)\u001b[0m\n\u001b[1;32m 1144\u001b[0m \u001b[0mparameters\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1145\u001b[0m \u001b[0mcursor\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1146\u001b[0;31m context)\n\u001b[0m\u001b[1;32m 1147\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1148\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_has_events\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mengine\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_has_events\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 150 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc\u001b[0m in \u001b[0;36m_handle_dbapi_exception\u001b[0;34m(self, e, statement, parameters, cursor, context)\u001b[0m\n\u001b[1;32m 1339\u001b[0m util.raise_from_cause(\n\u001b[1;32m 1340\u001b[0m \u001b[0msqlalchemy_exception\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1341\u001b[0;31m \u001b[0mexc_info\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1342\u001b[0m )\n\u001b[1;32m 1343\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 151 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book/lib/python2.7/site-packages/sqlalchemy/util/compat.pyc\u001b[0m in \u001b[0;36mraise_from_cause\u001b[0;34m(exception, exc_info)\u001b[0m\n\u001b[1;32m 197\u001b[0m \u001b[0mexc_info\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msys\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexc_info\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 198\u001b[0m \u001b[0mexc_type\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mexc_value\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mexc_tb\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mexc_info\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 199\u001b[0;31m \u001b[0mreraise\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtype\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mexception\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mexception\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtb\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mexc_tb\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 200\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 201\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mpy3k\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 152 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc\u001b[0m in \u001b[0;36m_execute_context\u001b[0;34m(self, dialect, constructor, statement, parameters, *args)\u001b[0m\n\u001b[1;32m 1137\u001b[0m \u001b[0mstatement\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1138\u001b[0m \u001b[0mparameters\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1139\u001b[0;31m context)\n\u001b[0m\u001b[1;32m 1140\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mException\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1141\u001b[0m self._handle_dbapi_exception(\n", 153 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book/lib/python2.7/site-packages/sqlalchemy/engine/default.pyc\u001b[0m in \u001b[0;36mdo_execute\u001b[0;34m(self, cursor, statement, parameters, context)\u001b[0m\n\u001b[1;32m 448\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 449\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdo_execute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcursor\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstatement\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparameters\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 450\u001b[0;31m \u001b[0mcursor\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstatement\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparameters\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 451\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 452\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdo_execute_no_params\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcursor\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstatement\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 154 | "\u001b[0;31mIntegrityError\u001b[0m: (sqlite3.IntegrityError) UNIQUE constraint failed: users.username [SQL: u'INSERT INTO users (username, email_address, phone, password, created_on, updated_on) VALUES (?, ?, ?, ?, ?, ?)'] [parameters: ('cookiemon', 'damon@cookie.com', '111-111-1111', 'password', '2015-06-27 22:08:54.699258', '2015-06-27 22:08:54.699266')]" 155 | ] 156 | } 157 | ], 158 | "source": [ 159 | "ins = insert(users).values(\n", 160 | " username=\"cookiemon\",\n", 161 | " email_address=\"damon@cookie.com\",\n", 162 | " phone=\"111-111-1111\",\n", 163 | " password=\"password\"\n", 164 | ")\n", 165 | "result = connection.execute(ins)" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": 6, 171 | "metadata": { 172 | "collapsed": false 173 | }, 174 | "outputs": [ 175 | { 176 | "name": "stdout", 177 | "output_type": "stream", 178 | "text": [ 179 | "('UNIQUE constraint failed: users.username', ('cookiemon', 'damon@cookie.com', '111-111-1111', 'password', '2015-04-26 15:42:45.862618', '2015-04-26 15:42:45.862627'))\n" 180 | ] 181 | } 182 | ], 183 | "source": [ 184 | "from sqlalchemy.exc import IntegrityError\n", 185 | "ins = insert(users).values(\n", 186 | " username=\"cookiemon\",\n", 187 | " email_address=\"damon@cookie.com\",\n", 188 | " phone=\"111-111-1111\",\n", 189 | " password=\"password\"\n", 190 | ")\n", 191 | "try:\n", 192 | " result = connection.execute(ins)\n", 193 | "except IntegrityError as error:\n", 194 | " print(error.orig.message, error.params)" 195 | ] 196 | }, 197 | { 198 | "cell_type": "code", 199 | "execution_count": 7, 200 | "metadata": { 201 | "collapsed": false 202 | }, 203 | "outputs": [], 204 | "source": [ 205 | "ins = cookies.insert()\n", 206 | "inventory_list = [\n", 207 | " {\n", 208 | " 'cookie_name': 'chocolate chip',\n", 209 | " 'cookie_recipe_url': 'http://some.aweso.me/cookie/recipe.html',\n", 210 | " 'cookie_sku': 'CC01',\n", 211 | " 'quantity': '12',\n", 212 | " 'unit_cost': '0.50'\n", 213 | " },\n", 214 | " {\n", 215 | " 'cookie_name': 'dark chocolate chip',\n", 216 | " 'cookie_recipe_url': 'http://some.aweso.me/cookie/recipe_dark.html',\n", 217 | " 'cookie_sku': 'CC02',\n", 218 | " 'quantity': '1',\n", 219 | " 'unit_cost': '0.75'\n", 220 | " },\n", 221 | " {\n", 222 | " 'cookie_name': 'peanut butter',\n", 223 | " 'cookie_recipe_url': 'http://some.aweso.me/cookie/peanut.html',\n", 224 | " 'cookie_sku': 'PB01',\n", 225 | " 'quantity': '24',\n", 226 | " 'unit_cost': '0.25'\n", 227 | " },\n", 228 | " {\n", 229 | " 'cookie_name': 'oatmeal raisin',\n", 230 | " 'cookie_recipe_url': 'http://some.okay.me/cookie/raisin.html',\n", 231 | " 'cookie_sku': 'EWW01',\n", 232 | " 'quantity': '100',\n", 233 | " 'unit_cost': '1.00'\n", 234 | " }\n", 235 | "]\n", 236 | "result = connection.execute(ins, inventory_list)" 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": 8, 242 | "metadata": { 243 | "collapsed": false 244 | }, 245 | "outputs": [ 246 | { 247 | "data": { 248 | "text/plain": [ 249 | "4" 250 | ] 251 | }, 252 | "execution_count": 8, 253 | "metadata": {}, 254 | "output_type": "execute_result" 255 | } 256 | ], 257 | "source": [ 258 | "result.rowcount" 259 | ] 260 | }, 261 | { 262 | "cell_type": "code", 263 | "execution_count": null, 264 | "metadata": { 265 | "collapsed": true 266 | }, 267 | "outputs": [], 268 | "source": [] 269 | } 270 | ], 271 | "metadata": { 272 | "kernelspec": { 273 | "display_name": "Python 2", 274 | "language": "python", 275 | "name": "python2" 276 | }, 277 | "language_info": { 278 | "codemirror_mode": { 279 | "name": "ipython", 280 | "version": 2 281 | }, 282 | "file_extension": ".py", 283 | "mimetype": "text/x-python", 284 | "name": "python", 285 | "nbconvert_exporter": "python", 286 | "pygments_lexer": "ipython2", 287 | "version": "2.7.10" 288 | } 289 | }, 290 | "nbformat": 4, 291 | "nbformat_minor": 0 292 | } 293 | -------------------------------------------------------------------------------- /ch03b.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": false 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from datetime import datetime\n", 12 | "\n", 13 | "from sqlalchemy import (MetaData, Table, Column, Integer, Numeric, String,\n", 14 | " DateTime, ForeignKey, Boolean, create_engine, CheckConstraint)\n", 15 | "metadata = MetaData()\n", 16 | "\n", 17 | "cookies = Table('cookies', metadata,\n", 18 | " Column('cookie_id', Integer(), primary_key=True),\n", 19 | " Column('cookie_name', String(50), index=True),\n", 20 | " Column('cookie_recipe_url', String(255)),\n", 21 | " Column('cookie_sku', String(55)),\n", 22 | " Column('quantity', Integer()),\n", 23 | " Column('unit_cost', Numeric(12, 2)),\n", 24 | " CheckConstraint('quantity >= 0', name='quantity_positive')\n", 25 | ")\n", 26 | "\n", 27 | "users = Table('users', metadata,\n", 28 | " Column('user_id', Integer(), primary_key=True),\n", 29 | " Column('username', String(15), nullable=False, unique=True),\n", 30 | " Column('email_address', String(255), nullable=False),\n", 31 | " Column('phone', String(20), nullable=False),\n", 32 | " Column('password', String(25), nullable=False),\n", 33 | " Column('created_on', DateTime(), default=datetime.now),\n", 34 | " Column('updated_on', DateTime(), default=datetime.now, onupdate=datetime.now)\n", 35 | ")\n", 36 | "\n", 37 | "orders = Table('orders', metadata,\n", 38 | " Column('order_id', Integer()),\n", 39 | " Column('user_id', ForeignKey('users.user_id')),\n", 40 | " Column('shipped', Boolean(), default=False)\n", 41 | ")\n", 42 | "\n", 43 | "line_items = Table('line_items', metadata,\n", 44 | " Column('line_items_id', Integer(), primary_key=True),\n", 45 | " Column('order_id', ForeignKey('orders.order_id')),\n", 46 | " Column('cookie_id', ForeignKey('cookies.cookie_id')),\n", 47 | " Column('quantity', Integer()),\n", 48 | " Column('extended_cost', Numeric(12, 2))\n", 49 | ")\n", 50 | "\n", 51 | "engine = create_engine('sqlite:///:memory:')\n", 52 | "metadata.create_all(engine)\n", 53 | "connection = engine.connect()" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 2, 59 | "metadata": { 60 | "collapsed": false 61 | }, 62 | "outputs": [], 63 | "source": [ 64 | "from sqlalchemy import select, insert, update\n", 65 | "ins = insert(users).values(\n", 66 | " username=\"cookiemon\",\n", 67 | " email_address=\"mon@cookie.com\",\n", 68 | " phone=\"111-111-1111\",\n", 69 | " password=\"password\"\n", 70 | ")\n", 71 | "result = connection.execute(ins)" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 3, 77 | "metadata": { 78 | "collapsed": false 79 | }, 80 | "outputs": [], 81 | "source": [ 82 | "ins = cookies.insert()\n", 83 | "inventory_list = [\n", 84 | " {\n", 85 | " 'cookie_name': 'chocolate chip',\n", 86 | " 'cookie_recipe_url': 'http://some.aweso.me/cookie/recipe.html',\n", 87 | " 'cookie_sku': 'CC01',\n", 88 | " 'quantity': '12',\n", 89 | " 'unit_cost': '0.50'\n", 90 | " },\n", 91 | " {\n", 92 | " 'cookie_name': 'dark chocolate chip',\n", 93 | " 'cookie_recipe_url': 'http://some.aweso.me/cookie/recipe_dark.html',\n", 94 | " 'cookie_sku': 'CC02',\n", 95 | " 'quantity': '1',\n", 96 | " 'unit_cost': '0.75'\n", 97 | " }\n", 98 | "]\n", 99 | "result = connection.execute(ins, inventory_list)" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": 4, 105 | "metadata": { 106 | "collapsed": false 107 | }, 108 | "outputs": [], 109 | "source": [ 110 | "ins = insert(orders).values(user_id=1, order_id=1)\n", 111 | "result = connection.execute(ins)\n", 112 | "ins = insert(line_items)\n", 113 | "order_items = [\n", 114 | " {\n", 115 | " 'order_id': 1,\n", 116 | " 'cookie_id': 1,\n", 117 | " 'quantity': 9,\n", 118 | " 'extended_cost': 4.50\n", 119 | " }\n", 120 | "]\n", 121 | "result = connection.execute(ins, order_items)" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": 5, 127 | "metadata": { 128 | "collapsed": false 129 | }, 130 | "outputs": [], 131 | "source": [ 132 | "ins = insert(orders).values(user_id=1, order_id=2)\n", 133 | "result = connection.execute(ins)\n", 134 | "ins = insert(line_items)\n", 135 | "order_items = [\n", 136 | " {\n", 137 | " 'order_id': 2,\n", 138 | " 'cookie_id': 2,\n", 139 | " 'quantity': 1,\n", 140 | " 'extended_cost': 1.50\n", 141 | " },\n", 142 | " {\n", 143 | " 'order_id': 2,\n", 144 | " 'cookie_id': 1,\n", 145 | " 'quantity': 4,\n", 146 | " 'extended_cost': 4.50\n", 147 | " }\n", 148 | "]\n", 149 | "result = connection.execute(ins, order_items)" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": 6, 155 | "metadata": { 156 | "collapsed": false 157 | }, 158 | "outputs": [], 159 | "source": [ 160 | "def ship_it(order_id):\n", 161 | "\n", 162 | " s = select([line_items.c.cookie_id, line_items.c.quantity])\n", 163 | " s = s.where(line_items.c.order_id == order_id)\n", 164 | " cookies_to_ship = connection.execute(s)\n", 165 | " for cookie in cookies_to_ship:\n", 166 | " u = update(cookies).where(cookies.c.cookie_id == cookie.cookie_id)\n", 167 | " u = u.values(quantity = cookies.c.quantity - cookie.quantity)\n", 168 | " result = connection.execute(u)\n", 169 | " u = update(orders).where(orders.c.order_id == order_id)\n", 170 | " u = u.values(shipped=True)\n", 171 | " result = connection.execute(u)\n", 172 | " print(\"Shipped order ID: {}\".format(order_id))" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": 7, 178 | "metadata": { 179 | "collapsed": false 180 | }, 181 | "outputs": [ 182 | { 183 | "name": "stdout", 184 | "output_type": "stream", 185 | "text": [ 186 | "Shipped order ID: 1\n" 187 | ] 188 | } 189 | ], 190 | "source": [ 191 | "ship_it(1)" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": 8, 197 | "metadata": { 198 | "collapsed": false 199 | }, 200 | "outputs": [ 201 | { 202 | "data": { 203 | "text/plain": [ 204 | "[(u'chocolate chip', 3), (u'dark chocolate chip', 1)]" 205 | ] 206 | }, 207 | "execution_count": 8, 208 | "metadata": {}, 209 | "output_type": "execute_result" 210 | } 211 | ], 212 | "source": [ 213 | "s = select([cookies.c.cookie_name, cookies.c.quantity])\n", 214 | "connection.execute(s).fetchall()" 215 | ] 216 | }, 217 | { 218 | "cell_type": "code", 219 | "execution_count": 9, 220 | "metadata": { 221 | "collapsed": false 222 | }, 223 | "outputs": [ 224 | { 225 | "ename": "IntegrityError", 226 | "evalue": "(sqlite3.IntegrityError) CHECK constraint failed: quantity_positive [SQL: u'UPDATE cookies SET quantity=(cookies.quantity - ?) WHERE cookies.cookie_id = ?'] [parameters: (4, 1)]", 227 | "output_type": "error", 228 | "traceback": [ 229 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 230 | "\u001b[0;31mIntegrityError\u001b[0m Traceback (most recent call last)", 231 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mship_it\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 232 | "\u001b[0;32m\u001b[0m in \u001b[0;36mship_it\u001b[0;34m(order_id)\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0mu\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcookies\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwhere\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcookies\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcookie_id\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0mcookie\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcookie_id\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 8\u001b[0m \u001b[0mu\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mu\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalues\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mquantity\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcookies\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquantity\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0mcookie\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquantity\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 9\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mconnection\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mu\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 10\u001b[0m \u001b[0mu\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0morders\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwhere\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0morders\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0morder_id\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0morder_id\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 11\u001b[0m \u001b[0mu\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mu\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalues\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mshipped\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 233 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc\u001b[0m in \u001b[0;36mexecute\u001b[0;34m(self, object, *multiparams, **params)\u001b[0m\n\u001b[1;32m 912\u001b[0m type(object))\n\u001b[1;32m 913\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 914\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mmeth\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmultiparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 915\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 916\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_execute_function\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmultiparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 234 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book/lib/python2.7/site-packages/sqlalchemy/sql/elements.pyc\u001b[0m in \u001b[0;36m_execute_on_connection\u001b[0;34m(self, connection, multiparams, params)\u001b[0m\n\u001b[1;32m 321\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 322\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_execute_on_connection\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconnection\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmultiparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 323\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mconnection\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_execute_clauseelement\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmultiparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 324\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 325\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0munique_params\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0moptionaldict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 235 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc\u001b[0m in \u001b[0;36m_execute_clauseelement\u001b[0;34m(self, elem, multiparams, params)\u001b[0m\n\u001b[1;32m 1008\u001b[0m \u001b[0mcompiled_sql\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1009\u001b[0m \u001b[0mdistilled_params\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1010\u001b[0;31m \u001b[0mcompiled_sql\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdistilled_params\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1011\u001b[0m )\n\u001b[1;32m 1012\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_has_events\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mengine\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_has_events\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 236 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc\u001b[0m in \u001b[0;36m_execute_context\u001b[0;34m(self, dialect, constructor, statement, parameters, *args)\u001b[0m\n\u001b[1;32m 1144\u001b[0m \u001b[0mparameters\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1145\u001b[0m \u001b[0mcursor\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1146\u001b[0;31m context)\n\u001b[0m\u001b[1;32m 1147\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1148\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_has_events\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mengine\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_has_events\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 237 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc\u001b[0m in \u001b[0;36m_handle_dbapi_exception\u001b[0;34m(self, e, statement, parameters, cursor, context)\u001b[0m\n\u001b[1;32m 1339\u001b[0m util.raise_from_cause(\n\u001b[1;32m 1340\u001b[0m \u001b[0msqlalchemy_exception\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1341\u001b[0;31m \u001b[0mexc_info\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1342\u001b[0m )\n\u001b[1;32m 1343\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 238 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book/lib/python2.7/site-packages/sqlalchemy/util/compat.pyc\u001b[0m in \u001b[0;36mraise_from_cause\u001b[0;34m(exception, exc_info)\u001b[0m\n\u001b[1;32m 197\u001b[0m \u001b[0mexc_info\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msys\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexc_info\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 198\u001b[0m \u001b[0mexc_type\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mexc_value\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mexc_tb\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mexc_info\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 199\u001b[0;31m \u001b[0mreraise\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtype\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mexception\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mexception\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtb\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mexc_tb\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 200\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 201\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mpy3k\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 239 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc\u001b[0m in \u001b[0;36m_execute_context\u001b[0;34m(self, dialect, constructor, statement, parameters, *args)\u001b[0m\n\u001b[1;32m 1137\u001b[0m \u001b[0mstatement\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1138\u001b[0m \u001b[0mparameters\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1139\u001b[0;31m context)\n\u001b[0m\u001b[1;32m 1140\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mException\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1141\u001b[0m self._handle_dbapi_exception(\n", 240 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book/lib/python2.7/site-packages/sqlalchemy/engine/default.pyc\u001b[0m in \u001b[0;36mdo_execute\u001b[0;34m(self, cursor, statement, parameters, context)\u001b[0m\n\u001b[1;32m 448\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 449\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdo_execute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcursor\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstatement\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparameters\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 450\u001b[0;31m \u001b[0mcursor\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstatement\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparameters\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 451\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 452\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdo_execute_no_params\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcursor\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstatement\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 241 | "\u001b[0;31mIntegrityError\u001b[0m: (sqlite3.IntegrityError) CHECK constraint failed: quantity_positive [SQL: u'UPDATE cookies SET quantity=(cookies.quantity - ?) WHERE cookies.cookie_id = ?'] [parameters: (4, 1)]" 242 | ] 243 | } 244 | ], 245 | "source": [ 246 | "ship_it(2)" 247 | ] 248 | }, 249 | { 250 | "cell_type": "code", 251 | "execution_count": 10, 252 | "metadata": { 253 | "collapsed": false 254 | }, 255 | "outputs": [ 256 | { 257 | "data": { 258 | "text/plain": [ 259 | "[(u'chocolate chip', 3), (u'dark chocolate chip', 0)]" 260 | ] 261 | }, 262 | "execution_count": 10, 263 | "metadata": {}, 264 | "output_type": "execute_result" 265 | } 266 | ], 267 | "source": [ 268 | "s = select([cookies.c.cookie_name, cookies.c.quantity])\n", 269 | "connection.execute(s).fetchall()" 270 | ] 271 | }, 272 | { 273 | "cell_type": "code", 274 | "execution_count": 15, 275 | "metadata": { 276 | "collapsed": true 277 | }, 278 | "outputs": [], 279 | "source": [ 280 | "u = update(cookies).where(cookies.c.cookie_name == \"dark chocolate chip\")\n", 281 | "u = u.values(quantity = 1)\n", 282 | "result = connection.execute(u)" 283 | ] 284 | }, 285 | { 286 | "cell_type": "code", 287 | "execution_count": 19, 288 | "metadata": { 289 | "collapsed": true 290 | }, 291 | "outputs": [], 292 | "source": [ 293 | "from sqlalchemy.exc import IntegrityError\n", 294 | "def ship_it(order_id):\n", 295 | " s = select([line_items.c.cookie_id, line_items.c.quantity])\n", 296 | " s = s.where(line_items.c.order_id == order_id)\n", 297 | " transaction = connection.begin()\n", 298 | " cookies_to_ship = connection.execute(s).fetchall()\n", 299 | " try:\n", 300 | " for cookie in cookies_to_ship:\n", 301 | " u = update(cookies).where(cookies.c.cookie_id == cookie.cookie_id)\n", 302 | " u = u.values(quantity = cookies.c.quantity-cookie.quantity)\n", 303 | " result = connection.execute(u)\n", 304 | " u = update(orders).where(orders.c.order_id == order_id)\n", 305 | " u = u.values(shipped=True)\n", 306 | " result = connection.execute(u)\n", 307 | " print(\"Shipped order ID: {}\".format(order_id))\n", 308 | " transaction.commit()\n", 309 | " except IntegrityError as error:\n", 310 | " transaction.rollback()\n", 311 | " print(error)" 312 | ] 313 | }, 314 | { 315 | "cell_type": "code", 316 | "execution_count": 20, 317 | "metadata": { 318 | "collapsed": false 319 | }, 320 | "outputs": [ 321 | { 322 | "name": "stdout", 323 | "output_type": "stream", 324 | "text": [ 325 | "(sqlite3.IntegrityError) CHECK constraint failed: quantity_positive [SQL: u'UPDATE cookies SET quantity=(cookies.quantity - ?) WHERE cookies.cookie_id = ?'] [parameters: (4, 1)]\n" 326 | ] 327 | } 328 | ], 329 | "source": [ 330 | "ship_it(2)" 331 | ] 332 | }, 333 | { 334 | "cell_type": "code", 335 | "execution_count": 21, 336 | "metadata": { 337 | "collapsed": false 338 | }, 339 | "outputs": [ 340 | { 341 | "data": { 342 | "text/plain": [ 343 | "[(u'chocolate chip', 3), (u'dark chocolate chip', 1)]" 344 | ] 345 | }, 346 | "execution_count": 21, 347 | "metadata": {}, 348 | "output_type": "execute_result" 349 | } 350 | ], 351 | "source": [ 352 | "s = select([cookies.c.cookie_name, cookies.c.quantity])\n", 353 | "connection.execute(s).fetchall()" 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": null, 359 | "metadata": { 360 | "collapsed": true 361 | }, 362 | "outputs": [], 363 | "source": [] 364 | } 365 | ], 366 | "metadata": { 367 | "kernelspec": { 368 | "display_name": "Python 2", 369 | "language": "python", 370 | "name": "python2" 371 | }, 372 | "language_info": { 373 | "codemirror_mode": { 374 | "name": "ipython", 375 | "version": 2 376 | }, 377 | "file_extension": ".py", 378 | "mimetype": "text/x-python", 379 | "name": "python", 380 | "nbconvert_exporter": "python", 381 | "pygments_lexer": "ipython2", 382 | "version": "2.7.10" 383 | } 384 | }, 385 | "nbformat": 4, 386 | "nbformat_minor": 0 387 | } 388 | -------------------------------------------------------------------------------- /ch04/app.py: -------------------------------------------------------------------------------- 1 | from db import dal 2 | from sqlalchemy.sql import select 3 | 4 | 5 | def get_orders_by_customer(cust_name, shipped=None, details=False): 6 | columns = [dal.orders.c.order_id, dal.users.c.username, dal.users.c.phone] 7 | joins = dal.users.join(dal.orders) 8 | if details: 9 | columns.extend([dal.cookies.c.cookie_name, 10 | dal.line_items.c.quantity, 11 | dal.line_items.c.extended_cost]) 12 | joins = joins.join(dal.line_items).join(dal.cookies) 13 | cust_orders = select(columns) 14 | cust_orders = cust_orders.select_from(joins).where( 15 | dal.users.c.username == cust_name) 16 | if shipped is not None: 17 | cust_orders = cust_orders.where(dal.orders.c.shipped == shipped) 18 | result = dal.connection.execute(cust_orders).fetchall() 19 | return result 20 | -------------------------------------------------------------------------------- /ch04/db.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from sqlalchemy import (MetaData, Table, Column, Integer, Numeric, String, 3 | DateTime, ForeignKey, Boolean, create_engine) 4 | from sqlalchemy.sql import insert 5 | 6 | 7 | class DataAccessLayer: 8 | connection = None 9 | engine = None 10 | conn_string = None 11 | metadata = MetaData() 12 | cookies = Table('cookies', 13 | metadata, 14 | Column('cookie_id', Integer(), primary_key=True), 15 | Column('cookie_name', String(50), index=True), 16 | Column('cookie_recipe_url', String(255)), 17 | Column('cookie_sku', String(55)), 18 | Column('quantity', Integer()), 19 | Column('unit_cost', Numeric(12, 2)) 20 | ) 21 | 22 | users = Table('users', metadata, 23 | Column('user_id', Integer(), primary_key=True), 24 | Column('customer_number', Integer(), autoincrement=True), 25 | Column('username', String(15), nullable=False, unique=True), 26 | Column('email_address', String(255), nullable=False), 27 | Column('phone', String(20), nullable=False), 28 | Column('password', String(25), nullable=False), 29 | Column('created_on', DateTime(), default=datetime.now), 30 | Column('updated_on', DateTime(), default=datetime.now, onupdate=datetime.now) 31 | ) 32 | 33 | orders = Table('orders', metadata, 34 | Column('order_id', Integer()), 35 | Column('user_id', ForeignKey('users.user_id')), 36 | Column('shipped', Boolean(), default=False) 37 | ) 38 | 39 | line_items = Table('line_items', metadata, 40 | Column('line_items_id', Integer(), primary_key=True), 41 | Column('order_id', ForeignKey('orders.order_id')), 42 | Column('cookie_id', ForeignKey('cookies.cookie_id')), 43 | Column('quantity', Integer()), 44 | Column('extended_cost', Numeric(12, 2)) 45 | ) 46 | 47 | def db_init(self, conn_string): 48 | self.engine = create_engine(conn_string or self.conn_string) 49 | self.metadata.create_all(self.engine) 50 | self.connection = self.engine.connect() 51 | 52 | dal = DataAccessLayer() 53 | 54 | 55 | def prep_db(): 56 | ins = dal.cookies.insert() 57 | dal.connection.execute(ins, cookie_name='dark chocolate chip', 58 | cookie_recipe_url='http://some.aweso.me/cookie/recipe_dark.html', 59 | cookie_sku='CC02', 60 | quantity='1', 61 | unit_cost='0.75') 62 | inventory_list = [ 63 | { 64 | 'cookie_name': 'peanut butter', 65 | 'cookie_recipe_url': 'http://some.aweso.me/cookie/peanut.html', 66 | 'cookie_sku': 'PB01', 67 | 'quantity': '24', 68 | 'unit_cost': '0.25' 69 | }, 70 | { 71 | 'cookie_name': 'oatmeal raisin', 72 | 'cookie_recipe_url': 'http://some.okay.me/cookie/raisin.html', 73 | 'cookie_sku': 'EWW01', 74 | 'quantity': '100', 75 | 'unit_cost': '1.00' 76 | } 77 | ] 78 | dal.connection.execute(ins, inventory_list) 79 | 80 | customer_list = [ 81 | { 82 | 'username': "cookiemon", 83 | 'email_address': "mon@cookie.com", 84 | 'phone': "111-111-1111", 85 | 'password': "password" 86 | }, 87 | { 88 | 'username': "cakeeater", 89 | 'email_address': "cakeeater@cake.com", 90 | 'phone': "222-222-2222", 91 | 'password': "password" 92 | }, 93 | { 94 | 'username': "pieguy", 95 | 'email_address': "guy@pie.com", 96 | 'phone': "333-333-3333", 97 | 'password': "password" 98 | } 99 | ] 100 | ins = dal.users.insert() 101 | dal.connection.execute(ins, customer_list) 102 | ins = insert(dal.orders).values(user_id=1, order_id='wlk001') 103 | dal.connection.execute(ins) 104 | ins = insert(dal.line_items) 105 | order_items = [ 106 | { 107 | 'order_id': 'wlk001', 108 | 'cookie_id': 1, 109 | 'quantity': 2, 110 | 'extended_cost': 1.00 111 | }, 112 | { 113 | 'order_id': 'wlk001', 114 | 'cookie_id': 3, 115 | 'quantity': 12, 116 | 'extended_cost': 3.00 117 | } 118 | ] 119 | dal.connection.execute(ins, order_items) 120 | ins = insert(dal.orders).values(user_id=2, order_id='ol001') 121 | dal.connection.execute(ins) 122 | ins = insert(dal.line_items) 123 | order_items = [ 124 | { 125 | 'order_id': 'ol001', 126 | 'cookie_id': 1, 127 | 'quantity': 24, 128 | 'extended_cost': 12.00 129 | }, 130 | { 131 | 'order_id': 'ol001', 132 | 'cookie_id': 4, 133 | 'quantity': 6, 134 | 'extended_cost': 6.00 135 | } 136 | ] 137 | dal.connection.execute(ins, order_items) 138 | -------------------------------------------------------------------------------- /ch04/test_app.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from decimal import Decimal 4 | 5 | from db import dal, prep_db 6 | from app import get_orders_by_customer 7 | 8 | 9 | class TestApp(unittest.TestCase): 10 | cookie_orders = [(u'wlk001', u'cookiemon', u'111-111-1111')] 11 | cookie_details = [ 12 | (u'wlk001', u'cookiemon', u'111-111-1111', 13 | u'dark chocolate chip', 2, Decimal('1.00')), 14 | (u'wlk001', u'cookiemon', u'111-111-1111', 15 | u'oatmeal raisin', 12, Decimal('3.00'))] 16 | 17 | @classmethod 18 | def setUpClass(cls): 19 | dal.db_init('sqlite:///:memory:') 20 | prep_db() 21 | 22 | def test_orders_by_customer_blank(self): 23 | results = get_orders_by_customer('') 24 | self.assertEqual(results, []) 25 | 26 | def test_orders_by_customer_blank_shipped(self): 27 | results = get_orders_by_customer('', True) 28 | self.assertEqual(results, []) 29 | 30 | def test_orders_by_customer_blank_notshipped(self): 31 | results = get_orders_by_customer('', False) 32 | self.assertEqual(results, []) 33 | 34 | def test_orders_by_customer_blank_details(self): 35 | results = get_orders_by_customer('', details=True) 36 | self.assertEqual(results, []) 37 | 38 | def test_orders_by_customer_blank_shipped_details(self): 39 | results = get_orders_by_customer('', True, True) 40 | self.assertEqual(results, []) 41 | 42 | def test_orders_by_customer_blank_notshipped_details(self): 43 | results = get_orders_by_customer('', False, True) 44 | self.assertEqual(results, []) 45 | 46 | def test_orders_by_customer_bad_cust(self): 47 | results = get_orders_by_customer('bad name') 48 | self.assertEqual(results, []) 49 | 50 | def test_orders_by_customer_bad_cust_shipped(self): 51 | results = get_orders_by_customer('bad name', True) 52 | self.assertEqual(results, []) 53 | 54 | def test_orders_by_customer_bad_cust_notshipped(self): 55 | results = get_orders_by_customer('bad name', False) 56 | self.assertEqual(results, []) 57 | 58 | def test_orders_by_customer_bad_cust_details(self): 59 | results = get_orders_by_customer('bad name', details=True) 60 | self.assertEqual(results, []) 61 | 62 | def test_orders_by_customer_bad_cust_shipped_details(self): 63 | results = get_orders_by_customer('bad name', True, True) 64 | self.assertEqual(results, []) 65 | 66 | def test_orders_by_customer_bad_cust_notshipped_details(self): 67 | results = get_orders_by_customer('bad name', False, True) 68 | self.assertEqual(results, []) 69 | 70 | def test_orders_by_customer(self): 71 | results = get_orders_by_customer('cookiemon') 72 | self.assertEqual(results, self.cookie_orders) 73 | 74 | def test_orders_by_customer_shipped_only(self): 75 | results = get_orders_by_customer('cookiemon', True) 76 | self.assertEqual(results, []) 77 | 78 | def test_orders_by_customer_unshipped_only(self): 79 | results = get_orders_by_customer('cookiemon', False) 80 | self.assertEqual(results, self.cookie_orders) 81 | 82 | def test_orders_by_customer_with_details(self): 83 | results = get_orders_by_customer('cookiemon', details=True) 84 | self.assertEqual(results, self.cookie_details) 85 | 86 | def test_orders_by_customer_shipped_only_with_details(self): 87 | results = get_orders_by_customer('cookiemon', True, True) 88 | self.assertEqual(results, []) 89 | 90 | def test_orders_by_customer_unshipped_only_details(self): 91 | results = get_orders_by_customer('cookiemon', False, True) 92 | self.assertEqual(results, self.cookie_details) 93 | -------------------------------------------------------------------------------- /ch04/test_mock_app.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from decimal import Decimal 3 | 4 | import mock 5 | 6 | from db import dal, prep_db 7 | from app import get_orders_by_customer 8 | 9 | 10 | class TestApp(unittest.TestCase): 11 | cookie_orders = [(u'wlk001', u'cookiemon', u'111-111-1111')] 12 | cookie_details = [ 13 | (u'wlk001', u'cookiemon', u'111-111-1111', 14 | u'dark chocolate chip', 2, Decimal('1.00')), 15 | (u'wlk001', u'cookiemon', u'111-111-1111', 16 | u'oatmeal raisin', 12, Decimal('3.00'))] 17 | 18 | @mock.patch('app.select') 19 | @mock.patch('app.dal.connection') 20 | def test_orders_by_customer_blank(self, mock_conn, mock_select): 21 | mock_select.return_value.select_from.return_value.where.return_value = None 22 | mock_conn.execute.return_value.fetchall.return_value = [] 23 | results = get_orders_by_customer('') 24 | self.assertEqual(results, []) 25 | 26 | @mock.patch('app.dal.connection') 27 | def test_orders_by_customer_blank_shipped(self, mock_conn): 28 | mock_conn.execute.return_value.fetchall.return_value = [] 29 | results = get_orders_by_customer('', True) 30 | self.assertEqual(results, []) 31 | 32 | @mock.patch('app.dal.connection') 33 | def test_orders_by_customer(self, mock_conn): 34 | mock_conn.execute.return_value.fetchall.return_value = self.cookie_orders 35 | results = get_orders_by_customer('cookiemon') 36 | self.assertEqual(results, self.cookie_orders) 37 | -------------------------------------------------------------------------------- /ch05/.ipynb_checkpoints/CH05-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": false 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from sqlalchemy import MetaData, Table, create_engine\n", 12 | "metadata = MetaData()\n", 13 | "engine = create_engine('sqlite:///Chinook_Sqlite.sqlite')" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 2, 19 | "metadata": { 20 | "collapsed": false 21 | }, 22 | "outputs": [], 23 | "source": [ 24 | "artist = Table('artist', metadata, autoload=True, autoload_with=engine)" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": 3, 30 | "metadata": { 31 | "collapsed": false 32 | }, 33 | "outputs": [], 34 | "source": [ 35 | "album = Table('album', metadata, autoload=True, autoload_with=engine)" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 5, 41 | "metadata": { 42 | "collapsed": false 43 | }, 44 | "outputs": [ 45 | { 46 | "data": { 47 | "text/plain": [ 48 | "['ArtistId', 'Name']" 49 | ] 50 | }, 51 | "execution_count": 5, 52 | "metadata": {}, 53 | "output_type": "execute_result" 54 | } 55 | ], 56 | "source": [ 57 | "artist.columns.keys()" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 6, 63 | "metadata": { 64 | "collapsed": false 65 | }, 66 | "outputs": [ 67 | { 68 | "data": { 69 | "text/plain": [ 70 | "[(1, 'AC/DC'),\n", 71 | " (2, 'Accept'),\n", 72 | " (3, 'Aerosmith'),\n", 73 | " (4, 'Alanis Morissette'),\n", 74 | " (5, 'Alice In Chains'),\n", 75 | " (6, 'Antônio Carlos Jobim'),\n", 76 | " (7, 'Apocalyptica'),\n", 77 | " (8, 'Audioslave'),\n", 78 | " (9, 'BackBeat'),\n", 79 | " (10, 'Billy Cobham')]" 80 | ] 81 | }, 82 | "execution_count": 6, 83 | "metadata": {}, 84 | "output_type": "execute_result" 85 | } 86 | ], 87 | "source": [ 88 | "from sqlalchemy import select\n", 89 | "s = select([artist]).limit(10)\n", 90 | "engine.execute(s).fetchall()" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": 7, 96 | "metadata": { 97 | "collapsed": false 98 | }, 99 | "outputs": [ 100 | { 101 | "data": { 102 | "text/plain": [ 103 | "set()" 104 | ] 105 | }, 106 | "execution_count": 7, 107 | "metadata": {}, 108 | "output_type": "execute_result" 109 | } 110 | ], 111 | "source": [ 112 | "album.foreign_keys" 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": 9, 118 | "metadata": { 119 | "collapsed": false 120 | }, 121 | "outputs": [], 122 | "source": [ 123 | "from sqlalchemy import ForeignKeyConstraint\n", 124 | "album.append_constraint(\n", 125 | " ForeignKeyConstraint(['ArtistId'], ['artist.ArtistId'])\n", 126 | ")" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": 10, 132 | "metadata": { 133 | "collapsed": false 134 | }, 135 | "outputs": [ 136 | { 137 | "data": { 138 | "text/plain": [ 139 | "Table('album', MetaData(bind=None), Column('AlbumId', INTEGER(), table=, primary_key=True, nullable=False), Column('Title', NVARCHAR(length=160), table=, nullable=False), Column('ArtistId', INTEGER(), ForeignKey('artist.ArtistId'), table=, nullable=False), schema=None)" 140 | ] 141 | }, 142 | "execution_count": 10, 143 | "metadata": {}, 144 | "output_type": "execute_result" 145 | } 146 | ], 147 | "source": [ 148 | "metadata.tables['album']" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": 11, 154 | "metadata": { 155 | "collapsed": false 156 | }, 157 | "outputs": [ 158 | { 159 | "data": { 160 | "text/plain": [ 161 | "'artist JOIN album ON artist.\"ArtistId\" = album.\"ArtistId\"'" 162 | ] 163 | }, 164 | "execution_count": 11, 165 | "metadata": {}, 166 | "output_type": "execute_result" 167 | } 168 | ], 169 | "source": [ 170 | "str(artist.join(album))" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": 13, 176 | "metadata": { 177 | "collapsed": false 178 | }, 179 | "outputs": [], 180 | "source": [ 181 | "metadata.reflect(bind=engine)" 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": 17, 187 | "metadata": { 188 | "collapsed": false 189 | }, 190 | "outputs": [ 191 | { 192 | "data": { 193 | "text/plain": [ 194 | "dict_keys(['track', 'InvoiceLine', 'Employee', 'Invoice', 'album', 'Genre', 'PlaylistTrack', 'Album', 'Customer', 'MediaType', 'Artist', 'Track', 'artist', 'Playlist'])" 195 | ] 196 | }, 197 | "execution_count": 17, 198 | "metadata": {}, 199 | "output_type": "execute_result" 200 | } 201 | ], 202 | "source": [ 203 | "metadata.tables.keys()" 204 | ] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "execution_count": 24, 209 | "metadata": { 210 | "collapsed": true 211 | }, 212 | "outputs": [], 213 | "source": [ 214 | "playlist = metadata.tables['Playlist']" 215 | ] 216 | }, 217 | { 218 | "cell_type": "code", 219 | "execution_count": 25, 220 | "metadata": { 221 | "collapsed": false 222 | }, 223 | "outputs": [ 224 | { 225 | "data": { 226 | "text/plain": [ 227 | "[(1, 'Music'),\n", 228 | " (2, 'Movies'),\n", 229 | " (3, 'TV Shows'),\n", 230 | " (4, 'Audiobooks'),\n", 231 | " (5, '90’s Music'),\n", 232 | " (6, 'Audiobooks'),\n", 233 | " (7, 'Movies'),\n", 234 | " (8, 'Music'),\n", 235 | " (9, 'Music Videos'),\n", 236 | " (10, 'TV Shows')]" 237 | ] 238 | }, 239 | "execution_count": 25, 240 | "metadata": {}, 241 | "output_type": "execute_result" 242 | } 243 | ], 244 | "source": [ 245 | "from sqlalchemy import select\n", 246 | "s = select([playlist]).limit(10)\n", 247 | "engine.execute(s).fetchall()" 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": null, 253 | "metadata": { 254 | "collapsed": true 255 | }, 256 | "outputs": [], 257 | "source": [] 258 | } 259 | ], 260 | "metadata": { 261 | "kernelspec": { 262 | "display_name": "Python 2", 263 | "language": "python", 264 | "name": "python2" 265 | }, 266 | "language_info": { 267 | "codemirror_mode": { 268 | "name": "ipython", 269 | "version": 2 270 | }, 271 | "file_extension": ".py", 272 | "mimetype": "text/x-python", 273 | "name": "python", 274 | "nbconvert_exporter": "python", 275 | "pygments_lexer": "ipython2", 276 | "version": "2.7.10" 277 | } 278 | }, 279 | "nbformat": 4, 280 | "nbformat_minor": 0 281 | } 282 | -------------------------------------------------------------------------------- /ch05/CH05.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": false 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from sqlalchemy import MetaData, Table, create_engine\n", 12 | "metadata = MetaData()\n", 13 | "engine = create_engine('sqlite:///Chinook_Sqlite.sqlite')" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 2, 19 | "metadata": { 20 | "collapsed": false 21 | }, 22 | "outputs": [], 23 | "source": [ 24 | "artist = Table('artist', metadata, autoload=True, autoload_with=engine)" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": 3, 30 | "metadata": { 31 | "collapsed": false 32 | }, 33 | "outputs": [], 34 | "source": [ 35 | "album = Table('album', metadata, autoload=True, autoload_with=engine)" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": 5, 41 | "metadata": { 42 | "collapsed": false 43 | }, 44 | "outputs": [ 45 | { 46 | "data": { 47 | "text/plain": [ 48 | "['ArtistId', 'Name']" 49 | ] 50 | }, 51 | "execution_count": 5, 52 | "metadata": {}, 53 | "output_type": "execute_result" 54 | } 55 | ], 56 | "source": [ 57 | "artist.columns.keys()" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 6, 63 | "metadata": { 64 | "collapsed": false 65 | }, 66 | "outputs": [ 67 | { 68 | "data": { 69 | "text/plain": [ 70 | "[(1, 'AC/DC'),\n", 71 | " (2, 'Accept'),\n", 72 | " (3, 'Aerosmith'),\n", 73 | " (4, 'Alanis Morissette'),\n", 74 | " (5, 'Alice In Chains'),\n", 75 | " (6, 'Antônio Carlos Jobim'),\n", 76 | " (7, 'Apocalyptica'),\n", 77 | " (8, 'Audioslave'),\n", 78 | " (9, 'BackBeat'),\n", 79 | " (10, 'Billy Cobham')]" 80 | ] 81 | }, 82 | "execution_count": 6, 83 | "metadata": {}, 84 | "output_type": "execute_result" 85 | } 86 | ], 87 | "source": [ 88 | "from sqlalchemy import select\n", 89 | "s = select([artist]).limit(10)\n", 90 | "engine.execute(s).fetchall()" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": 7, 96 | "metadata": { 97 | "collapsed": false 98 | }, 99 | "outputs": [ 100 | { 101 | "data": { 102 | "text/plain": [ 103 | "set()" 104 | ] 105 | }, 106 | "execution_count": 7, 107 | "metadata": {}, 108 | "output_type": "execute_result" 109 | } 110 | ], 111 | "source": [ 112 | "album.foreign_keys" 113 | ] 114 | }, 115 | { 116 | "cell_type": "code", 117 | "execution_count": 9, 118 | "metadata": { 119 | "collapsed": false 120 | }, 121 | "outputs": [], 122 | "source": [ 123 | "from sqlalchemy import ForeignKeyConstraint\n", 124 | "album.append_constraint(\n", 125 | " ForeignKeyConstraint(['ArtistId'], ['artist.ArtistId'])\n", 126 | ")" 127 | ] 128 | }, 129 | { 130 | "cell_type": "code", 131 | "execution_count": 10, 132 | "metadata": { 133 | "collapsed": false 134 | }, 135 | "outputs": [ 136 | { 137 | "data": { 138 | "text/plain": [ 139 | "Table('album', MetaData(bind=None), Column('AlbumId', INTEGER(), table=, primary_key=True, nullable=False), Column('Title', NVARCHAR(length=160), table=, nullable=False), Column('ArtistId', INTEGER(), ForeignKey('artist.ArtistId'), table=, nullable=False), schema=None)" 140 | ] 141 | }, 142 | "execution_count": 10, 143 | "metadata": {}, 144 | "output_type": "execute_result" 145 | } 146 | ], 147 | "source": [ 148 | "metadata.tables['album']" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": 11, 154 | "metadata": { 155 | "collapsed": false 156 | }, 157 | "outputs": [ 158 | { 159 | "data": { 160 | "text/plain": [ 161 | "'artist JOIN album ON artist.\"ArtistId\" = album.\"ArtistId\"'" 162 | ] 163 | }, 164 | "execution_count": 11, 165 | "metadata": {}, 166 | "output_type": "execute_result" 167 | } 168 | ], 169 | "source": [ 170 | "str(artist.join(album))" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": 13, 176 | "metadata": { 177 | "collapsed": false 178 | }, 179 | "outputs": [], 180 | "source": [ 181 | "metadata.reflect(bind=engine)" 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": 17, 187 | "metadata": { 188 | "collapsed": false 189 | }, 190 | "outputs": [ 191 | { 192 | "data": { 193 | "text/plain": [ 194 | "dict_keys(['track', 'InvoiceLine', 'Employee', 'Invoice', 'album', 'Genre', 'PlaylistTrack', 'Album', 'Customer', 'MediaType', 'Artist', 'Track', 'artist', 'Playlist'])" 195 | ] 196 | }, 197 | "execution_count": 17, 198 | "metadata": {}, 199 | "output_type": "execute_result" 200 | } 201 | ], 202 | "source": [ 203 | "metadata.tables.keys()" 204 | ] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "execution_count": 24, 209 | "metadata": { 210 | "collapsed": true 211 | }, 212 | "outputs": [], 213 | "source": [ 214 | "playlist = metadata.tables['Playlist']" 215 | ] 216 | }, 217 | { 218 | "cell_type": "code", 219 | "execution_count": 25, 220 | "metadata": { 221 | "collapsed": false 222 | }, 223 | "outputs": [ 224 | { 225 | "data": { 226 | "text/plain": [ 227 | "[(1, 'Music'),\n", 228 | " (2, 'Movies'),\n", 229 | " (3, 'TV Shows'),\n", 230 | " (4, 'Audiobooks'),\n", 231 | " (5, '90’s Music'),\n", 232 | " (6, 'Audiobooks'),\n", 233 | " (7, 'Movies'),\n", 234 | " (8, 'Music'),\n", 235 | " (9, 'Music Videos'),\n", 236 | " (10, 'TV Shows')]" 237 | ] 238 | }, 239 | "execution_count": 25, 240 | "metadata": {}, 241 | "output_type": "execute_result" 242 | } 243 | ], 244 | "source": [ 245 | "from sqlalchemy import select\n", 246 | "s = select([playlist]).limit(10)\n", 247 | "engine.execute(s).fetchall()" 248 | ] 249 | }, 250 | { 251 | "cell_type": "code", 252 | "execution_count": null, 253 | "metadata": { 254 | "collapsed": true 255 | }, 256 | "outputs": [], 257 | "source": [] 258 | } 259 | ], 260 | "metadata": { 261 | "kernelspec": { 262 | "display_name": "Python 2", 263 | "language": "python", 264 | "name": "python2" 265 | }, 266 | "language_info": { 267 | "codemirror_mode": { 268 | "name": "ipython", 269 | "version": 2 270 | }, 271 | "file_extension": ".py", 272 | "mimetype": "text/x-python", 273 | "name": "python", 274 | "nbconvert_exporter": "python", 275 | "pygments_lexer": "ipython2", 276 | "version": "2.7.10" 277 | } 278 | }, 279 | "nbformat": 4, 280 | "nbformat_minor": 0 281 | } 282 | -------------------------------------------------------------------------------- /ch05/Chinook_Sqlite.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreillymedia/essential-sqlalchemy-2e/bdfed6cf7f0b36b20b4850f5fc0c489947cbea5f/ch05/Chinook_Sqlite.sqlite -------------------------------------------------------------------------------- /ch06.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from sqlalchemy.ext.declarative import declarative_base\n", 12 | "from sqlalchemy import Table, Column, Integer, Numeric, String, Boolean\n", 13 | "\n", 14 | "Base = declarative_base()\n", 15 | "\n", 16 | "class Cookie(Base):\n", 17 | " __tablename__ = 'cookies'\n", 18 | "\n", 19 | " cookie_id = Column(Integer(), primary_key=True)\n", 20 | " cookie_name = Column(String(50), index=True)\n", 21 | " cookie_recipe_url = Column(String(255))\n", 22 | " cookie_sku = Column(String(55))\n", 23 | " quantity = Column(Integer())\n", 24 | " unit_cost = Column(Numeric(12, 2))" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": 2, 30 | "metadata": { 31 | "collapsed": false 32 | }, 33 | "outputs": [ 34 | { 35 | "data": { 36 | "text/plain": [ 37 | "Table('cookies', MetaData(bind=None), Column('cookie_id', Integer(), table=, primary_key=True, nullable=False), Column('cookie_name', String(length=50), table=), Column('cookie_recipe_url', String(length=255), table=), Column('cookie_sku', String(length=55), table=), Column('quantity', Integer(), table=), Column('unit_cost', Numeric(precision=12, scale=2), table=), schema=None)" 38 | ] 39 | }, 40 | "execution_count": 2, 41 | "metadata": {}, 42 | "output_type": "execute_result" 43 | } 44 | ], 45 | "source": [ 46 | "Cookie.__table__" 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": 3, 52 | "metadata": { 53 | "collapsed": false 54 | }, 55 | "outputs": [], 56 | "source": [ 57 | "from datetime import datetime\n", 58 | "from sqlalchemy import DateTime\n", 59 | "\n", 60 | "class User(Base):\n", 61 | " __tablename__ = 'users'\n", 62 | " \n", 63 | " user_id = Column(Integer(), primary_key=True)\n", 64 | " username = Column(String(15), nullable=False, unique=True)\n", 65 | " email_address = Column(String(255), nullable=False)\n", 66 | " phone = Column(String(20), nullable=False)\n", 67 | " password = Column(String(25), nullable=False)\n", 68 | " created_on = Column(DateTime(), default=datetime.now)\n", 69 | " updated_on = Column(DateTime(), default=datetime.now, onupdate=datetime.now)" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 4, 75 | "metadata": { 76 | "collapsed": false 77 | }, 78 | "outputs": [ 79 | { 80 | "data": { 81 | "text/plain": [ 82 | "Table('users', MetaData(bind=None), Column('user_id', Integer(), table=, primary_key=True, nullable=False), Column('username', String(length=15), table=, nullable=False), Column('email_address', String(length=255), table=, nullable=False), Column('phone', String(length=20), table=, nullable=False), Column('password', String(length=25), table=, nullable=False), Column('created_on', DateTime(), table=, default=ColumnDefault( at 0x1124c30c8>)), Column('updated_on', DateTime(), table=, onupdate=ColumnDefault( at 0x112488d70>), default=ColumnDefault( at 0x112488de8>)), schema=None)" 83 | ] 84 | }, 85 | "execution_count": 4, 86 | "metadata": {}, 87 | "output_type": "execute_result" 88 | } 89 | ], 90 | "source": [ 91 | "User.__table__" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": 5, 97 | "metadata": { 98 | "collapsed": false 99 | }, 100 | "outputs": [], 101 | "source": [ 102 | "from sqlalchemy import ForeignKey\n", 103 | "from sqlalchemy.orm import relationship, backref\n", 104 | "\n", 105 | "class Order(Base):\n", 106 | " __tablename__ = 'orders'\n", 107 | " order_id = Column(Integer(), primary_key=True)\n", 108 | " user_id = Column(Integer(), ForeignKey('users.user_id'))\n", 109 | " shipped = Column(Boolean(), default=False)\n", 110 | " user = relationship(\"User\", backref=backref('orders', order_by=id))\n", 111 | "\n", 112 | "\n", 113 | "class LineItems(Base):\n", 114 | " __tablename__ = 'line_items'\n", 115 | " line_items_id = Column(Integer(), primary_key=True)\n", 116 | " order_id = Column(Integer(), ForeignKey('orders.order_id'))\n", 117 | " cookie_id = Column(Integer(), ForeignKey('cookies.cookie_id'))\n", 118 | " quantity = Column(Integer())\n", 119 | " extended_cost = Column(Numeric(12, 2))\n", 120 | " order = relationship(\"Order\", backref=backref('line_items', order_by=line_items_id))\n", 121 | " cookie = relationship(\"Cookie\", uselist=False)" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": 6, 127 | "metadata": { 128 | "collapsed": false 129 | }, 130 | "outputs": [], 131 | "source": [ 132 | "from sqlalchemy import create_engine\n", 133 | "engine = create_engine('sqlite:///:memory:')\n", 134 | "\n", 135 | "Base.metadata.create_all(engine)" 136 | ] 137 | } 138 | ], 139 | "metadata": { 140 | "kernelspec": { 141 | "display_name": "Python 2", 142 | "language": "python", 143 | "name": "python2" 144 | }, 145 | "language_info": { 146 | "codemirror_mode": { 147 | "name": "ipython", 148 | "version": 2 149 | }, 150 | "file_extension": ".py", 151 | "mimetype": "text/x-python", 152 | "name": "python", 153 | "nbconvert_exporter": "python", 154 | "pygments_lexer": "ipython2", 155 | "version": "2.7.10" 156 | } 157 | }, 158 | "nbformat": 4, 159 | "nbformat_minor": 0 160 | } 161 | -------------------------------------------------------------------------------- /ch07.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": false 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from sqlalchemy import create_engine\n", 12 | "from sqlalchemy.orm import sessionmaker\n", 13 | "\n", 14 | "engine = create_engine('sqlite:///:memory:')\n", 15 | "\n", 16 | "Session = sessionmaker(bind=engine)\n", 17 | "\n", 18 | "session = Session()" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 2, 24 | "metadata": { 25 | "collapsed": false 26 | }, 27 | "outputs": [], 28 | "source": [ 29 | "from datetime import datetime\n", 30 | "\n", 31 | "from sqlalchemy import Column, Integer, Numeric, String, DateTime, ForeignKey, Boolean\n", 32 | "from sqlalchemy.ext.declarative import declarative_base\n", 33 | "from sqlalchemy.orm import relationship, backref\n", 34 | "\n", 35 | "\n", 36 | "Base = declarative_base()\n", 37 | "\n", 38 | "\n", 39 | "class Cookie(Base):\n", 40 | " __tablename__ = 'cookies'\n", 41 | "\n", 42 | " cookie_id = Column(Integer, primary_key=True)\n", 43 | " cookie_name = Column(String(50), index=True)\n", 44 | " cookie_recipe_url = Column(String(255))\n", 45 | " cookie_sku = Column(String(55))\n", 46 | " quantity = Column(Integer())\n", 47 | " unit_cost = Column(Numeric(12, 2))\n", 48 | " \n", 49 | " def __repr__(self):\n", 50 | " return \"Cookie(cookie_name='{self.cookie_name}', \" \\\n", 51 | " \"cookie_recipe_url='{self.cookie_recipe_url}', \" \\\n", 52 | " \"cookie_sku='{self.cookie_sku}', \" \\\n", 53 | " \"quantity={self.quantity}, \" \\\n", 54 | " \"unit_cost={self.unit_cost})\".format(self=self)\n", 55 | " \n", 56 | " \n", 57 | "class User(Base):\n", 58 | " __tablename__ = 'users'\n", 59 | " \n", 60 | " user_id = Column(Integer(), primary_key=True)\n", 61 | " username = Column(String(15), nullable=False, unique=True)\n", 62 | " email_address = Column(String(255), nullable=False)\n", 63 | " phone = Column(String(20), nullable=False)\n", 64 | " password = Column(String(25), nullable=False)\n", 65 | " created_on = Column(DateTime(), default=datetime.now)\n", 66 | " updated_on = Column(DateTime(), default=datetime.now, onupdate=datetime.now)\n", 67 | " \n", 68 | " def __repr__(self):\n", 69 | " return \"User(username='{self.username}', \" \\\n", 70 | " \"email_address='{self.email_address}', \" \\\n", 71 | " \"phone='{self.phone}', \" \\\n", 72 | " \"password='{self.password}')\".format(self=self)\n", 73 | " \n", 74 | "\n", 75 | "class Order(Base):\n", 76 | " __tablename__ = 'orders'\n", 77 | " order_id = Column(Integer(), primary_key=True)\n", 78 | " user_id = Column(Integer(), ForeignKey('users.user_id'))\n", 79 | " shipped = Column(Boolean(), default=False)\n", 80 | " \n", 81 | " user = relationship(\"User\", backref=backref('orders', order_by=order_id))\n", 82 | " \n", 83 | " def __repr__(self):\n", 84 | " return \"Order(user_id={self.user_id}, \" \\\n", 85 | " \"shipped={self.shipped})\".format(self=self)\n", 86 | "\n", 87 | "\n", 88 | "class LineItem(Base):\n", 89 | " __tablename__ = 'line_items'\n", 90 | " line_item_id = Column(Integer(), primary_key=True)\n", 91 | " order_id = Column(Integer(), ForeignKey('orders.order_id'))\n", 92 | " cookie_id = Column(Integer(), ForeignKey('cookies.cookie_id'))\n", 93 | " quantity = Column(Integer())\n", 94 | " extended_cost = Column(Numeric(12, 2))\n", 95 | " \n", 96 | " order = relationship(\"Order\", backref=backref('line_items', order_by=line_item_id))\n", 97 | " cookie = relationship(\"Cookie\", uselist=False)\n", 98 | "\n", 99 | " def __repr__(self):\n", 100 | " return \"LineItems(order_id={self.order_id}, \" \\\n", 101 | " \"cookie_id={self.cookie_id}, \" \\\n", 102 | " \"quantity={self.quantity}, \" \\\n", 103 | " \"extended_cost={self.extended_cost})\".format(\n", 104 | " self=self) \n", 105 | " \n", 106 | "Base.metadata.create_all(engine)" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": 3, 112 | "metadata": { 113 | "collapsed": false 114 | }, 115 | "outputs": [], 116 | "source": [ 117 | "cc_cookie = Cookie(cookie_name='chocolate chip', \n", 118 | " cookie_recipe_url='http://some.aweso.me/cookie/recipe.html', \n", 119 | " cookie_sku='CC01', \n", 120 | " quantity=12, \n", 121 | " unit_cost=0.50)" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": 4, 127 | "metadata": { 128 | "collapsed": false 129 | }, 130 | "outputs": [], 131 | "source": [ 132 | "session.add(cc_cookie)" 133 | ] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "execution_count": 5, 138 | "metadata": { 139 | "collapsed": false 140 | }, 141 | "outputs": [], 142 | "source": [ 143 | "session.commit()" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": 6, 149 | "metadata": { 150 | "collapsed": false 151 | }, 152 | "outputs": [ 153 | { 154 | "name": "stderr", 155 | "output_type": "stream", 156 | "text": [ 157 | "/Users/jasomyer/.virtualenvs/sa-book/lib/python2.7/site-packages/sqlalchemy/sql/sqltypes.py:565: SAWarning: Dialect sqlite+pysqlite does *not* support Decimal objects natively, and SQLAlchemy must convert from floating point - rounding errors and other issues may occur. Please consider storing Decimal numbers as strings or integers on this platform for lossless storage.\n", 158 | " 'storage.' % (dialect.name, dialect.driver))\n" 159 | ] 160 | }, 161 | { 162 | "data": { 163 | "text/plain": [ 164 | "1" 165 | ] 166 | }, 167 | "execution_count": 6, 168 | "metadata": {}, 169 | "output_type": "execute_result" 170 | } 171 | ], 172 | "source": [ 173 | "cc_cookie.cookie_id" 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": 7, 179 | "metadata": { 180 | "collapsed": false 181 | }, 182 | "outputs": [], 183 | "source": [ 184 | "dcc = Cookie(cookie_name='dark chocolate chip',\n", 185 | " cookie_recipe_url='http://some.aweso.me/cookie/recipe_dark.html',\n", 186 | " cookie_sku='CC02',\n", 187 | " quantity=1,\n", 188 | " unit_cost=0.75)\n", 189 | "mol = Cookie(cookie_name='molasses',\n", 190 | " cookie_recipe_url='http://some.aweso.me/cookie/recipe_molasses.html',\n", 191 | " cookie_sku='MOL01',\n", 192 | " quantity=1,\n", 193 | " unit_cost=0.80)\n", 194 | "session.add(dcc)\n", 195 | "session.add(mol)\n", 196 | "session.flush()" 197 | ] 198 | }, 199 | { 200 | "cell_type": "code", 201 | "execution_count": 8, 202 | "metadata": { 203 | "collapsed": false 204 | }, 205 | "outputs": [ 206 | { 207 | "name": "stdout", 208 | "output_type": "stream", 209 | "text": [ 210 | "2\n", 211 | "3\n" 212 | ] 213 | } 214 | ], 215 | "source": [ 216 | "print(dcc.cookie_id)\n", 217 | "print(mol.cookie_id)" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": 9, 223 | "metadata": { 224 | "collapsed": false 225 | }, 226 | "outputs": [], 227 | "source": [ 228 | "c1 = Cookie(cookie_name='peanut butter',\n", 229 | " cookie_recipe_url='http://some.aweso.me/cookie/peanut.html',\n", 230 | " cookie_sku='PB01',\n", 231 | " quantity=24,\n", 232 | " unit_cost=0.25)\n", 233 | "c2 = Cookie(cookie_name='oatmeal raisin',\n", 234 | " cookie_recipe_url='http://some.okay.me/cookie/raisin.html',\n", 235 | " cookie_sku='EWW01',\n", 236 | " quantity=100,\n", 237 | " unit_cost=1.00)" 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": 10, 243 | "metadata": { 244 | "collapsed": false 245 | }, 246 | "outputs": [], 247 | "source": [ 248 | "session.bulk_save_objects([c1,c2])\n", 249 | "session.commit()" 250 | ] 251 | }, 252 | { 253 | "cell_type": "code", 254 | "execution_count": 11, 255 | "metadata": { 256 | "collapsed": false 257 | }, 258 | "outputs": [], 259 | "source": [ 260 | "c1.cookie_id" 261 | ] 262 | }, 263 | { 264 | "cell_type": "code", 265 | "execution_count": 12, 266 | "metadata": { 267 | "collapsed": false 268 | }, 269 | "outputs": [ 270 | { 271 | "name": "stdout", 272 | "output_type": "stream", 273 | "text": [ 274 | "[Cookie(cookie_name='chocolate chip', cookie_recipe_url='http://some.aweso.me/cookie/recipe.html', cookie_sku='CC01', quantity=12, unit_cost=0.50), Cookie(cookie_name='dark chocolate chip', cookie_recipe_url='http://some.aweso.me/cookie/recipe_dark.html', cookie_sku='CC02', quantity=1, unit_cost=0.75), Cookie(cookie_name='molasses', cookie_recipe_url='http://some.aweso.me/cookie/recipe_molasses.html', cookie_sku='MOL01', quantity=1, unit_cost=0.80), Cookie(cookie_name='peanut butter', cookie_recipe_url='http://some.aweso.me/cookie/peanut.html', cookie_sku='PB01', quantity=24, unit_cost=0.25), Cookie(cookie_name='oatmeal raisin', cookie_recipe_url='http://some.okay.me/cookie/raisin.html', cookie_sku='EWW01', quantity=100, unit_cost=1.00)]\n" 275 | ] 276 | } 277 | ], 278 | "source": [ 279 | "cookies = session.query(Cookie).all()\n", 280 | "print(cookies)" 281 | ] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": 13, 286 | "metadata": { 287 | "collapsed": false 288 | }, 289 | "outputs": [ 290 | { 291 | "name": "stdout", 292 | "output_type": "stream", 293 | "text": [ 294 | "Cookie(cookie_name='chocolate chip', cookie_recipe_url='http://some.aweso.me/cookie/recipe.html', cookie_sku='CC01', quantity=12, unit_cost=0.50)\n", 295 | "Cookie(cookie_name='dark chocolate chip', cookie_recipe_url='http://some.aweso.me/cookie/recipe_dark.html', cookie_sku='CC02', quantity=1, unit_cost=0.75)\n", 296 | "Cookie(cookie_name='molasses', cookie_recipe_url='http://some.aweso.me/cookie/recipe_molasses.html', cookie_sku='MOL01', quantity=1, unit_cost=0.80)\n", 297 | "Cookie(cookie_name='peanut butter', cookie_recipe_url='http://some.aweso.me/cookie/peanut.html', cookie_sku='PB01', quantity=24, unit_cost=0.25)\n", 298 | "Cookie(cookie_name='oatmeal raisin', cookie_recipe_url='http://some.okay.me/cookie/raisin.html', cookie_sku='EWW01', quantity=100, unit_cost=1.00)\n" 299 | ] 300 | } 301 | ], 302 | "source": [ 303 | "for cookie in session.query(Cookie): \n", 304 | " print(cookie)" 305 | ] 306 | }, 307 | { 308 | "cell_type": "code", 309 | "execution_count": 14, 310 | "metadata": { 311 | "collapsed": false 312 | }, 313 | "outputs": [ 314 | { 315 | "name": "stdout", 316 | "output_type": "stream", 317 | "text": [ 318 | "(u'chocolate chip', 12)\n" 319 | ] 320 | } 321 | ], 322 | "source": [ 323 | "print(session.query(Cookie.cookie_name, Cookie.quantity).first())" 324 | ] 325 | }, 326 | { 327 | "cell_type": "code", 328 | "execution_count": 15, 329 | "metadata": { 330 | "collapsed": false 331 | }, 332 | "outputs": [ 333 | { 334 | "name": "stdout", 335 | "output_type": "stream", 336 | "text": [ 337 | " 1 - dark chocolate chip\n", 338 | " 1 - molasses\n", 339 | " 12 - chocolate chip\n", 340 | " 24 - peanut butter\n", 341 | "100 - oatmeal raisin\n" 342 | ] 343 | } 344 | ], 345 | "source": [ 346 | "for cookie in session.query(Cookie).order_by(Cookie.quantity):\n", 347 | " print('{:3} - {}'.format(cookie.quantity, cookie.cookie_name))" 348 | ] 349 | }, 350 | { 351 | "cell_type": "code", 352 | "execution_count": 16, 353 | "metadata": { 354 | "collapsed": false 355 | }, 356 | "outputs": [ 357 | { 358 | "name": "stdout", 359 | "output_type": "stream", 360 | "text": [ 361 | "100 - oatmeal raisin\n", 362 | " 24 - peanut butter\n", 363 | " 12 - chocolate chip\n", 364 | " 1 - dark chocolate chip\n", 365 | " 1 - molasses\n" 366 | ] 367 | } 368 | ], 369 | "source": [ 370 | "from sqlalchemy import desc\n", 371 | "for cookie in session.query(Cookie).order_by(desc(Cookie.quantity)):\n", 372 | " print('{:3} - {}'.format(cookie.quantity, cookie.cookie_name))" 373 | ] 374 | }, 375 | { 376 | "cell_type": "code", 377 | "execution_count": 17, 378 | "metadata": { 379 | "collapsed": false 380 | }, 381 | "outputs": [ 382 | { 383 | "name": "stdout", 384 | "output_type": "stream", 385 | "text": [ 386 | "[u'dark chocolate chip', u'molasses']\n" 387 | ] 388 | } 389 | ], 390 | "source": [ 391 | "query = session.query(Cookie).order_by(Cookie.quantity)[:2]\n", 392 | "print([result.cookie_name for result in query])" 393 | ] 394 | }, 395 | { 396 | "cell_type": "code", 397 | "execution_count": 18, 398 | "metadata": { 399 | "collapsed": false 400 | }, 401 | "outputs": [ 402 | { 403 | "name": "stdout", 404 | "output_type": "stream", 405 | "text": [ 406 | "[u'dark chocolate chip', u'molasses']\n" 407 | ] 408 | } 409 | ], 410 | "source": [ 411 | "query = session.query(Cookie).order_by(Cookie.quantity).limit(2)\n", 412 | "print([result.cookie_name for result in query])" 413 | ] 414 | }, 415 | { 416 | "cell_type": "code", 417 | "execution_count": 19, 418 | "metadata": { 419 | "collapsed": false 420 | }, 421 | "outputs": [ 422 | { 423 | "name": "stdout", 424 | "output_type": "stream", 425 | "text": [ 426 | "138\n" 427 | ] 428 | } 429 | ], 430 | "source": [ 431 | "from sqlalchemy import func\n", 432 | "inv_count = session.query(func.sum(Cookie.quantity)).scalar()\n", 433 | "print(inv_count)" 434 | ] 435 | }, 436 | { 437 | "cell_type": "code", 438 | "execution_count": 20, 439 | "metadata": { 440 | "collapsed": false 441 | }, 442 | "outputs": [ 443 | { 444 | "name": "stdout", 445 | "output_type": "stream", 446 | "text": [ 447 | "(5,)\n" 448 | ] 449 | } 450 | ], 451 | "source": [ 452 | "rec_count = session.query(func.count(Cookie.cookie_name)).first()\n", 453 | "print(rec_count)" 454 | ] 455 | }, 456 | { 457 | "cell_type": "code", 458 | "execution_count": 21, 459 | "metadata": { 460 | "collapsed": false 461 | }, 462 | "outputs": [ 463 | { 464 | "name": "stdout", 465 | "output_type": "stream", 466 | "text": [ 467 | "['inventory_count']\n", 468 | "5\n" 469 | ] 470 | } 471 | ], 472 | "source": [ 473 | "rec_count = session.query(func.count(Cookie.cookie_name) \\\n", 474 | " .label('inventory_count')).first()\n", 475 | "print(rec_count.keys())\n", 476 | "print(rec_count.inventory_count)" 477 | ] 478 | }, 479 | { 480 | "cell_type": "code", 481 | "execution_count": 22, 482 | "metadata": { 483 | "collapsed": false 484 | }, 485 | "outputs": [ 486 | { 487 | "name": "stdout", 488 | "output_type": "stream", 489 | "text": [ 490 | "Cookie(cookie_name='chocolate chip', cookie_recipe_url='http://some.aweso.me/cookie/recipe.html', cookie_sku='CC01', quantity=12, unit_cost=0.50)\n" 491 | ] 492 | } 493 | ], 494 | "source": [ 495 | "record = session.query(Cookie).filter(Cookie.cookie_name == 'chocolate chip').first()\n", 496 | "print(record)" 497 | ] 498 | }, 499 | { 500 | "cell_type": "code", 501 | "execution_count": 23, 502 | "metadata": { 503 | "collapsed": false 504 | }, 505 | "outputs": [ 506 | { 507 | "name": "stdout", 508 | "output_type": "stream", 509 | "text": [ 510 | "Cookie(cookie_name='chocolate chip', cookie_recipe_url='http://some.aweso.me/cookie/recipe.html', cookie_sku='CC01', quantity=12, unit_cost=0.50)\n" 511 | ] 512 | } 513 | ], 514 | "source": [ 515 | "record = session.query(Cookie).filter_by(cookie_name='chocolate chip').first()\n", 516 | "print(record)" 517 | ] 518 | }, 519 | { 520 | "cell_type": "code", 521 | "execution_count": 24, 522 | "metadata": { 523 | "collapsed": false 524 | }, 525 | "outputs": [ 526 | { 527 | "name": "stdout", 528 | "output_type": "stream", 529 | "text": [ 530 | "chocolate chip\n", 531 | "dark chocolate chip\n" 532 | ] 533 | } 534 | ], 535 | "source": [ 536 | "query = session.query(Cookie).filter(Cookie.cookie_name.like('%chocolate%'))\n", 537 | "for record in query: \n", 538 | " print(record.cookie_name)" 539 | ] 540 | }, 541 | { 542 | "cell_type": "code", 543 | "execution_count": 25, 544 | "metadata": { 545 | "collapsed": false 546 | }, 547 | "outputs": [ 548 | { 549 | "name": "stdout", 550 | "output_type": "stream", 551 | "text": [ 552 | "(u'chocolate chip', u'SKU-CC01')\n", 553 | "(u'dark chocolate chip', u'SKU-CC02')\n", 554 | "(u'molasses', u'SKU-MOL01')\n", 555 | "(u'peanut butter', u'SKU-PB01')\n", 556 | "(u'oatmeal raisin', u'SKU-EWW01')\n" 557 | ] 558 | } 559 | ], 560 | "source": [ 561 | "results = session.query(Cookie.cookie_name, 'SKU-' + Cookie.cookie_sku).all()\n", 562 | "for row in results:\n", 563 | " print(row)" 564 | ] 565 | }, 566 | { 567 | "cell_type": "code", 568 | "execution_count": 26, 569 | "metadata": { 570 | "collapsed": false 571 | }, 572 | "outputs": [ 573 | { 574 | "name": "stdout", 575 | "output_type": "stream", 576 | "text": [ 577 | "chocolate chip - 6.00\n", 578 | "dark chocolate chip - 0.75\n", 579 | "molasses - 0.80\n", 580 | "peanut butter - 6.00\n", 581 | "oatmeal raisin - 100.00\n" 582 | ] 583 | } 584 | ], 585 | "source": [ 586 | "from sqlalchemy import cast\n", 587 | "query = session.query(Cookie.cookie_name,\n", 588 | " cast((Cookie.quantity * Cookie.unit_cost), \n", 589 | " Numeric(12,2)).label('inv_cost'))\n", 590 | "for result in query:\n", 591 | " print('{} - {}'.format(result.cookie_name, result.inv_cost))" 592 | ] 593 | }, 594 | { 595 | "cell_type": "code", 596 | "execution_count": 27, 597 | "metadata": { 598 | "collapsed": false 599 | }, 600 | "outputs": [ 601 | { 602 | "name": "stdout", 603 | "output_type": "stream", 604 | "text": [ 605 | "peanut butter\n" 606 | ] 607 | } 608 | ], 609 | "source": [ 610 | "from sqlalchemy import and_, or_, not_\n", 611 | "query = session.query(Cookie).filter(\n", 612 | " Cookie.quantity > 23,\n", 613 | " Cookie.unit_cost < 0.40\n", 614 | ")\n", 615 | "for result in query:\n", 616 | " print(result.cookie_name)" 617 | ] 618 | }, 619 | { 620 | "cell_type": "code", 621 | "execution_count": 28, 622 | "metadata": { 623 | "collapsed": false 624 | }, 625 | "outputs": [ 626 | { 627 | "name": "stdout", 628 | "output_type": "stream", 629 | "text": [ 630 | "chocolate chip\n", 631 | "dark chocolate chip\n", 632 | "peanut butter\n" 633 | ] 634 | } 635 | ], 636 | "source": [ 637 | "from sqlalchemy import and_, or_, not_\n", 638 | "query = session.query(Cookie).filter(\n", 639 | " or_(\n", 640 | " Cookie.quantity.between(10, 50),\n", 641 | " Cookie.cookie_name.contains('chip')\n", 642 | " )\n", 643 | ")\n", 644 | "for result in query:\n", 645 | " print(result.cookie_name)" 646 | ] 647 | }, 648 | { 649 | "cell_type": "code", 650 | "execution_count": 29, 651 | "metadata": { 652 | "collapsed": false 653 | }, 654 | "outputs": [ 655 | { 656 | "name": "stdout", 657 | "output_type": "stream", 658 | "text": [ 659 | "132\n" 660 | ] 661 | } 662 | ], 663 | "source": [ 664 | "query = session.query(Cookie)\n", 665 | "cc_cookie = query.filter(Cookie.cookie_name == \"chocolate chip\").first()\n", 666 | "cc_cookie.quantity = cc_cookie.quantity + 120\n", 667 | "session.commit()\n", 668 | "print(cc_cookie.quantity)" 669 | ] 670 | }, 671 | { 672 | "cell_type": "code", 673 | "execution_count": 30, 674 | "metadata": { 675 | "collapsed": false 676 | }, 677 | "outputs": [ 678 | { 679 | "name": "stdout", 680 | "output_type": "stream", 681 | "text": [ 682 | "112\n" 683 | ] 684 | } 685 | ], 686 | "source": [ 687 | "query = session.query(Cookie)\n", 688 | "query = query.filter(Cookie.cookie_name == \"chocolate chip\")\n", 689 | "query.update({Cookie.quantity: Cookie.quantity - 20})\n", 690 | "\n", 691 | "cc_cookie = query.first()\n", 692 | "print(cc_cookie.quantity)" 693 | ] 694 | }, 695 | { 696 | "cell_type": "code", 697 | "execution_count": 31, 698 | "metadata": { 699 | "collapsed": false 700 | }, 701 | "outputs": [ 702 | { 703 | "name": "stdout", 704 | "output_type": "stream", 705 | "text": [ 706 | "None\n" 707 | ] 708 | } 709 | ], 710 | "source": [ 711 | "query = session.query(Cookie)\n", 712 | "query = query.filter(Cookie.cookie_name == \"dark chocolate chip\")\n", 713 | "dcc_cookie = query.one()\n", 714 | "session.delete(dcc_cookie)\n", 715 | "session.commit()\n", 716 | "dcc_cookie = query.first()\n", 717 | "print(dcc_cookie)" 718 | ] 719 | }, 720 | { 721 | "cell_type": "code", 722 | "execution_count": 32, 723 | "metadata": { 724 | "collapsed": false 725 | }, 726 | "outputs": [ 727 | { 728 | "name": "stdout", 729 | "output_type": "stream", 730 | "text": [ 731 | "None\n" 732 | ] 733 | } 734 | ], 735 | "source": [ 736 | "query = session.query(Cookie)\n", 737 | "query = query.filter(Cookie.cookie_name == \"molasses\")\n", 738 | "query.delete()\n", 739 | "mol_cookie = query.first()\n", 740 | "print(mol_cookie)" 741 | ] 742 | }, 743 | { 744 | "cell_type": "code", 745 | "execution_count": 33, 746 | "metadata": { 747 | "collapsed": false 748 | }, 749 | "outputs": [], 750 | "source": [ 751 | "cookiemon = User(username='cookiemon', \n", 752 | " email_address='mon@cookie.com', \n", 753 | " phone='111-111-1111', \n", 754 | " password='password')\n", 755 | "cakeeater = User(username='cakeeater', \n", 756 | " email_address='cakeeater@cake.com', \n", 757 | " phone='222-222-2222', \n", 758 | " password='password')\n", 759 | "pieperson = User(username='pieperson', \n", 760 | " email_address='person@pie.com', \n", 761 | " phone='333-333-3333', \n", 762 | " password='password')\n", 763 | "session.add(cookiemon)\n", 764 | "session.add(cakeeater)\n", 765 | "session.add(pieperson)\n", 766 | "session.commit()" 767 | ] 768 | }, 769 | { 770 | "cell_type": "code", 771 | "execution_count": 34, 772 | "metadata": { 773 | "collapsed": false 774 | }, 775 | "outputs": [], 776 | "source": [ 777 | "o1 = Order()\n", 778 | "o1.user = cookiemon\n", 779 | "session.add(o1)\n", 780 | "\n", 781 | "cc = session.query(Cookie).filter(Cookie.cookie_name == \n", 782 | " \"chocolate chip\").one()\n", 783 | "line1 = LineItem(cookie=cc, quantity=2, extended_cost=1.00)\n", 784 | "\n", 785 | "pb = session.query(Cookie).filter(Cookie.cookie_name == \n", 786 | " \"peanut butter\").one()\n", 787 | "line2 = LineItem(quantity=12, extended_cost=3.00)\n", 788 | "line2.cookie = pb \n", 789 | "line2.order = o1\n", 790 | "\n", 791 | "o1.line_items.append(line1)\n", 792 | "o1.line_items.append(line2)\n", 793 | "session.commit()" 794 | ] 795 | }, 796 | { 797 | "cell_type": "code", 798 | "execution_count": 35, 799 | "metadata": { 800 | "collapsed": false 801 | }, 802 | "outputs": [], 803 | "source": [ 804 | "o2 = Order()\n", 805 | "o2.user = cakeeater\n", 806 | "\n", 807 | "cc = session.query(Cookie).filter(Cookie.cookie_name == \n", 808 | " \"chocolate chip\").one()\n", 809 | "line1 = LineItem(cookie=cc, quantity=24, extended_cost=12.00)\n", 810 | "\n", 811 | "oat = session.query(Cookie).filter(Cookie.cookie_name == \n", 812 | " \"oatmeal raisin\").one()\n", 813 | "line2 = LineItem(cookie=oat, quantity=6, extended_cost=6.00)\n", 814 | "\n", 815 | "o2.line_items.append(line1)\n", 816 | "o2.line_items.append(line2)\n", 817 | "\n", 818 | "session.add(o2)\n", 819 | "session.commit()" 820 | ] 821 | }, 822 | { 823 | "cell_type": "code", 824 | "execution_count": 36, 825 | "metadata": { 826 | "collapsed": false 827 | }, 828 | "outputs": [ 829 | { 830 | "name": "stdout", 831 | "output_type": "stream", 832 | "text": [ 833 | "[(1, u'cookiemon', u'111-111-1111', u'peanut butter', 12, Decimal('3.00')), (1, u'cookiemon', u'111-111-1111', u'chocolate chip', 2, Decimal('1.00'))]\n" 834 | ] 835 | } 836 | ], 837 | "source": [ 838 | "query = session.query(Order.order_id, User.username, User.phone,\n", 839 | " Cookie.cookie_name, LineItem.quantity,\n", 840 | " LineItem.extended_cost)\n", 841 | "query = query.join(User).join(LineItem).join(Cookie)\n", 842 | "results = query.filter(User.username == 'cookiemon').all()\n", 843 | "print(results)" 844 | ] 845 | }, 846 | { 847 | "cell_type": "code", 848 | "execution_count": 37, 849 | "metadata": { 850 | "collapsed": false 851 | }, 852 | "outputs": [ 853 | { 854 | "name": "stdout", 855 | "output_type": "stream", 856 | "text": [ 857 | "(u'cakeeater', 1)\n", 858 | "(u'cookiemon', 1)\n", 859 | "(u'pieperson', 0)\n" 860 | ] 861 | } 862 | ], 863 | "source": [ 864 | "query = session.query(User.username, func.count(Order.order_id))\n", 865 | "query = query.outerjoin(Order).group_by(User.username)\n", 866 | "for row in query:\n", 867 | " print(row)" 868 | ] 869 | }, 870 | { 871 | "cell_type": "code", 872 | "execution_count": 38, 873 | "metadata": { 874 | "collapsed": false 875 | }, 876 | "outputs": [], 877 | "source": [ 878 | "class Employee(Base):\n", 879 | " __tablename__ = 'employees'\n", 880 | " \n", 881 | " id = Column(Integer(), primary_key=True)\n", 882 | " manager_id = Column(Integer(), ForeignKey('employees.id'))\n", 883 | " name = Column(String(255), nullable=False)\n", 884 | " \n", 885 | " manager = relationship(\"Employee\", backref=backref('reports'), remote_side=[id])\n", 886 | "\n", 887 | "Base.metadata.create_all(engine)" 888 | ] 889 | }, 890 | { 891 | "cell_type": "code", 892 | "execution_count": 39, 893 | "metadata": { 894 | "collapsed": false 895 | }, 896 | "outputs": [], 897 | "source": [ 898 | "marsha = Employee(name='Marsha')\n", 899 | "fred = Employee(name='Fred')\n", 900 | "marsha.reports.append(fred)\n", 901 | "session.add(marsha)\n", 902 | "session.commit()" 903 | ] 904 | }, 905 | { 906 | "cell_type": "code", 907 | "execution_count": 40, 908 | "metadata": { 909 | "collapsed": false 910 | }, 911 | "outputs": [ 912 | { 913 | "name": "stdout", 914 | "output_type": "stream", 915 | "text": [ 916 | "Fred\n" 917 | ] 918 | } 919 | ], 920 | "source": [ 921 | "for report in marsha.reports:\n", 922 | " print(report.name)" 923 | ] 924 | }, 925 | { 926 | "cell_type": "code", 927 | "execution_count": 41, 928 | "metadata": { 929 | "collapsed": false 930 | }, 931 | "outputs": [ 932 | { 933 | "name": "stdout", 934 | "output_type": "stream", 935 | "text": [ 936 | "(u'cakeeater', 1)\n", 937 | "(u'cookiemon', 1)\n", 938 | "(u'pieperson', 0)\n" 939 | ] 940 | } 941 | ], 942 | "source": [ 943 | "query = session.query(User.username, func.count(Order.order_id))\n", 944 | "query = query.outerjoin(Order).group_by(User.username)\n", 945 | "for row in query:\n", 946 | " print(row)" 947 | ] 948 | }, 949 | { 950 | "cell_type": "code", 951 | "execution_count": 42, 952 | "metadata": { 953 | "collapsed": false 954 | }, 955 | "outputs": [ 956 | { 957 | "data": { 958 | "text/plain": [ 959 | "[(2, u'cakeeater', u'222-222-2222', u'chocolate chip', 24, Decimal('12.00')),\n", 960 | " (2, u'cakeeater', u'222-222-2222', u'oatmeal raisin', 6, Decimal('6.00'))]" 961 | ] 962 | }, 963 | "execution_count": 42, 964 | "metadata": {}, 965 | "output_type": "execute_result" 966 | } 967 | ], 968 | "source": [ 969 | "def get_orders_by_customer(cust_name):\n", 970 | " query = session.query(Order.order_id, User.username, User.phone,\n", 971 | " Cookie.cookie_name, LineItem.quantity,\n", 972 | " LineItem.extended_cost)\n", 973 | " query = query.join(User).join(LineItem).join(Cookie)\n", 974 | " results = query.filter(User.username == cust_name).all()\n", 975 | " return results\n", 976 | "\n", 977 | "get_orders_by_customer('cakeeater')" 978 | ] 979 | }, 980 | { 981 | "cell_type": "code", 982 | "execution_count": 43, 983 | "metadata": { 984 | "collapsed": false 985 | }, 986 | "outputs": [ 987 | { 988 | "name": "stdout", 989 | "output_type": "stream", 990 | "text": [ 991 | "[(2, u'cakeeater', u'222-222-2222')]\n", 992 | "[(2, u'cakeeater', u'222-222-2222', u'chocolate chip', 24, Decimal('12.00')), (2, u'cakeeater', u'222-222-2222', u'oatmeal raisin', 6, Decimal('6.00'))]\n", 993 | "[]\n", 994 | "[(2, u'cakeeater', u'222-222-2222')]\n", 995 | "[(2, u'cakeeater', u'222-222-2222', u'chocolate chip', 24, Decimal('12.00')), (2, u'cakeeater', u'222-222-2222', u'oatmeal raisin', 6, Decimal('6.00'))]\n" 996 | ] 997 | } 998 | ], 999 | "source": [ 1000 | "def get_orders_by_customer(cust_name, shipped=None, details=False):\n", 1001 | " query = session.query(Order.order_id, User.username, User.phone)\n", 1002 | " query = query.join(User)\n", 1003 | " if details:\n", 1004 | " query = query.add_columns(Cookie.cookie_name, LineItem.quantity,\n", 1005 | " LineItem.extended_cost)\n", 1006 | " query = query.join(LineItem).join(Cookie)\n", 1007 | " if shipped is not None:\n", 1008 | " query = query.filter(Order.shipped == shipped)\n", 1009 | " results = query.filter(User.username == cust_name).all()\n", 1010 | " return results\n", 1011 | "\n", 1012 | "print(get_orders_by_customer('cakeeater'))\n", 1013 | "\n", 1014 | "print(get_orders_by_customer('cakeeater', details=True))\n", 1015 | "\n", 1016 | "print(get_orders_by_customer('cakeeater', shipped=True))\n", 1017 | "\n", 1018 | "print(get_orders_by_customer('cakeeater', shipped=False))\n", 1019 | "\n", 1020 | "print(get_orders_by_customer('cakeeater', shipped=False, details=True))" 1021 | ] 1022 | }, 1023 | { 1024 | "cell_type": "code", 1025 | "execution_count": 44, 1026 | "metadata": { 1027 | "collapsed": false 1028 | }, 1029 | "outputs": [ 1030 | { 1031 | "name": "stdout", 1032 | "output_type": "stream", 1033 | "text": [ 1034 | "[User(username='cookiemon', email_address='mon@cookie.com', phone='111-111-1111', password='password')]\n" 1035 | ] 1036 | } 1037 | ], 1038 | "source": [ 1039 | "from sqlalchemy import text\n", 1040 | "query = session.query(User).filter(text(\"username='cookiemon'\"))\n", 1041 | "print(query.all())" 1042 | ] 1043 | } 1044 | ], 1045 | "metadata": { 1046 | "kernelspec": { 1047 | "display_name": "Python 2", 1048 | "language": "python", 1049 | "name": "python2" 1050 | }, 1051 | "language_info": { 1052 | "codemirror_mode": { 1053 | "name": "ipython", 1054 | "version": 2 1055 | }, 1056 | "file_extension": ".py", 1057 | "mimetype": "text/x-python", 1058 | "name": "python", 1059 | "nbconvert_exporter": "python", 1060 | "pygments_lexer": "ipython2", 1061 | "version": "2.7.10" 1062 | } 1063 | }, 1064 | "nbformat": 4, 1065 | "nbformat_minor": 0 1066 | } 1067 | -------------------------------------------------------------------------------- /ch08b.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from sqlalchemy import create_engine\n", 12 | "from sqlalchemy.orm import sessionmaker\n", 13 | "\n", 14 | "engine = create_engine('sqlite:///:memory:')\n", 15 | "\n", 16 | "Session = sessionmaker(bind=engine)\n", 17 | "\n", 18 | "session = Session()" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 2, 24 | "metadata": { 25 | "collapsed": false 26 | }, 27 | "outputs": [], 28 | "source": [ 29 | "from datetime import datetime\n", 30 | "\n", 31 | "from sqlalchemy import Table, Column, Integer, Numeric, String, DateTime, ForeignKey, Boolean, CheckConstraint\n", 32 | "from sqlalchemy.ext.declarative import declarative_base\n", 33 | "from sqlalchemy.orm import relationship, backref\n", 34 | "\n", 35 | "\n", 36 | "Base = declarative_base()\n", 37 | "\n", 38 | "\n", 39 | "class Cookie(Base):\n", 40 | " __tablename__ = 'cookies'\n", 41 | " __table_args__ = (CheckConstraint('quantity >= 0', name='quantity_positive'),)\n", 42 | "\n", 43 | " cookie_id = Column(Integer, primary_key=True)\n", 44 | " cookie_name = Column(String(50), index=True)\n", 45 | " cookie_recipe_url = Column(String(255))\n", 46 | " cookie_sku = Column(String(55))\n", 47 | " quantity = Column(Integer())\n", 48 | " unit_cost = Column(Numeric(12, 2))\n", 49 | " \n", 50 | " def __init__(self, name, recipe_url=None, sku=None, quantity=0, unit_cost=0.00):\n", 51 | " self.cookie_name = name\n", 52 | " self.cookie_recipe_url = recipe_url\n", 53 | " self.cookie_sku = sku\n", 54 | " self.quantity = quantity\n", 55 | " self.unit_cost = unit_cost\n", 56 | " \n", 57 | " def __repr__(self):\n", 58 | " return \"Cookie(cookie_name='{self.cookie_name}', \" \\\n", 59 | " \"cookie_recipe_url='{self.cookie_recipe_url}', \" \\\n", 60 | " \"cookie_sku='{self.cookie_sku}', \" \\\n", 61 | " \"quantity={self.quantity}, \" \\\n", 62 | " \"unit_cost={self.unit_cost})\".format(self=self)\n", 63 | " \n", 64 | " \n", 65 | "class User(Base):\n", 66 | " __tablename__ = 'users'\n", 67 | " \n", 68 | " user_id = Column(Integer(), primary_key=True)\n", 69 | " username = Column(String(15), nullable=False, unique=True)\n", 70 | " email_address = Column(String(255), nullable=False)\n", 71 | " phone = Column(String(20), nullable=False)\n", 72 | " password = Column(String(25), nullable=False)\n", 73 | " created_on = Column(DateTime(), default=datetime.now)\n", 74 | " updated_on = Column(DateTime(), default=datetime.now, onupdate=datetime.now)\n", 75 | " \n", 76 | " def __init__(self, username, email_address, phone, password):\n", 77 | " self.username = username\n", 78 | " self.email_address = email_address\n", 79 | " self.phone = phone\n", 80 | " self.password = password\n", 81 | " \n", 82 | " def __repr__(self):\n", 83 | " return \"User(username='{self.username}', \" \\\n", 84 | " \"email_address='{self.email_address}', \" \\\n", 85 | " \"phone='{self.phone}', \" \\\n", 86 | " \"password='{self.password}')\".format(self=self)\n", 87 | " \n", 88 | "\n", 89 | "class Order(Base):\n", 90 | " __tablename__ = 'orders'\n", 91 | " order_id = Column(Integer(), primary_key=True)\n", 92 | " user_id = Column(Integer(), ForeignKey('users.user_id'))\n", 93 | " shipped = Column(Boolean(), default=False)\n", 94 | " \n", 95 | " user = relationship(\"User\", backref=backref('orders', order_by=order_id))\n", 96 | " \n", 97 | " def __repr__(self):\n", 98 | " return \"Order(user_id={self.user_id}, \" \\\n", 99 | " \"shipped={self.shipped})\".format(self=self)\n", 100 | "\n", 101 | "\n", 102 | "class LineItem(Base):\n", 103 | " __tablename__ = 'line_items'\n", 104 | " line_item_id = Column(Integer(), primary_key=True)\n", 105 | " order_id = Column(Integer(), ForeignKey('orders.order_id'))\n", 106 | " cookie_id = Column(Integer(), ForeignKey('cookies.cookie_id'))\n", 107 | " quantity = Column(Integer())\n", 108 | " extended_cost = Column(Numeric(12, 2))\n", 109 | " \n", 110 | " order = relationship(\"Order\", backref=backref('line_items', order_by=line_item_id))\n", 111 | " cookie = relationship(\"Cookie\", uselist=False)\n", 112 | "\n", 113 | " def __repr__(self):\n", 114 | " return \"LineItems(order_id={self.order_id}, \" \\\n", 115 | " \"cookie_id={self.cookie_id}, \" \\\n", 116 | " \"quantity={self.quantity}, \" \\\n", 117 | " \"extended_cost={self.extended_cost})\".format(\n", 118 | " self=self) \n", 119 | " \n", 120 | "Base.metadata.create_all(engine)" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": 3, 126 | "metadata": { 127 | "collapsed": false 128 | }, 129 | "outputs": [], 130 | "source": [ 131 | "cookiemon = User('cookiemon', 'mon@cookie.com', '111-111-1111', 'password')\n", 132 | "cc = Cookie('chocolate chip', 'http://some.aweso.me/cookie/recipe.html', 'CC01', 12, 0.50)\n", 133 | "dcc = Cookie('dark chocolate chip',\n", 134 | " 'http://some.aweso.me/cookie/recipe_dark.html',\n", 135 | " 'CC02',\n", 136 | " 1,\n", 137 | " 0.75)\n", 138 | "session.add(cookiemon)\n", 139 | "session.add(cc)\n", 140 | "session.add(dcc)" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": 4, 146 | "metadata": { 147 | "collapsed": false 148 | }, 149 | "outputs": [ 150 | { 151 | "name": "stderr", 152 | "output_type": "stream", 153 | "text": [ 154 | "/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/sql/type_api.py:322: SAWarning: Dialect sqlite+pysqlite does *not* support Decimal objects natively, and SQLAlchemy must convert from floating point - rounding errors and other issues may occur. Please consider storing Decimal numbers as strings or integers on this platform for lossless storage.\n", 155 | " d[coltype] = rp = d['impl'].result_processor(dialect, coltype)\n" 156 | ] 157 | } 158 | ], 159 | "source": [ 160 | "o1 = Order()\n", 161 | "o1.user = cookiemon\n", 162 | "session.add(o1)\n", 163 | "\n", 164 | "line1 = LineItem(order=o1, cookie=cc, quantity=9, extended_cost=4.50)\n", 165 | "\n", 166 | "\n", 167 | "session.add(line1)\n", 168 | "session.commit()\n", 169 | "o2 = Order()\n", 170 | "o2.user = cookiemon\n", 171 | "session.add(o2)\n", 172 | "\n", 173 | "line1 = LineItem(order=o2, cookie=cc, quantity=2, extended_cost=1.50)\n", 174 | "line2 = LineItem(order=o2, cookie=dcc, quantity=9, extended_cost=6.75)\n", 175 | "\n", 176 | "\n", 177 | "session.add(line1)\n", 178 | "session.add(line2)\n", 179 | "session.commit()" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": 5, 185 | "metadata": { 186 | "collapsed": false 187 | }, 188 | "outputs": [], 189 | "source": [ 190 | "def ship_it(order_id):\n", 191 | " order = session.query(Order).get(order_id)\n", 192 | " for li in order.line_items:\n", 193 | " li.cookie.quantity = li.cookie.quantity - li.quantity\n", 194 | " session.add(li.cookie)\n", 195 | " order.shipped = True\n", 196 | " session.add(order)\n", 197 | " session.commit()\n", 198 | " print(\"shipped order ID: {}\".format(order_id))" 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": 6, 204 | "metadata": { 205 | "collapsed": false 206 | }, 207 | "outputs": [ 208 | { 209 | "name": "stdout", 210 | "output_type": "stream", 211 | "text": [ 212 | "shipped order ID: 1\n", 213 | "[(u'chocolate chip', 3), (u'dark chocolate chip', 1)]\n" 214 | ] 215 | } 216 | ], 217 | "source": [ 218 | "ship_it(1)\n", 219 | "print(session.query(Cookie.cookie_name, Cookie.quantity).all())" 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": 7, 225 | "metadata": { 226 | "collapsed": false 227 | }, 228 | "outputs": [ 229 | { 230 | "ename": "IntegrityError", 231 | "evalue": "(IntegrityError) CHECK constraint failed: quantity_positive u'UPDATE cookies SET quantity=? WHERE cookies.cookie_id = ?' (-8, 2)", 232 | "output_type": "error", 233 | "traceback": [ 234 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 235 | "\u001b[0;31mIntegrityError\u001b[0m Traceback (most recent call last)", 236 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mship_it\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 237 | "\u001b[0;32m\u001b[0m in \u001b[0;36mship_it\u001b[0;34m(order_id)\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0morder\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshipped\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mTrue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0msession\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0morder\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 8\u001b[0;31m \u001b[0msession\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcommit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 9\u001b[0m \u001b[0;32mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"shipped order ID: {}\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0morder_id\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 238 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/orm/session.pyc\u001b[0m in \u001b[0;36mcommit\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 786\u001b[0m \u001b[0;32mraise\u001b[0m \u001b[0msa_exc\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mInvalidRequestError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"No transaction is begun.\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 787\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 788\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransaction\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcommit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 789\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 790\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mprepare\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 239 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/orm/session.pyc\u001b[0m in \u001b[0;36mcommit\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 382\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_assert_active\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprepared_ok\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 383\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_state\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mPREPARED\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 384\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_prepare_impl\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 385\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 386\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_parent\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0mNone\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnested\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 240 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/orm/session.pyc\u001b[0m in \u001b[0;36m_prepare_impl\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 362\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msession\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_is_clean\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 363\u001b[0m \u001b[0;32mbreak\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 364\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msession\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mflush\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 365\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 366\u001b[0m raise exc.FlushError(\n", 241 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/orm/session.pyc\u001b[0m in \u001b[0;36mflush\u001b[0;34m(self, objects)\u001b[0m\n\u001b[1;32m 1983\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1984\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_flushing\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mTrue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1985\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_flush\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobjects\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1986\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1987\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_flushing\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mFalse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 242 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/orm/session.pyc\u001b[0m in \u001b[0;36m_flush\u001b[0;34m(self, objects)\u001b[0m\n\u001b[1;32m 2101\u001b[0m \u001b[0;32mexcept\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2102\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mutil\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msafe_reraise\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2103\u001b[0;31m \u001b[0mtransaction\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrollback\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_capture_exception\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2104\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2105\u001b[0m def is_modified(self, instance, include_collections=True,\n", 243 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.pyc\u001b[0m in \u001b[0;36m__exit__\u001b[0;34m(self, type_, value, traceback)\u001b[0m\n\u001b[1;32m 58\u001b[0m \u001b[0mexc_type\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mexc_value\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mexc_tb\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_exc_info\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 59\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_exc_info\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mNone\u001b[0m \u001b[0;31m# remove potential circular references\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 60\u001b[0;31m \u001b[0mcompat\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mreraise\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mexc_type\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mexc_value\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mexc_tb\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 61\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 62\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_exc_info\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mNone\u001b[0m \u001b[0;31m# remove potential circular references\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 244 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/orm/session.pyc\u001b[0m in \u001b[0;36m_flush\u001b[0;34m(self, objects)\u001b[0m\n\u001b[1;32m 2065\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_warn_on_events\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mTrue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2066\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2067\u001b[0;31m \u001b[0mflush_context\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2068\u001b[0m \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2069\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_warn_on_events\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mFalse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 245 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/orm/unitofwork.pyc\u001b[0m in \u001b[0;36mexecute\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 370\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdependencies\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 371\u001b[0m postsort_actions):\n\u001b[0;32m--> 372\u001b[0;31m \u001b[0mrec\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 373\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 374\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mfinalize_flush_changes\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 246 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/orm/unitofwork.pyc\u001b[0m in \u001b[0;36mexecute\u001b[0;34m(self, uow)\u001b[0m\n\u001b[1;32m 524\u001b[0m uow.states_for_mapper_hierarchy(\n\u001b[1;32m 525\u001b[0m self.mapper, False, False),\n\u001b[0;32m--> 526\u001b[0;31m \u001b[0muow\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 527\u001b[0m )\n\u001b[1;32m 528\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", 247 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/orm/persistence.pyc\u001b[0m in \u001b[0;36msave_obj\u001b[0;34m(base_mapper, states, uowtransaction, single)\u001b[0m\n\u001b[1;32m 58\u001b[0m _emit_update_statements(base_mapper, uowtransaction,\n\u001b[1;32m 59\u001b[0m \u001b[0mcached_connections\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 60\u001b[0;31m mapper, table, update)\n\u001b[0m\u001b[1;32m 61\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 62\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0minsert\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 248 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/orm/persistence.pyc\u001b[0m in \u001b[0;36m_emit_update_statements\u001b[0;34m(base_mapper, uowtransaction, cached_connections, mapper, table, update)\u001b[0m\n\u001b[1;32m 516\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 517\u001b[0m \u001b[0mc\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcached_connections\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mconnection\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;31m\\\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 518\u001b[0;31m \u001b[0mexecute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstatement\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 519\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 520\u001b[0m _postfetch(\n", 249 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc\u001b[0m in \u001b[0;36mexecute\u001b[0;34m(self, object, *multiparams, **params)\u001b[0m\n\u001b[1;32m 839\u001b[0m type(object))\n\u001b[1;32m 840\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 841\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mmeth\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmultiparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 842\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 843\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_execute_function\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfunc\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmultiparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 250 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/sql/elements.pyc\u001b[0m in \u001b[0;36m_execute_on_connection\u001b[0;34m(self, connection, multiparams, params)\u001b[0m\n\u001b[1;32m 320\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 321\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_execute_on_connection\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mconnection\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmultiparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 322\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mconnection\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_execute_clauseelement\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmultiparams\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 323\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 324\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0munique_params\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0moptionaldict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 251 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc\u001b[0m in \u001b[0;36m_execute_clauseelement\u001b[0;34m(self, elem, multiparams, params)\u001b[0m\n\u001b[1;32m 936\u001b[0m \u001b[0mcompiled_sql\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 937\u001b[0m \u001b[0mdistilled_params\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 938\u001b[0;31m \u001b[0mcompiled_sql\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdistilled_params\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 939\u001b[0m )\n\u001b[1;32m 940\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_has_events\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mengine\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_has_events\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 252 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc\u001b[0m in \u001b[0;36m_execute_context\u001b[0;34m(self, dialect, constructor, statement, parameters, *args)\u001b[0m\n\u001b[1;32m 1068\u001b[0m \u001b[0mparameters\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1069\u001b[0m \u001b[0mcursor\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1070\u001b[0;31m context)\n\u001b[0m\u001b[1;32m 1071\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1072\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_has_events\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mengine\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_has_events\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 253 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc\u001b[0m in \u001b[0;36m_handle_dbapi_exception\u001b[0;34m(self, e, statement, parameters, cursor, context)\u001b[0m\n\u001b[1;32m 1269\u001b[0m util.raise_from_cause(\n\u001b[1;32m 1270\u001b[0m \u001b[0msqlalchemy_exception\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1271\u001b[0;31m \u001b[0mexc_info\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1272\u001b[0m )\n\u001b[1;32m 1273\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 254 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/util/compat.pyc\u001b[0m in \u001b[0;36mraise_from_cause\u001b[0;34m(exception, exc_info)\u001b[0m\n\u001b[1;32m 197\u001b[0m \u001b[0mexc_info\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msys\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexc_info\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 198\u001b[0m \u001b[0mexc_type\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mexc_value\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mexc_tb\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mexc_info\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 199\u001b[0;31m \u001b[0mreraise\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtype\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mexception\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mexception\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtb\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mexc_tb\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 200\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 201\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mpy3k\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 255 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/engine/base.pyc\u001b[0m in \u001b[0;36m_execute_context\u001b[0;34m(self, dialect, constructor, statement, parameters, *args)\u001b[0m\n\u001b[1;32m 1061\u001b[0m \u001b[0mstatement\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1062\u001b[0m \u001b[0mparameters\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1063\u001b[0;31m context)\n\u001b[0m\u001b[1;32m 1064\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0mException\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1065\u001b[0m self._handle_dbapi_exception(\n", 256 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/engine/default.pyc\u001b[0m in \u001b[0;36mdo_execute\u001b[0;34m(self, cursor, statement, parameters, context)\u001b[0m\n\u001b[1;32m 440\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 441\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdo_execute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcursor\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstatement\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparameters\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 442\u001b[0;31m \u001b[0mcursor\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstatement\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mparameters\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 443\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 444\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mdo_execute_no_params\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcursor\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mstatement\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcontext\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 257 | "\u001b[0;31mIntegrityError\u001b[0m: (IntegrityError) CHECK constraint failed: quantity_positive u'UPDATE cookies SET quantity=? WHERE cookies.cookie_id = ?' (-8, 2)" 258 | ] 259 | } 260 | ], 261 | "source": [ 262 | "ship_it(2)" 263 | ] 264 | }, 265 | { 266 | "cell_type": "code", 267 | "execution_count": 8, 268 | "metadata": { 269 | "collapsed": false 270 | }, 271 | "outputs": [ 272 | { 273 | "ename": "InvalidRequestError", 274 | "evalue": "This Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback(). Original exception was: (IntegrityError) CHECK constraint failed: quantity_positive u'UPDATE cookies SET quantity=? WHERE cookies.cookie_id = ?' (-8, 2)", 275 | "output_type": "error", 276 | "traceback": [ 277 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 278 | "\u001b[0;31mInvalidRequestError\u001b[0m Traceback (most recent call last)", 279 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msession\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquery\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mCookie\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcookie_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mCookie\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mquantity\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mall\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 280 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/orm/query.pyc\u001b[0m in \u001b[0;36mall\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 2321\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2322\u001b[0m \"\"\"\n\u001b[0;32m-> 2323\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2324\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2325\u001b[0m \u001b[0;34m@\u001b[0m\u001b[0m_generative\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_no_clauseelement_condition\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 281 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/orm/query.pyc\u001b[0m in \u001b[0;36m__iter__\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 2439\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_autoflush\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_populate_existing\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2440\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msession\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_autoflush\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2441\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_execute_and_instances\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcontext\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2442\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2443\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_connection_from_session\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 282 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/orm/query.pyc\u001b[0m in \u001b[0;36m_execute_and_instances\u001b[0;34m(self, querycontext)\u001b[0m\n\u001b[1;32m 2452\u001b[0m \u001b[0mmapper\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_mapper_zero_or_none\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2453\u001b[0m \u001b[0mclause\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mquerycontext\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstatement\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2454\u001b[0;31m close_with_result=True)\n\u001b[0m\u001b[1;32m 2455\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2456\u001b[0m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mconn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mquerycontext\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstatement\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_params\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 283 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/orm/query.pyc\u001b[0m in \u001b[0;36m_connection_from_session\u001b[0;34m(self, **kw)\u001b[0m\n\u001b[1;32m 2443\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_connection_from_session\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2444\u001b[0m conn = self.session.connection(\n\u001b[0;32m-> 2445\u001b[0;31m **kw)\n\u001b[0m\u001b[1;32m 2446\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_execution_options\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2447\u001b[0m \u001b[0mconn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mconn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mexecution_options\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_execution_options\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 284 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/orm/session.pyc\u001b[0m in \u001b[0;36mconnection\u001b[0;34m(self, mapper, clause, bind, close_with_result, execution_options, **kw)\u001b[0m\n\u001b[1;32m 878\u001b[0m return self._connection_for_bind(bind,\n\u001b[1;32m 879\u001b[0m \u001b[0mclose_with_result\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mclose_with_result\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 880\u001b[0;31m execution_options=execution_options)\n\u001b[0m\u001b[1;32m 881\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 882\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_connection_for_bind\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mengine\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mexecution_options\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 285 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/orm/session.pyc\u001b[0m in \u001b[0;36m_connection_for_bind\u001b[0;34m(self, engine, execution_options, **kw)\u001b[0m\n\u001b[1;32m 883\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtransaction\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 884\u001b[0m return self.transaction._connection_for_bind(\n\u001b[0;32m--> 885\u001b[0;31m engine, execution_options)\n\u001b[0m\u001b[1;32m 886\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 887\u001b[0m \u001b[0mconn\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mengine\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcontextual_connect\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0mkw\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 286 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/orm/session.pyc\u001b[0m in \u001b[0;36m_connection_for_bind\u001b[0;34m(self, bind, execution_options)\u001b[0m\n\u001b[1;32m 303\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 304\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_connection_for_bind\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbind\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mexecution_options\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 305\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_assert_active\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 306\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 307\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mbind\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_connections\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 287 | "\u001b[0;32m/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/orm/session.pyc\u001b[0m in \u001b[0;36m_assert_active\u001b[0;34m(self, prepared_ok, rollback_ok, deactive_ok, closed_msg)\u001b[0m\n\u001b[1;32m 212\u001b[0m \u001b[0;34m\"first issue Session.rollback().\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 213\u001b[0m \u001b[0;34m\" Original exception was: %s\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 214\u001b[0;31m \u001b[0;34m%\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_rollback_exception\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 215\u001b[0m )\n\u001b[1;32m 216\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mdeactive_ok\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 288 | "\u001b[0;31mInvalidRequestError\u001b[0m: This Session's transaction has been rolled back due to a previous exception during flush. To begin a new transaction with this Session, first issue Session.rollback(). Original exception was: (IntegrityError) CHECK constraint failed: quantity_positive u'UPDATE cookies SET quantity=? WHERE cookies.cookie_id = ?' (-8, 2)" 289 | ] 290 | } 291 | ], 292 | "source": [ 293 | "print(session.query(Cookie.cookie_name, Cookie.quantity).all())" 294 | ] 295 | }, 296 | { 297 | "cell_type": "code", 298 | "execution_count": 9, 299 | "metadata": { 300 | "collapsed": false 301 | }, 302 | "outputs": [ 303 | { 304 | "name": "stdout", 305 | "output_type": "stream", 306 | "text": [ 307 | "[(u'chocolate chip', 3), (u'dark chocolate chip', 1)]\n" 308 | ] 309 | } 310 | ], 311 | "source": [ 312 | "session.rollback()\n", 313 | "print(session.query(Cookie.cookie_name, Cookie.quantity).all())" 314 | ] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": 12, 319 | "metadata": { 320 | "collapsed": true 321 | }, 322 | "outputs": [], 323 | "source": [ 324 | "from sqlalchemy.exc import IntegrityError\n", 325 | "def ship_it(order_id):\n", 326 | " order = session.query(Order).get(order_id)\n", 327 | " for li in order.line_items:\n", 328 | " li.cookie.quantity = li.cookie.quantity - li.quantity\n", 329 | " session.add(li.cookie)\n", 330 | " order.shipped = True\n", 331 | " session.add(order)\n", 332 | " try:\n", 333 | " session.commit()\n", 334 | " print(\"shipped order ID: {}\".format(order_id))\n", 335 | " except IntegrityError as error:\n", 336 | " print('ERROR: {!s}'.format(error.orig))\n", 337 | " session.rollback()" 338 | ] 339 | }, 340 | { 341 | "cell_type": "code", 342 | "execution_count": 13, 343 | "metadata": { 344 | "collapsed": false 345 | }, 346 | "outputs": [ 347 | { 348 | "name": "stdout", 349 | "output_type": "stream", 350 | "text": [ 351 | "ERROR: CHECK constraint failed: quantity_positive\n" 352 | ] 353 | } 354 | ], 355 | "source": [ 356 | "ship_it(2)" 357 | ] 358 | }, 359 | { 360 | "cell_type": "code", 361 | "execution_count": null, 362 | "metadata": { 363 | "collapsed": true 364 | }, 365 | "outputs": [], 366 | "source": [] 367 | } 368 | ], 369 | "metadata": { 370 | "kernelspec": { 371 | "display_name": "Python 2", 372 | "language": "python", 373 | "name": "python2" 374 | }, 375 | "language_info": { 376 | "codemirror_mode": { 377 | "name": "ipython", 378 | "version": 2 379 | }, 380 | "file_extension": ".py", 381 | "mimetype": "text/x-python", 382 | "name": "python", 383 | "nbconvert_exporter": "python", 384 | "pygments_lexer": "ipython2", 385 | "version": "2.7.10" 386 | } 387 | }, 388 | "nbformat": 4, 389 | "nbformat_minor": 0 390 | } 391 | -------------------------------------------------------------------------------- /ch09/app.py: -------------------------------------------------------------------------------- 1 | from db import Cookie, LineItem, Order, User, dal 2 | 3 | 4 | def get_orders_by_customer(cust_name, shipped=None, details=False): 5 | query = dal.session.query(Order.order_id, User.username, User.phone) 6 | query = query.join(User) 7 | if details: 8 | query = query.add_columns(Cookie.cookie_name, LineItem.quantity, 9 | LineItem.extended_cost) 10 | query = query.join(LineItem).join(Cookie) 11 | if shipped is not None: 12 | query = query.filter(Order.shipped == shipped) 13 | results = query.filter(User.username == cust_name).all() 14 | return results 15 | -------------------------------------------------------------------------------- /ch09/db.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | from sqlalchemy import (Column, Integer, Numeric, String, DateTime, ForeignKey, 4 | Boolean, create_engine) 5 | from sqlalchemy.ext.declarative import declarative_base 6 | from sqlalchemy.orm import relationship, backref, sessionmaker 7 | 8 | 9 | conn_string = 'some conn string' 10 | Base = declarative_base() 11 | 12 | 13 | class Cookie(Base): 14 | __tablename__ = 'cookies' 15 | 16 | cookie_id = Column(Integer, primary_key=True) 17 | cookie_name = Column(String(50), index=True) 18 | cookie_recipe_url = Column(String(255)) 19 | cookie_sku = Column(String(55)) 20 | quantity = Column(Integer()) 21 | unit_cost = Column(Numeric(12, 2)) 22 | 23 | def __repr__(self): 24 | return "Cookie(cookie_name='{self.cookie_name}', " \ 25 | "cookie_recipe_url='{self.cookie_recipe_url}', " \ 26 | "cookie_sku='{self.cookie_sku}', " \ 27 | "quantity={self.quantity}, " \ 28 | "unit_cost={self.unit_cost})".format(self=self) 29 | 30 | 31 | class User(Base): 32 | __tablename__ = 'users' 33 | 34 | user_id = Column(Integer(), primary_key=True) 35 | username = Column(String(15), nullable=False, unique=True) 36 | email_address = Column(String(255), nullable=False) 37 | phone = Column(String(20), nullable=False) 38 | password = Column(String(25), nullable=False) 39 | created_on = Column(DateTime(), default=datetime.now) 40 | updated_on = Column(DateTime(), default=datetime.now, onupdate=datetime.now) 41 | 42 | def __repr__(self): 43 | return "User(username='{self.username}', " \ 44 | "email_address='{self.email_address}', " \ 45 | "phone='{self.phone}', " \ 46 | "password='{self.password}')".format(self=self) 47 | 48 | 49 | class Order(Base): 50 | __tablename__ = 'orders' 51 | order_id = Column(Integer(), primary_key=True) 52 | user_id = Column(Integer(), ForeignKey('users.user_id')) 53 | shipped = Column(Boolean(), default=False) 54 | 55 | user = relationship("User", backref=backref('orders', order_by=order_id)) 56 | 57 | def __repr__(self): 58 | return "Order(user_id={self.user_id}, " \ 59 | "shipped={self.shipped})".format(self=self) 60 | 61 | 62 | class LineItem(Base): 63 | __tablename__ = 'line_items' 64 | line_item_id = Column(Integer(), primary_key=True) 65 | order_id = Column(Integer(), ForeignKey('orders.order_id')) 66 | cookie_id = Column(Integer(), ForeignKey('cookies.cookie_id')) 67 | quantity = Column(Integer()) 68 | extended_cost = Column(Numeric(12, 2)) 69 | 70 | order = relationship("Order", backref=backref('line_items', 71 | order_by=line_item_id)) 72 | cookie = relationship("Cookie", uselist=False) 73 | 74 | def __repr__(self): 75 | return "LineItems(order_id={self.order_id}, " \ 76 | "cookie_id={self.cookie_id}, " \ 77 | "quantity={self.quantity}, " \ 78 | "extended_cost={self.extended_cost})".format( 79 | self=self) 80 | 81 | 82 | class DataAccessLayer: 83 | 84 | def __init__(self): 85 | self.engine = None 86 | self.session = None 87 | self.conn_string = conn_string 88 | 89 | def connect(self): 90 | self.engine = create_engine(self.conn_string) 91 | Base.metadata.create_all(self.engine) 92 | self.Session = sessionmaker(bind=self.engine) 93 | 94 | 95 | dal = DataAccessLayer() 96 | 97 | 98 | def prep_db(session): 99 | c1 = Cookie(cookie_name='dark chocolate chip', 100 | cookie_recipe_url='http://some.aweso.me/cookie/dark_cc.html', 101 | cookie_sku='CC02', 102 | quantity=1, 103 | unit_cost=0.75) 104 | c2 = Cookie(cookie_name='peanut butter', 105 | cookie_recipe_url='http://some.aweso.me/cookie/peanut.html', 106 | cookie_sku='PB01', 107 | quantity=24, 108 | unit_cost=0.25) 109 | c3 = Cookie(cookie_name='oatmeal raisin', 110 | cookie_recipe_url='http://some.okay.me/cookie/raisin.html', 111 | cookie_sku='EWW01', 112 | quantity=100, 113 | unit_cost=1.00) 114 | session.bulk_save_objects([c1, c2, c3]) 115 | session.commit() 116 | 117 | cookiemon = User(username='cookiemon', 118 | email_address='mon@cookie.com', 119 | phone='111-111-1111', 120 | password='password') 121 | cakeeater = User(username='cakeeater', 122 | email_address='cakeeater@cake.com', 123 | phone='222-222-2222', 124 | password='password') 125 | pieperson = User(username='pieperson', 126 | email_address='person@pie.com', 127 | phone='333-333-3333', 128 | password='password') 129 | session.add(cookiemon) 130 | session.add(cakeeater) 131 | session.add(pieperson) 132 | session.commit() 133 | 134 | o1 = Order() 135 | o1.user = cookiemon 136 | session.add(o1) 137 | 138 | line1 = LineItem(cookie=c1, quantity=2, extended_cost=1.00) 139 | 140 | line2 = LineItem(cookie=c3, quantity=12, extended_cost=3.00) 141 | 142 | o1.line_items.append(line1) 143 | o1.line_items.append(line2) 144 | session.commit() 145 | 146 | o2 = Order() 147 | o2.user = cakeeater 148 | 149 | line1 = LineItem(cookie=c1, quantity=24, extended_cost=12.00) 150 | line2 = LineItem(cookie=c3, quantity=6, extended_cost=6.00) 151 | 152 | o2.line_items.append(line1) 153 | o2.line_items.append(line2) 154 | 155 | session.add(o2) 156 | session.commit() 157 | -------------------------------------------------------------------------------- /ch09/test_app.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from decimal import Decimal 4 | 5 | from db import prep_db, dal 6 | 7 | from app import get_orders_by_customer 8 | 9 | 10 | class TestApp(unittest.TestCase): 11 | cookie_orders = [(1, u'cookiemon', u'111-111-1111')] 12 | cookie_details = [ 13 | (1, u'cookiemon', u'111-111-1111', 14 | u'dark chocolate chip', 2, Decimal('1.00')), 15 | (1, u'cookiemon', u'111-111-1111', 16 | u'oatmeal raisin', 12, Decimal('3.00'))] 17 | 18 | @classmethod 19 | def setUpClass(cls): 20 | dal.conn_string = 'sqlite:///:memory:' 21 | dal.connect() 22 | dal.session = dal.Session() 23 | prep_db(dal.session) 24 | dal.session.close() 25 | 26 | def setUp(self): 27 | dal.session = dal.Session() 28 | 29 | def tearDown(self): 30 | dal.session.rollback() 31 | dal.session.close() 32 | 33 | def test_orders_by_customer_blank(self): 34 | results = get_orders_by_customer('') 35 | self.assertEqual(results, []) 36 | 37 | def test_orders_by_customer_blank_shipped(self): 38 | results = get_orders_by_customer('', True) 39 | self.assertEqual(results, []) 40 | 41 | def test_orders_by_customer_blank_notshipped(self): 42 | results = get_orders_by_customer('', False) 43 | self.assertEqual(results, []) 44 | 45 | def test_orders_by_customer_blank_details(self): 46 | results = get_orders_by_customer('', details=True) 47 | self.assertEqual(results, []) 48 | 49 | def test_orders_by_customer_blank_shipped_details(self): 50 | results = get_orders_by_customer('', True, True) 51 | self.assertEqual(results, []) 52 | 53 | def test_orders_by_customer_blank_notshipped_details(self): 54 | results = get_orders_by_customer('', False, True) 55 | self.assertEqual(results, []) 56 | 57 | def test_orders_by_customer_bad_cust(self): 58 | results = get_orders_by_customer('bad name') 59 | self.assertEqual(results, []) 60 | 61 | def test_orders_by_customer_bad_cust_shipped(self): 62 | results = get_orders_by_customer('bad name', True) 63 | self.assertEqual(results, []) 64 | 65 | def test_orders_by_customer_bad_cust_notshipped(self): 66 | results = get_orders_by_customer('bad name', False) 67 | self.assertEqual(results, []) 68 | 69 | def test_orders_by_customer_bad_cust_details(self): 70 | results = get_orders_by_customer('bad name', details=True) 71 | self.assertEqual(results, []) 72 | 73 | def test_orders_by_customer_bad_cust_shipped_details(self): 74 | results = get_orders_by_customer('bad name', True, True) 75 | self.assertEqual(results, []) 76 | 77 | def test_orders_by_customer_bad_cust_notshipped_details(self): 78 | results = get_orders_by_customer('bad name', False, True) 79 | self.assertEqual(results, []) 80 | 81 | def test_orders_by_customer(self): 82 | results = get_orders_by_customer('cookiemon') 83 | self.assertEqual(results, self.cookie_orders) 84 | 85 | def test_orders_by_customer_shipped_only(self): 86 | results = get_orders_by_customer('cookiemon', True) 87 | self.assertEqual(results, []) 88 | 89 | def test_orders_by_customer_unshipped_only(self): 90 | results = get_orders_by_customer('cookiemon', False) 91 | self.assertEqual(results, self.cookie_orders) 92 | 93 | def test_orders_by_customer_with_details(self): 94 | results = get_orders_by_customer('cookiemon', details=True) 95 | self.assertEqual(results, self.cookie_details) 96 | 97 | def test_orders_by_customer_shipped_only_with_details(self): 98 | results = get_orders_by_customer('cookiemon', True, True) 99 | self.assertEqual(results, []) 100 | 101 | def test_orders_by_customer_unshipped_only_details(self): 102 | results = get_orders_by_customer('cookiemon', False, True) 103 | self.assertEqual(results, self.cookie_details) 104 | -------------------------------------------------------------------------------- /ch09/test_mock_app.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from decimal import Decimal 3 | 4 | import mock 5 | 6 | from app import get_orders_by_customer 7 | 8 | 9 | class TestApp(unittest.TestCase): 10 | cookie_orders = [(1, u'cookiemon', u'111-111-1111')] 11 | cookie_details = [ 12 | (1, u'cookiemon', u'111-111-1111', 13 | u'dark chocolate chip', 2, Decimal('1.00')), 14 | (1, u'cookiemon', u'111-111-1111', 15 | u'oatmeal raisin', 12, Decimal('3.00'))] 16 | 17 | @mock.patch('app.dal.session') 18 | def test_orders_by_customer_blank(self, mock_dal): 19 | mock_dal.query.return_value.join.return_value.filter.return_value. \ 20 | all.return_value = [] 21 | results = get_orders_by_customer('') 22 | self.assertEqual(results, []) 23 | 24 | @mock.patch('app.dal.session') 25 | def test_orders_by_customer_blank_shipped(self, mock_dal): 26 | mock_dal.query.return_value.join.return_value.filter.return_value. \ 27 | filter.return_value.all.return_value = [] 28 | results = get_orders_by_customer('', True) 29 | self.assertEqual(results, []) 30 | 31 | @mock.patch('app.dal.session') 32 | def test_orders_by_customer(self, mock_dal): 33 | mock_dal.query.return_value.join.return_value.filter.return_value. \ 34 | all.return_value = self.cookie_orders 35 | results = get_orders_by_customer('cookiemon') 36 | self.assertEqual(results, self.cookie_orders) 37 | -------------------------------------------------------------------------------- /ch10/.ipynb_checkpoints/ch10-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from sqlalchemy.ext.automap import automap_base\n", 12 | "from sqlalchemy.orm import Session\n", 13 | "from sqlalchemy import create_engine\n", 14 | "\n", 15 | "Base = automap_base()" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": 2, 21 | "metadata": { 22 | "collapsed": true 23 | }, 24 | "outputs": [], 25 | "source": [ 26 | "from sqlalchemy import create_engine\n", 27 | "\n", 28 | "engine = create_engine('sqlite:///Chinook_Sqlite.sqlite')" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 3, 34 | "metadata": { 35 | "collapsed": true 36 | }, 37 | "outputs": [], 38 | "source": [ 39 | "Base.prepare(engine, reflect=True)" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 4, 45 | "metadata": { 46 | "collapsed": false 47 | }, 48 | "outputs": [ 49 | { 50 | "data": { 51 | "text/plain": [ 52 | "['Album',\n", 53 | " 'Customer',\n", 54 | " 'Playlist',\n", 55 | " 'Artist',\n", 56 | " 'Track',\n", 57 | " 'Employee',\n", 58 | " 'MediaType',\n", 59 | " 'InvoiceLine',\n", 60 | " 'Invoice',\n", 61 | " 'Genre']" 62 | ] 63 | }, 64 | "execution_count": 4, 65 | "metadata": {}, 66 | "output_type": "execute_result" 67 | } 68 | ], 69 | "source": [ 70 | "Base.classes.keys()" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": 5, 76 | "metadata": { 77 | "collapsed": true 78 | }, 79 | "outputs": [], 80 | "source": [ 81 | "Artist = Base.classes.Artist\n", 82 | "Album = Base.classes.Album" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": 6, 88 | "metadata": { 89 | "collapsed": false 90 | }, 91 | "outputs": [ 92 | { 93 | "name": "stdout", 94 | "output_type": "stream", 95 | "text": [ 96 | "(1, u'AC/DC')\n", 97 | "(2, u'Accept')\n", 98 | "(3, u'Aerosmith')\n", 99 | "(4, u'Alanis Morissette')\n", 100 | "(5, u'Alice In Chains')\n", 101 | "(6, u'Ant\\xf4nio Carlos Jobim')\n", 102 | "(7, u'Apocalyptica')\n", 103 | "(8, u'Audioslave')\n", 104 | "(9, u'BackBeat')\n", 105 | "(10, u'Billy Cobham')\n" 106 | ] 107 | } 108 | ], 109 | "source": [ 110 | "from sqlalchemy.orm import Session\n", 111 | "\n", 112 | "session = Session(engine)\n", 113 | "for artist in session.query(Artist).limit(10):\n", 114 | " print(artist.ArtistId, artist.Name)" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": 7, 120 | "metadata": { 121 | "collapsed": false 122 | }, 123 | "outputs": [ 124 | { 125 | "name": "stdout", 126 | "output_type": "stream", 127 | "text": [ 128 | "AC/DC - For Those About To Rock We Salute You\n", 129 | "AC/DC - Let There Be Rock\n" 130 | ] 131 | } 132 | ], 133 | "source": [ 134 | "artist = session.query(Artist).first()\n", 135 | "for album in artist.album_collection:\n", 136 | " print('{} - {}'.format(artist.Name, album.Title))" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": null, 142 | "metadata": { 143 | "collapsed": true 144 | }, 145 | "outputs": [], 146 | "source": [] 147 | } 148 | ], 149 | "metadata": { 150 | "kernelspec": { 151 | "display_name": "Python 2", 152 | "language": "python", 153 | "name": "python2" 154 | }, 155 | "language_info": { 156 | "codemirror_mode": { 157 | "name": "ipython", 158 | "version": 2 159 | }, 160 | "file_extension": ".py", 161 | "mimetype": "text/x-python", 162 | "name": "python", 163 | "nbconvert_exporter": "python", 164 | "pygments_lexer": "ipython2", 165 | "version": "2.7.10" 166 | } 167 | }, 168 | "nbformat": 4, 169 | "nbformat_minor": 0 170 | } 171 | -------------------------------------------------------------------------------- /ch10/Chinook_Sqlite.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreillymedia/essential-sqlalchemy-2e/bdfed6cf7f0b36b20b4850f5fc0c489947cbea5f/ch10/Chinook_Sqlite.sqlite -------------------------------------------------------------------------------- /ch10/ch10.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from sqlalchemy.ext.automap import automap_base\n", 12 | "from sqlalchemy.orm import Session\n", 13 | "from sqlalchemy import create_engine\n", 14 | "\n", 15 | "Base = automap_base()" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": 2, 21 | "metadata": { 22 | "collapsed": true 23 | }, 24 | "outputs": [], 25 | "source": [ 26 | "from sqlalchemy import create_engine\n", 27 | "\n", 28 | "engine = create_engine('sqlite:///Chinook_Sqlite.sqlite')" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 3, 34 | "metadata": { 35 | "collapsed": true 36 | }, 37 | "outputs": [], 38 | "source": [ 39 | "Base.prepare(engine, reflect=True)" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": 4, 45 | "metadata": { 46 | "collapsed": false 47 | }, 48 | "outputs": [ 49 | { 50 | "data": { 51 | "text/plain": [ 52 | "['Album',\n", 53 | " 'Customer',\n", 54 | " 'Playlist',\n", 55 | " 'Artist',\n", 56 | " 'Track',\n", 57 | " 'Employee',\n", 58 | " 'MediaType',\n", 59 | " 'InvoiceLine',\n", 60 | " 'Invoice',\n", 61 | " 'Genre']" 62 | ] 63 | }, 64 | "execution_count": 4, 65 | "metadata": {}, 66 | "output_type": "execute_result" 67 | } 68 | ], 69 | "source": [ 70 | "Base.classes.keys()" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": 5, 76 | "metadata": { 77 | "collapsed": true 78 | }, 79 | "outputs": [], 80 | "source": [ 81 | "Artist = Base.classes.Artist\n", 82 | "Album = Base.classes.Album" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": 6, 88 | "metadata": { 89 | "collapsed": false 90 | }, 91 | "outputs": [ 92 | { 93 | "name": "stdout", 94 | "output_type": "stream", 95 | "text": [ 96 | "(1, u'AC/DC')\n", 97 | "(2, u'Accept')\n", 98 | "(3, u'Aerosmith')\n", 99 | "(4, u'Alanis Morissette')\n", 100 | "(5, u'Alice In Chains')\n", 101 | "(6, u'Ant\\xf4nio Carlos Jobim')\n", 102 | "(7, u'Apocalyptica')\n", 103 | "(8, u'Audioslave')\n", 104 | "(9, u'BackBeat')\n", 105 | "(10, u'Billy Cobham')\n" 106 | ] 107 | } 108 | ], 109 | "source": [ 110 | "from sqlalchemy.orm import Session\n", 111 | "\n", 112 | "session = Session(engine)\n", 113 | "for artist in session.query(Artist).limit(10):\n", 114 | " print(artist.ArtistId, artist.Name)" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": 7, 120 | "metadata": { 121 | "collapsed": false 122 | }, 123 | "outputs": [ 124 | { 125 | "name": "stdout", 126 | "output_type": "stream", 127 | "text": [ 128 | "AC/DC - For Those About To Rock We Salute You\n", 129 | "AC/DC - Let There Be Rock\n" 130 | ] 131 | } 132 | ], 133 | "source": [ 134 | "artist = session.query(Artist).first()\n", 135 | "for album in artist.album_collection:\n", 136 | " print('{} - {}'.format(artist.Name, album.Title))" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": null, 142 | "metadata": { 143 | "collapsed": true 144 | }, 145 | "outputs": [], 146 | "source": [] 147 | } 148 | ], 149 | "metadata": { 150 | "kernelspec": { 151 | "display_name": "Python 2", 152 | "language": "python", 153 | "name": "python2" 154 | }, 155 | "language_info": { 156 | "codemirror_mode": { 157 | "name": "ipython", 158 | "version": 2 159 | }, 160 | "file_extension": ".py", 161 | "mimetype": "text/x-python", 162 | "name": "python", 163 | "nbconvert_exporter": "python", 164 | "pygments_lexer": "ipython2", 165 | "version": "2.7.10" 166 | } 167 | }, 168 | "nbformat": 4, 169 | "nbformat_minor": 0 170 | } 171 | -------------------------------------------------------------------------------- /ch11/alembic.ini: -------------------------------------------------------------------------------- 1 | # A generic, single database configuration. 2 | 3 | [alembic] 4 | # path to migration scripts 5 | script_location = alembic 6 | 7 | # template used to generate migration files 8 | # file_template = %%(rev)s_%%(slug)s 9 | 10 | # max length of characters to apply to the 11 | # "slug" field 12 | #truncate_slug_length = 40 13 | 14 | # set to 'true' to run the environment during 15 | # the 'revision' command, regardless of autogenerate 16 | # revision_environment = false 17 | 18 | # set to 'true' to allow .pyc and .pyo files without 19 | # a source .py file to be detected as revisions in the 20 | # versions/ directory 21 | # sourceless = false 22 | 23 | # version location specification; this defaults 24 | # to alembic/versions. When using multiple version 25 | # directories, initial revisions must be specified with --version-path 26 | # version_locations = %(here)s/bar %(here)s/bat alembic/versions 27 | 28 | # the output encoding used when revision files 29 | # are written from script.py.mako 30 | # output_encoding = utf-8 31 | 32 | sqlalchemy.url = sqlite:///alembictest.db 33 | 34 | 35 | # Logging configuration 36 | [loggers] 37 | keys = root,sqlalchemy,alembic 38 | 39 | [handlers] 40 | keys = console 41 | 42 | [formatters] 43 | keys = generic 44 | 45 | [logger_root] 46 | level = WARN 47 | handlers = console 48 | qualname = 49 | 50 | [logger_sqlalchemy] 51 | level = WARN 52 | handlers = 53 | qualname = sqlalchemy.engine 54 | 55 | [logger_alembic] 56 | level = INFO 57 | handlers = 58 | qualname = alembic 59 | 60 | [handler_console] 61 | class = StreamHandler 62 | args = (sys.stderr,) 63 | level = NOTSET 64 | formatter = generic 65 | 66 | [formatter_generic] 67 | format = %(levelname)-5.5s [%(name)s] %(message)s 68 | datefmt = %H:%M:%S 69 | -------------------------------------------------------------------------------- /ch11/alembic/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /ch11/alembic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreillymedia/essential-sqlalchemy-2e/bdfed6cf7f0b36b20b4850f5fc0c489947cbea5f/ch11/alembic/__init__.py -------------------------------------------------------------------------------- /ch11/alembic/env.py: -------------------------------------------------------------------------------- 1 | from __future__ import with_statement 2 | import os 3 | import sys 4 | 5 | from sqlalchemy import engine_from_config, pool 6 | 7 | from alembic import context 8 | from logging.config import fileConfig 9 | 10 | sys.path.append(os.getcwd()) 11 | 12 | # this is the Alembic Config object, which provides 13 | # access to the values within the .ini file in use. 14 | config = context.config 15 | 16 | # Interpret the config file for Python logging. 17 | # This line sets up loggers basically. 18 | fileConfig(config.config_file_name) 19 | 20 | # add your model's MetaData object here 21 | # for 'autogenerate' support 22 | # from myapp import mymodel 23 | # target_metadata = mymodel.Base.metadata 24 | from app.db import Base 25 | target_metadata = Base.metadata 26 | 27 | # other values from the config, defined by the needs of env.py, 28 | # can be acquired: 29 | # my_important_option = config.get_main_option("my_important_option") 30 | # ... etc. 31 | 32 | 33 | def run_migrations_offline(): 34 | """Run migrations in 'offline' mode. 35 | 36 | This configures the context with just a URL 37 | and not an Engine, though an Engine is acceptable 38 | here as well. By skipping the Engine creation 39 | we don't even need a DBAPI to be available. 40 | 41 | Calls to context.execute() here emit the given string to the 42 | script output. 43 | 44 | """ 45 | url = config.get_main_option("sqlalchemy.url") 46 | context.configure( 47 | url=url, target_metadata=target_metadata, literal_binds=True) 48 | 49 | with context.begin_transaction(): 50 | context.run_migrations() 51 | 52 | 53 | def run_migrations_online(): 54 | """Run migrations in 'online' mode. 55 | 56 | In this scenario we need to create an Engine 57 | and associate a connection with the context. 58 | 59 | """ 60 | connectable = engine_from_config( 61 | config.get_section(config.config_ini_section), 62 | prefix='sqlalchemy.', 63 | poolclass=pool.NullPool) 64 | 65 | with connectable.connect() as connection: 66 | context.configure( 67 | connection=connection, 68 | target_metadata=target_metadata 69 | ) 70 | 71 | with context.begin_transaction(): 72 | context.run_migrations() 73 | 74 | if context.is_offline_mode(): 75 | run_migrations_offline() 76 | else: 77 | run_migrations_online() 78 | -------------------------------------------------------------------------------- /ch11/alembic/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = ${repr(up_revision)} 11 | down_revision = ${repr(down_revision)} 12 | branch_labels = ${repr(branch_labels)} 13 | depends_on = ${repr(depends_on)} 14 | 15 | from alembic import op 16 | import sqlalchemy as sa 17 | ${imports if imports else ""} 18 | 19 | def upgrade(): 20 | ${upgrades if upgrades else "pass"} 21 | 22 | 23 | def downgrade(): 24 | ${downgrades if downgrades else "pass"} 25 | -------------------------------------------------------------------------------- /ch11/alembic/versions/2e6a6cc63e9_renaming_cookies_to_new_cookies.py: -------------------------------------------------------------------------------- 1 | """Renaming cookies to new_cookies 2 | 3 | Revision ID: 2e6a6cc63e9 4 | Revises: 34044511331 5 | Create Date: 2015-09-14 21:21:07.579264 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = '2e6a6cc63e9' 11 | down_revision = '34044511331' 12 | branch_labels = None 13 | depends_on = None 14 | 15 | from alembic import op 16 | import sqlalchemy as sa 17 | 18 | 19 | def upgrade(): 20 | op.rename_table('cookies', 'new_cookies') 21 | 22 | 23 | def downgrade(): 24 | op.rename_table('new_cookies', 'cookies') 25 | -------------------------------------------------------------------------------- /ch11/alembic/versions/34044511331_added_cookie_model.py: -------------------------------------------------------------------------------- 1 | """Added Cookie model 2 | 3 | Revision ID: 34044511331 4 | Revises: 8a8a9d067 5 | Create Date: 2015-09-14 20:37:25.924031 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = '34044511331' 11 | down_revision = '8a8a9d067' 12 | branch_labels = None 13 | depends_on = None 14 | 15 | from alembic import op 16 | import sqlalchemy as sa 17 | 18 | 19 | def upgrade(): 20 | ### commands auto generated by Alembic - please adjust! ### 21 | op.create_table('cookies', 22 | sa.Column('cookie_id', sa.Integer(), nullable=False), 23 | sa.Column('cookie_name', sa.String(length=50), nullable=True), 24 | sa.Column('cookie_recipe_url', sa.String(length=255), nullable=True), 25 | sa.Column('cookie_sku', sa.String(length=55), nullable=True), 26 | sa.Column('quantity', sa.Integer(), nullable=True), 27 | sa.Column('unit_cost', sa.Numeric(precision=12, scale=2), nullable=True), 28 | sa.PrimaryKeyConstraint('cookie_id') 29 | ) 30 | op.create_index(op.f('ix_cookies_cookie_name'), 'cookies', ['cookie_name'], unique=False) 31 | ### end Alembic commands ### 32 | 33 | 34 | def downgrade(): 35 | ### commands auto generated by Alembic - please adjust! ### 36 | op.drop_index(op.f('ix_cookies_cookie_name'), table_name='cookies') 37 | op.drop_table('cookies') 38 | ### end Alembic commands ### 39 | -------------------------------------------------------------------------------- /ch11/alembic/versions/8a8a9d067_empty_init.py: -------------------------------------------------------------------------------- 1 | """Empty Init 2 | 3 | Revision ID: 8a8a9d067 4 | Revises: 5 | Create Date: 2015-09-13 20:10:05.486995 6 | 7 | """ 8 | 9 | # revision identifiers, used by Alembic. 10 | revision = '8a8a9d067' 11 | down_revision = None 12 | branch_labels = None 13 | depends_on = None 14 | 15 | from alembic import op 16 | import sqlalchemy as sa 17 | 18 | 19 | def upgrade(): 20 | pass 21 | 22 | 23 | def downgrade(): 24 | pass 25 | -------------------------------------------------------------------------------- /ch11/alembictest.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreillymedia/essential-sqlalchemy-2e/bdfed6cf7f0b36b20b4850f5fc0c489947cbea5f/ch11/alembictest.db -------------------------------------------------------------------------------- /ch11/app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreillymedia/essential-sqlalchemy-2e/bdfed6cf7f0b36b20b4850f5fc0c489947cbea5f/ch11/app/__init__.py -------------------------------------------------------------------------------- /ch11/app/db.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine, Column, Integer, Numeric, String 2 | 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy.orm import sessionmaker 5 | 6 | engine = create_engine('sqlite:///alembictest.db') 7 | 8 | Base = declarative_base() 9 | 10 | 11 | class Cookie(Base): 12 | __tablename__ = 'new_cookies' 13 | 14 | cookie_id = Column(Integer, primary_key=True) 15 | cookie_name = Column(String(50), index=True) 16 | cookie_recipe_url = Column(String(255)) 17 | cookie_sku = Column(String(55)) 18 | quantity = Column(Integer()) 19 | unit_cost = Column(Numeric(12, 2)) 20 | -------------------------------------------------------------------------------- /ch11/migration.sql: -------------------------------------------------------------------------------- 1 | -- Running upgrade 34044511331 -> 2e6a6cc63e9 2 | 3 | ALTER TABLE cookies RENAME TO new_cookies; 4 | 5 | UPDATE alembic_version SET version_num='2e6a6cc63e9' WHERE alembic_version.version_num = '34044511331'; 6 | 7 | -------------------------------------------------------------------------------- /ch14/Association Proxy - Part 1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from sqlalchemy import create_engine\n", 12 | "from sqlalchemy.orm import sessionmaker\n", 13 | "\n", 14 | "engine = create_engine('sqlite:///:memory:')\n", 15 | "\n", 16 | "Session = sessionmaker(bind=engine)" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 2, 22 | "metadata": { 23 | "collapsed": false 24 | }, 25 | "outputs": [], 26 | "source": [ 27 | "from datetime import datetime\n", 28 | "\n", 29 | "from sqlalchemy import Column, Integer, Numeric, String, Table, ForeignKey\n", 30 | "from sqlalchemy.orm import relationship\n", 31 | "from sqlalchemy.ext.declarative import declarative_base\n", 32 | "\n", 33 | "Base = declarative_base()\n", 34 | "\n", 35 | "\n", 36 | "cookieingredients_table = Table('cookieingredients', Base.metadata,\n", 37 | " Column('cookie_id', Integer, ForeignKey(\"cookies.cookie_id\"),\n", 38 | " primary_key=True),\n", 39 | " Column('ingredient_id', Integer, ForeignKey(\"ingredients.ingredient_id\"),\n", 40 | " primary_key=True)\n", 41 | ")\n", 42 | "\n", 43 | "\n", 44 | "class Ingredient(Base):\n", 45 | " __tablename__ = 'ingredients'\n", 46 | " \n", 47 | " ingredient_id = Column(Integer, primary_key=True)\n", 48 | " name = Column(String(255), index=True)\n", 49 | " \n", 50 | " def __repr__(self):\n", 51 | " return \"Ingredient(name='{self.name}')\".format(self=self)\n", 52 | "\n", 53 | "\n", 54 | "class Cookie(Base):\n", 55 | " __tablename__ = 'cookies'\n", 56 | "\n", 57 | " cookie_id = Column(Integer, primary_key=True)\n", 58 | " cookie_name = Column(String(50), index=True)\n", 59 | " cookie_recipe_url = Column(String(255))\n", 60 | " cookie_sku = Column(String(55))\n", 61 | " quantity = Column(Integer())\n", 62 | " unit_cost = Column(Numeric(12, 2))\n", 63 | " \n", 64 | " ingredients = relationship(\"Ingredient\", secondary=cookieingredients_table)\n", 65 | " \n", 66 | " def __repr__(self):\n", 67 | " return \"Cookie(cookie_name='{self.cookie_name}', \" \\\n", 68 | " \"cookie_recipe_url='{self.cookie_recipe_url}', \" \\\n", 69 | " \"cookie_sku='{self.cookie_sku}', \" \\\n", 70 | " \"quantity={self.quantity}, \" \\\n", 71 | " \"unit_cost={self.unit_cost})\".format(self=self)\n", 72 | " \n", 73 | "\n", 74 | "Base.metadata.create_all(engine)" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": 3, 80 | "metadata": { 81 | "collapsed": false 82 | }, 83 | "outputs": [], 84 | "source": [ 85 | "session = Session()\n", 86 | "cc_cookie = Cookie(cookie_name='chocolate chip', \n", 87 | " cookie_recipe_url='http://some.aweso.me/cookie/recipe.html', \n", 88 | " cookie_sku='CC01', \n", 89 | " quantity=12, \n", 90 | " unit_cost=0.50)\n", 91 | "flour = Ingredient(name='Flour')\n", 92 | "sugar = Ingredient(name='Sugar')\n", 93 | "egg = Ingredient(name='Egg')\n", 94 | "cc = Ingredient(name='Chocolate Chips')\n", 95 | "cc_cookie.ingredients.extend([flour, sugar, egg, cc])\n", 96 | "session.add(cc_cookie)\n", 97 | "session.flush()" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": 4, 103 | "metadata": { 104 | "collapsed": false 105 | }, 106 | "outputs": [ 107 | { 108 | "data": { 109 | "text/plain": [ 110 | "['Flour', 'Sugar', 'Egg', 'Chocolate Chips']" 111 | ] 112 | }, 113 | "execution_count": 4, 114 | "metadata": {}, 115 | "output_type": "execute_result" 116 | } 117 | ], 118 | "source": [ 119 | "[ingredient.name for ingredient in cc_cookie.ingredients]" 120 | ] 121 | } 122 | ], 123 | "metadata": { 124 | "kernelspec": { 125 | "display_name": "Python 2", 126 | "language": "python", 127 | "name": "python2" 128 | }, 129 | "language_info": { 130 | "codemirror_mode": { 131 | "name": "ipython", 132 | "version": 2 133 | }, 134 | "file_extension": ".py", 135 | "mimetype": "text/x-python", 136 | "name": "python", 137 | "nbconvert_exporter": "python", 138 | "pygments_lexer": "ipython2", 139 | "version": "2.7.10" 140 | } 141 | }, 142 | "nbformat": 4, 143 | "nbformat_minor": 0 144 | } 145 | -------------------------------------------------------------------------------- /ch14/Association Proxy - Part 2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from sqlalchemy import create_engine\n", 12 | "from sqlalchemy.orm import sessionmaker\n", 13 | "\n", 14 | "engine = create_engine('sqlite:///:memory:')\n", 15 | "\n", 16 | "Session = sessionmaker(bind=engine)" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 2, 22 | "metadata": { 23 | "collapsed": false 24 | }, 25 | "outputs": [], 26 | "source": [ 27 | "from datetime import datetime\n", 28 | "\n", 29 | "from sqlalchemy import Column, Integer, Numeric, String, Table, ForeignKey\n", 30 | "from sqlalchemy.orm import relationship\n", 31 | "from sqlalchemy.ext.declarative import declarative_base\n", 32 | "from sqlalchemy.ext.associationproxy import association_proxy\n", 33 | "\n", 34 | "\n", 35 | "Base = declarative_base()\n", 36 | "\n", 37 | "\n", 38 | "cookieingredients_table = Table('cookieingredients', Base.metadata,\n", 39 | " Column('cookie_id', Integer, ForeignKey(\"cookies.cookie_id\"),\n", 40 | " primary_key=True),\n", 41 | " Column('ingredient_id', Integer, ForeignKey(\"ingredients.ingredient_id\"),\n", 42 | " primary_key=True)\n", 43 | ")\n", 44 | "\n", 45 | "\n", 46 | "class Ingredient(Base):\n", 47 | " __tablename__ = 'ingredients'\n", 48 | " \n", 49 | " ingredient_id = Column(Integer, primary_key=True)\n", 50 | " name = Column(String(255), index=True)\n", 51 | " \n", 52 | " def __init__(self, name):\n", 53 | " self.name = name\n", 54 | " \n", 55 | " \n", 56 | " def __repr__(self):\n", 57 | " return \"Ingredient(name='{self.name}')\".format(self=self)\n", 58 | "\n", 59 | "\n", 60 | "class Cookie(Base):\n", 61 | " __tablename__ = 'cookies'\n", 62 | "\n", 63 | " cookie_id = Column(Integer, primary_key=True)\n", 64 | " cookie_name = Column(String(50), index=True)\n", 65 | " cookie_recipe_url = Column(String(255))\n", 66 | " cookie_sku = Column(String(55))\n", 67 | " quantity = Column(Integer())\n", 68 | " unit_cost = Column(Numeric(12, 2))\n", 69 | " \n", 70 | " ingredients = relationship(\"Ingredient\", secondary=lambda: cookieingredients_table)\n", 71 | " \n", 72 | " ingredient_names = association_proxy('ingredients', 'name')\n", 73 | "\n", 74 | " \n", 75 | " def __repr__(self):\n", 76 | " return \"Cookie(cookie_name='{self.cookie_name}', \" \\\n", 77 | " \"cookie_recipe_url='{self.cookie_recipe_url}', \" \\\n", 78 | " \"cookie_sku='{self.cookie_sku}', \" \\\n", 79 | " \"quantity={self.quantity}, \" \\\n", 80 | " \"unit_cost={self.unit_cost})\".format(self=self)\n", 81 | " \n", 82 | "\n", 83 | "Base.metadata.create_all(engine)" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": 3, 89 | "metadata": { 90 | "collapsed": false 91 | }, 92 | "outputs": [], 93 | "source": [ 94 | "session = Session()\n", 95 | "cc_cookie = Cookie(cookie_name='chocolate chip', \n", 96 | " cookie_recipe_url='http://some.aweso.me/cookie/recipe.html', \n", 97 | " cookie_sku='CC01', \n", 98 | " quantity=12, \n", 99 | " unit_cost=0.50)\n", 100 | "dcc = Cookie(cookie_name='dark chocolate chip',\n", 101 | " cookie_recipe_url='http://some.aweso.me/cookie/recipe_dark.html',\n", 102 | " cookie_sku='CC02',\n", 103 | " quantity=1,\n", 104 | " unit_cost=0.75)\n", 105 | "\n", 106 | "flour = Ingredient(name='Flour')\n", 107 | "sugar = Ingredient(name='Sugar')\n", 108 | "egg = Ingredient(name='Egg')\n", 109 | "cc = Ingredient(name='Chocolate Chips')\n", 110 | "\n", 111 | "cc_cookie.ingredients.extend([flour, sugar, egg, cc])\n", 112 | "session.add(cc_cookie)\n", 113 | "session.add(dcc)\n", 114 | "session.flush()" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": 4, 120 | "metadata": { 121 | "collapsed": false 122 | }, 123 | "outputs": [ 124 | { 125 | "data": { 126 | "text/plain": [ 127 | "['Flour', 'Sugar', 'Egg', 'Chocolate Chips']" 128 | ] 129 | }, 130 | "execution_count": 4, 131 | "metadata": {}, 132 | "output_type": "execute_result" 133 | } 134 | ], 135 | "source": [ 136 | "[ingredient.name for ingredient in cc_cookie.ingredients]" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 5, 142 | "metadata": { 143 | "collapsed": false 144 | }, 145 | "outputs": [ 146 | { 147 | "data": { 148 | "text/plain": [ 149 | "['Flour', 'Sugar', 'Egg', 'Chocolate Chips']" 150 | ] 151 | }, 152 | "execution_count": 5, 153 | "metadata": {}, 154 | "output_type": "execute_result" 155 | } 156 | ], 157 | "source": [ 158 | "cc_cookie.ingredient_names" 159 | ] 160 | }, 161 | { 162 | "cell_type": "code", 163 | "execution_count": 6, 164 | "metadata": { 165 | "collapsed": false 166 | }, 167 | "outputs": [], 168 | "source": [ 169 | "cc_cookie.ingredient_names.append('Oil')\n", 170 | "session.flush()" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": 7, 176 | "metadata": { 177 | "collapsed": false 178 | }, 179 | "outputs": [ 180 | { 181 | "data": { 182 | "text/plain": [ 183 | "['Flour', 'Sugar', 'Egg', 'Chocolate Chips', 'Oil']" 184 | ] 185 | }, 186 | "execution_count": 7, 187 | "metadata": {}, 188 | "output_type": "execute_result" 189 | } 190 | ], 191 | "source": [ 192 | "cc_cookie.ingredient_names" 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": 8, 198 | "metadata": { 199 | "collapsed": false 200 | }, 201 | "outputs": [ 202 | { 203 | "data": { 204 | "text/plain": [ 205 | "[Ingredient(name='Flour'),\n", 206 | " Ingredient(name='Sugar'),\n", 207 | " Ingredient(name='Egg'),\n", 208 | " Ingredient(name='Chocolate Chips'),\n", 209 | " Ingredient(name='Oil')]" 210 | ] 211 | }, 212 | "execution_count": 8, 213 | "metadata": {}, 214 | "output_type": "execute_result" 215 | } 216 | ], 217 | "source": [ 218 | "cc_cookie.ingredients" 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": 9, 224 | "metadata": { 225 | "collapsed": true 226 | }, 227 | "outputs": [], 228 | "source": [ 229 | "dcc_ingredient_list = ['Flour', 'Sugar', 'Egg', 'Dark Chocolate Chips', 'Oil']" 230 | ] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": 10, 235 | "metadata": { 236 | "collapsed": true 237 | }, 238 | "outputs": [], 239 | "source": [ 240 | "existing_ingredients = session.query(Ingredient).filter(\n", 241 | " Ingredient.name.in_(dcc_ingredient_list)).all()" 242 | ] 243 | }, 244 | { 245 | "cell_type": "code", 246 | "execution_count": 11, 247 | "metadata": { 248 | "collapsed": false 249 | }, 250 | "outputs": [], 251 | "source": [ 252 | "missing = set(dcc_ingredient_list) - set([x.name for x in \n", 253 | " existing_ingredients])" 254 | ] 255 | }, 256 | { 257 | "cell_type": "code", 258 | "execution_count": 12, 259 | "metadata": { 260 | "collapsed": false 261 | }, 262 | "outputs": [ 263 | { 264 | "data": { 265 | "text/plain": [ 266 | "{'Dark Chocolate Chips'}" 267 | ] 268 | }, 269 | "execution_count": 12, 270 | "metadata": {}, 271 | "output_type": "execute_result" 272 | } 273 | ], 274 | "source": [ 275 | "missing" 276 | ] 277 | }, 278 | { 279 | "cell_type": "code", 280 | "execution_count": 13, 281 | "metadata": { 282 | "collapsed": false 283 | }, 284 | "outputs": [], 285 | "source": [ 286 | "dcc.ingredients.extend(existing_ingredients)" 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": 14, 292 | "metadata": { 293 | "collapsed": true 294 | }, 295 | "outputs": [], 296 | "source": [ 297 | "dcc.ingredient_names.extend(list(missing))" 298 | ] 299 | }, 300 | { 301 | "cell_type": "code", 302 | "execution_count": 15, 303 | "metadata": { 304 | "collapsed": false 305 | }, 306 | "outputs": [ 307 | { 308 | "data": { 309 | "text/plain": [ 310 | "['Egg', 'Flour', 'Oil', 'Sugar', 'Dark Chocolate Chips']" 311 | ] 312 | }, 313 | "execution_count": 15, 314 | "metadata": {}, 315 | "output_type": "execute_result" 316 | } 317 | ], 318 | "source": [ 319 | "dcc.ingredient_names" 320 | ] 321 | } 322 | ], 323 | "metadata": { 324 | "kernelspec": { 325 | "display_name": "Python 2", 326 | "language": "python", 327 | "name": "python2" 328 | }, 329 | "language_info": { 330 | "codemirror_mode": { 331 | "name": "ipython", 332 | "version": 2 333 | }, 334 | "file_extension": ".py", 335 | "mimetype": "text/x-python", 336 | "name": "python", 337 | "nbconvert_exporter": "python", 338 | "pygments_lexer": "ipython2", 339 | "version": "2.7.10" 340 | } 341 | }, 342 | "nbformat": 4, 343 | "nbformat_minor": 0 344 | } 345 | -------------------------------------------------------------------------------- /ch14/Chinook_Sqlite.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreillymedia/essential-sqlalchemy-2e/bdfed6cf7f0b36b20b4850f5fc0c489947cbea5f/ch14/Chinook_Sqlite.sqlite -------------------------------------------------------------------------------- /ch14/Hybrid Properties.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from sqlalchemy import create_engine\n", 12 | "from sqlalchemy.orm import sessionmaker\n", 13 | "\n", 14 | "engine = create_engine('sqlite:///:memory:')\n", 15 | "\n", 16 | "Session = sessionmaker(bind=engine)" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 2, 22 | "metadata": { 23 | "collapsed": false 24 | }, 25 | "outputs": [], 26 | "source": [ 27 | "from datetime import datetime\n", 28 | "\n", 29 | "from sqlalchemy import Column, Integer, Numeric, String\n", 30 | "from sqlalchemy.ext.declarative import declarative_base\n", 31 | "from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method\n", 32 | "\n", 33 | "Base = declarative_base()\n", 34 | "\n", 35 | "\n", 36 | "class Cookie(Base):\n", 37 | " __tablename__ = 'cookies'\n", 38 | "\n", 39 | " cookie_id = Column(Integer, primary_key=True)\n", 40 | " cookie_name = Column(String(50), index=True)\n", 41 | " cookie_recipe_url = Column(String(255))\n", 42 | " cookie_sku = Column(String(55))\n", 43 | " quantity = Column(Integer())\n", 44 | " unit_cost = Column(Numeric(12, 2))\n", 45 | " \n", 46 | " @hybrid_property\n", 47 | " def inventory_value(self):\n", 48 | " return self.unit_cost * self.quantity\n", 49 | " \n", 50 | " @hybrid_method\n", 51 | " def bake_more(self, min_quantity):\n", 52 | " return self.quantity < min_quantity\n", 53 | " \n", 54 | " def __repr__(self):\n", 55 | " return \"Cookie(cookie_name='{self.cookie_name}', \" \\\n", 56 | " \"cookie_recipe_url='{self.cookie_recipe_url}', \" \\\n", 57 | " \"cookie_sku='{self.cookie_sku}', \" \\\n", 58 | " \"quantity={self.quantity}, \" \\\n", 59 | " \"unit_cost={self.unit_cost})\".format(self=self)\n", 60 | " \n", 61 | "\n", 62 | "Base.metadata.create_all(engine)" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": 3, 68 | "metadata": { 69 | "collapsed": false 70 | }, 71 | "outputs": [ 72 | { 73 | "name": "stdout", 74 | "output_type": "stream", 75 | "text": [ 76 | "cookies.unit_cost * cookies.quantity < :param_1\n" 77 | ] 78 | } 79 | ], 80 | "source": [ 81 | "print(Cookie.inventory_value < 10.00)" 82 | ] 83 | }, 84 | { 85 | "cell_type": "code", 86 | "execution_count": 4, 87 | "metadata": { 88 | "collapsed": false 89 | }, 90 | "outputs": [ 91 | { 92 | "name": "stdout", 93 | "output_type": "stream", 94 | "text": [ 95 | "cookies.quantity < :quantity_1\n" 96 | ] 97 | } 98 | ], 99 | "source": [ 100 | "print(Cookie.bake_more(12))" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": 5, 106 | "metadata": { 107 | "collapsed": false 108 | }, 109 | "outputs": [], 110 | "source": [ 111 | "session = Session()\n", 112 | "cc_cookie = Cookie(cookie_name='chocolate chip', \n", 113 | " cookie_recipe_url='http://some.aweso.me/cookie/recipe.html', \n", 114 | " cookie_sku='CC01', \n", 115 | " quantity=12, \n", 116 | " unit_cost=0.50)\n", 117 | "dcc = Cookie(cookie_name='dark chocolate chip',\n", 118 | " cookie_recipe_url='http://some.aweso.me/cookie/recipe_dark.html',\n", 119 | " cookie_sku='CC02',\n", 120 | " quantity=1,\n", 121 | " unit_cost=0.75)\n", 122 | "mol = Cookie(cookie_name='molasses',\n", 123 | " cookie_recipe_url='http://some.aweso.me/cookie/recipe_molasses.html',\n", 124 | " cookie_sku='MOL01',\n", 125 | " quantity=1,\n", 126 | " unit_cost=0.80)\n", 127 | "session.add(cc_cookie)\n", 128 | "session.add(dcc)\n", 129 | "session.add(mol)\n", 130 | "session.flush()" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": 6, 136 | "metadata": { 137 | "collapsed": false 138 | }, 139 | "outputs": [ 140 | { 141 | "data": { 142 | "text/plain": [ 143 | "0.75" 144 | ] 145 | }, 146 | "execution_count": 6, 147 | "metadata": {}, 148 | "output_type": "execute_result" 149 | } 150 | ], 151 | "source": [ 152 | "dcc.inventory_value" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": 7, 158 | "metadata": { 159 | "collapsed": false 160 | }, 161 | "outputs": [ 162 | { 163 | "data": { 164 | "text/plain": [ 165 | "True" 166 | ] 167 | }, 168 | "execution_count": 7, 169 | "metadata": {}, 170 | "output_type": "execute_result" 171 | } 172 | ], 173 | "source": [ 174 | "dcc.bake_more(12)" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": 7, 180 | "metadata": { 181 | "collapsed": false 182 | }, 183 | "outputs": [ 184 | { 185 | "name": "stdout", 186 | "output_type": "stream", 187 | "text": [ 188 | " chocolate chip - 6.00\n", 189 | " molasses - 0.80\n", 190 | " dark chocolate chip - 0.75\n" 191 | ] 192 | }, 193 | { 194 | "name": "stderr", 195 | "output_type": "stream", 196 | "text": [ 197 | "/Users/jasomyer/.virtualenvs/sa-book-dev/lib/python2.7/site-packages/sqlalchemy/sql/type_api.py:322: SAWarning: Dialect sqlite+pysqlite does *not* support Decimal objects natively, and SQLAlchemy must convert from floating point - rounding errors and other issues may occur. Please consider storing Decimal numbers as strings or integers on this platform for lossless storage.\n", 198 | " d[coltype] = rp = d['impl'].result_processor(dialect, coltype)\n" 199 | ] 200 | } 201 | ], 202 | "source": [ 203 | "from sqlalchemy import desc\n", 204 | "for cookie in session.query(Cookie).order_by(desc(Cookie.inventory_value)):\n", 205 | " print('{:>20} - {:.2f}'.format(cookie.cookie_name, cookie.inventory_value))" 206 | ] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": 8, 211 | "metadata": { 212 | "collapsed": false 213 | }, 214 | "outputs": [ 215 | { 216 | "name": "stdout", 217 | "output_type": "stream", 218 | "text": [ 219 | " dark chocolate chip - 1\n", 220 | " molasses - 1\n" 221 | ] 222 | } 223 | ], 224 | "source": [ 225 | "for cookie in session.query(Cookie).filter(Cookie.bake_more(12)):\n", 226 | " print('{:>20} - {}'.format(cookie.cookie_name, cookie.quantity))" 227 | ] 228 | } 229 | ], 230 | "metadata": { 231 | "kernelspec": { 232 | "display_name": "Python 2", 233 | "language": "python", 234 | "name": "python2" 235 | }, 236 | "language_info": { 237 | "codemirror_mode": { 238 | "name": "ipython", 239 | "version": 2 240 | }, 241 | "file_extension": ".py", 242 | "mimetype": "text/x-python", 243 | "name": "python", 244 | "nbconvert_exporter": "python", 245 | "pygments_lexer": "ipython2", 246 | "version": "2.7.10" 247 | } 248 | }, 249 | "nbformat": 4, 250 | "nbformat_minor": 0 251 | } 252 | -------------------------------------------------------------------------------- /ch14/db.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from sqlalchemy import Column, ForeignKey, Integer, Numeric, Unicode 3 | from sqlalchemy.orm import relationship 4 | from sqlalchemy.ext.declarative import declarative_base 5 | 6 | 7 | Base = declarative_base() 8 | metadata = Base.metadata 9 | 10 | 11 | class Album(Base): 12 | __tablename__ = 'Album' 13 | 14 | AlbumId = Column(Integer, primary_key=True, unique=True) 15 | Title = Column(Unicode(160), nullable=False) 16 | ArtistId = Column(ForeignKey(u'Artist.ArtistId'), nullable=False, index=True) 17 | 18 | Artist = relationship(u'Artist') 19 | 20 | 21 | class Artist(Base): 22 | __tablename__ = 'Artist' 23 | 24 | ArtistId = Column(Integer, primary_key=True, unique=True) 25 | Name = Column(Unicode(120)) 26 | 27 | 28 | class Genre(Base): 29 | __tablename__ = 'Genre' 30 | 31 | GenreId = Column(Integer, primary_key=True, unique=True) 32 | Name = Column(Unicode(120)) 33 | 34 | 35 | class MediaType(Base): 36 | __tablename__ = 'MediaType' 37 | 38 | MediaTypeId = Column(Integer, primary_key=True, unique=True) 39 | Name = Column(Unicode(120)) 40 | 41 | 42 | class Track(Base): 43 | __tablename__ = 'Track' 44 | 45 | TrackId = Column(Integer, primary_key=True, unique=True) 46 | Name = Column(Unicode(200), nullable=False) 47 | AlbumId = Column(ForeignKey(u'Album.AlbumId'), index=True) 48 | MediaTypeId = Column(ForeignKey(u'MediaType.MediaTypeId'), nullable=False, index=True) 49 | GenreId = Column(ForeignKey(u'Genre.GenreId'), index=True) 50 | Composer = Column(Unicode(220)) 51 | Milliseconds = Column(Integer, nullable=False) 52 | Bytes = Column(Integer) 53 | UnitPrice = Column(Numeric(10, 2), nullable=False) 54 | 55 | Album = relationship(u'Album') 56 | Genre = relationship(u'Genre') 57 | MediaType = relationship(u'MediaType') 58 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | -f file:///Users/jasomyer/Library/Caches/org.pip-installer.pip/Wheelhouse 2 | backports.ssl-match-hostname==3.4.0.2 3 | certifi==14.5.14 4 | flake8==2.4.0 5 | gnureadline==6.3.3 6 | ipython==3.0.0 7 | Jinja2==2.7.3 8 | jsonschema==2.4.0 9 | MarkupSafe==0.23 10 | mccabe==0.3 11 | mistune==0.5.1 12 | mock==1.0.1 13 | nose==1.3.4 14 | pep8==1.5.7 15 | ptyprocess==0.4 16 | pyflakes==0.8.1 17 | Pygments==2.0.2 18 | python-termstyle==0.1.10 19 | pyzmq==14.5.0 20 | rednose==0.4.1 21 | SQLAlchemy==1.0.8 22 | terminado==0.5 23 | tornado==4.1 24 | wheel==0.24.0 25 | --------------------------------------------------------------------------------