diff options
Diffstat (limited to 'reveal.js/sqlalchemy.html')
| -rwxr-xr-x | reveal.js/sqlalchemy.html | 115 |
1 files changed, 66 insertions, 49 deletions
diff --git a/reveal.js/sqlalchemy.html b/reveal.js/sqlalchemy.html index 4bafccd..578fc13 100755 --- a/reveal.js/sqlalchemy.html +++ b/reveal.js/sqlalchemy.html | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | <html lang="en"> | 2 | <html lang="en"> |
| 3 | <head> | 3 | <head> |
| 4 | <meta charset="utf-8"> | 4 | <meta charset="utf-8"> |
| 5 | <title>SQLAlchemy & Odin Data Access</title> | 5 | <title>SQLAlchemy</title> |
| 6 | <meta name="author" content="Simeon Simeonov"> | 6 | <meta name="author" content="Simeon Simeonov"> |
| 7 | <meta name="apple-mobile-web-app-capable" content="yes"> | 7 | <meta name="apple-mobile-web-app-capable" content="yes"> |
| 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> |
| @@ -11,7 +11,7 @@ | |||
| 11 | <link rel="stylesheet" href="dist/reset.css"> | 11 | <link rel="stylesheet" href="dist/reset.css"> |
| 12 | <link rel="stylesheet" href="dist/reveal.css"> | 12 | <link rel="stylesheet" href="dist/reveal.css"> |
| 13 | 13 | ||
| 14 | <link rel="stylesheet" href="dist/theme/fifty.css" id="theme"> | 14 | <link rel="stylesheet" href="dist/theme/statnett.css" id="theme"> |
| 15 | 15 | ||
| 16 | <!-- Theme used for syntax highlighting of code --> | 16 | <!-- Theme used for syntax highlighting of code --> |
| 17 | <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"> | 17 | <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"> |
| @@ -24,8 +24,8 @@ | |||
| 24 | <div class="slides"> | 24 | <div class="slides"> |
| 25 | 25 | ||
| 26 | <section> | 26 | <section> |
| 27 | <h2>SQLAlchemy & Odin Data Access</h2> | 27 | <h2>SQLAlchemy</h2> |
| 28 | <h4>Fifty Data Science</h4> | 28 | <h4>Data Science @ Statnett</h4> |
| 29 | </br> | 29 | </br> |
| 30 | <p><small>Simeon Simeonov</small></p> | 30 | <p><small>Simeon Simeonov</small></p> |
| 31 | </section> | 31 | </section> |
| @@ -38,7 +38,6 @@ | |||
| 38 | <ul> | 38 | <ul> |
| 39 | <span class="fragment"><li>SQLAlchemy - Design & overview</li></span> | 39 | <span class="fragment"><li>SQLAlchemy - Design & overview</li></span> |
| 40 | <span class="fragment"><li>SQLAlchemy - A small practical example</li></span> | 40 | <span class="fragment"><li>SQLAlchemy - A small practical example</li></span> |
| 41 | <span class="fragment"><li>Odin Data Access - Overview and examples</li></span> | ||
| 42 | <span class="fragment"><li>Q & A</li></span> | 41 | <span class="fragment"><li>Q & A</li></span> |
| 43 | </ul> | 42 | </ul> |
| 44 | </section> | 43 | </section> |
| @@ -56,18 +55,20 @@ | |||
| 56 | <h2>Why use SQLAlchemy?</h2> | 55 | <h2>Why use SQLAlchemy?</h2> |
| 57 | </br> | 56 | </br> |
| 58 | <ul> | 57 | <ul> |
| 58 | <li><p>free software - free as in "freedom" (MIT licensed)</p></li> | ||
| 59 | <li><p>portability - the programming interface is independent of the type of RDBMS and connector used</p></li> | 59 | <li><p>portability - the programming interface is independent of the type of RDBMS and connector used</p></li> |
| 60 | <li><p>security - no more SQL injections</p></li> | 60 | <li><p>security - no more SQL injections</p></li> |
| 61 | <li><p>abstraction - no need to bother with complex JOINs</p></li> | 61 | <li><p>abstraction - no need to bother with complex JOINs</p></li> |
| 62 | <li><p>object-orientation - you work with objects and NOT tables and rows</p></li> | 62 | <li><p>object-orientation - you work with objects and NOT tables and rows</p></li> |
| 63 | <li><p>performance - exploits the likehood of reusing a particular query</p></li> | 63 | <li><p>performance - exploits the likehood of reusing a particular query</p></li> |
| 64 | <li><p>flexibility - you can override almost anything</p></li> | 64 | <li><p>flexibility - you can override almost anything</p></li> |
| 65 | <li><p>0.0125 karma points for each MB transferred (2021)</p></li> | ||
| 65 | </ul> | 66 | </ul> |
| 66 | </section> | 67 | </section> |
| 67 | 68 | ||
| 68 | <section> | 69 | <section> |
| 69 | <h2>Basic architecture</h2> | 70 | <h2>Basic architecture</h2> |
| 70 | <p><em>SQLAlchemy</em> consists of several components, including the the <em>ORM</em>.</p> | 71 | <p><em>SQLAlchemy</em> consists of several components, including the <em>ORM</em>.</p> |
| 71 | <ul> | 72 | <ul> |
| 72 | <li><em>Engine</em>- manages the connection pool and the RDBMS-independent SQL dialect layer</li> | 73 | <li><em>Engine</em>- manages the connection pool and the RDBMS-independent SQL dialect layer</li> |
| 73 | <li><em>MetaData</em> - used to collect and organize information about your table layout (schema)</li> | 74 | <li><em>MetaData</em> - used to collect and organize information about your table layout (schema)</li> |
| @@ -98,24 +99,24 @@ | |||
| 98 | # engine = create_engine("sqlite:///library.db", echo=True) | 99 | # engine = create_engine("sqlite:///library.db", echo=True) |
| 99 | engine = create_engine("sqlite:///:memory:", echo=True) | 100 | engine = create_engine("sqlite:///:memory:", echo=True) |
| 100 | 101 | ||
| 101 | metadata = MetaData() | 102 | from sqlalchemy import Column, ForeignKey, Integer, String, Table |
| 102 | 103 | ||
| 103 | from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey | 104 | metadata = MetaData() |
| 104 | 105 | ||
| 105 | authors_table = Table( | 106 | authors_table = Table( |
| 106 | "authors", | 107 | "authors", |
| 107 | metadata, | 108 | metadata, |
| 108 | Column("id", Integer, primary_key=True), | 109 | Column("author_id", Integer, primary_key=True), |
| 109 | Column("name", String), | 110 | Column("name", String), |
| 110 | ) # Column("name", String(50)) is possible | 111 | ) # Column("name", String(50)) is possible |
| 111 | 112 | ||
| 112 | books_table = Table( | 113 | books_table = Table( |
| 113 | "books", | 114 | "books", |
| 114 | metadata, | 115 | metadata, |
| 115 | Column("id", Integer, primary_key=True), | 116 | Column("book_id", Integer, primary_key=True), |
| 116 | Column("title", String), | 117 | Column("title", String), |
| 117 | Column("description", String), | 118 | Column("description", String), |
| 118 | Column("author_id", ForeignKey('authors.id')), | 119 | Column("author_id", ForeignKey('authors.author_id')), |
| 119 | ) | 120 | ) |
| 120 | 121 | ||
| 121 | metadata.create_all(engine) # creates the tables | 122 | metadata.create_all(engine) # creates the tables |
| @@ -139,13 +140,13 @@ | |||
| 139 | # Out: {'id': None, 'name': None} | 140 | # Out: {'id': None, 'name': None} |
| 140 | 141 | ||
| 141 | insert_stmt.execute(name="Alexandre Dumas") # insert a single entry | 142 | insert_stmt.execute(name="Alexandre Dumas") # insert a single entry |
| 142 | insert_stmt.execute([{"name": 'Mr X'},{'name': 'Mr Y'}]) # a list of entries | 143 | insert_stmt.execute([{"name": "Mr X"}, {"name": "Mr Y"}]) # a list of entries |
| 143 | 144 | ||
| 144 | metadata.bind = engine # no need to explicitly bind the engine from now on | 145 | metadata.bind = engine # no need to explicitly bind the engine from now on |
| 145 | select_stmt = authors_table.select(authors_table.c.id==2) | 146 | select_stmt = authors_table.select(authors_table.c.id==2) |
| 146 | result = select_stmt.execute() | 147 | result = select_stmt.execute() |
| 147 | result.fetchall() | 148 | result.fetchall() |
| 148 | # Out: [(1, u'Mr X')] | 149 | # Out: [(1, 'Mr X')] |
| 149 | 150 | ||
| 150 | del_stmt = authors_table.delete() | 151 | del_stmt = authors_table.delete() |
| 151 | del_stmt.execute(whereclause=text("name='Mr Y'")) | 152 | del_stmt.execute(whereclause=text("name='Mr Y'")) |
| @@ -159,8 +160,7 @@ | |||
| 159 | <h4 data-id="code-title">Use of <em>classical mapping</em></h4> | 160 | <h4 data-id="code-title">Use of <em>classical mapping</em></h4> |
| 160 | <pre data-id="code-animation"> | 161 | <pre data-id="code-animation"> |
| 161 | <code class="python" data-trim data-line-numbers type="text/template"> | 162 | <code class="python" data-trim data-line-numbers type="text/template"> |
| 162 | from sqlalchemy.orm import mapper | 163 | from sqlalchemy.orm import backref, mapper, relation |
| 163 | from sqlalchemy.orm import relationship, backref | ||
| 164 | 164 | ||
| 165 | class Author: | 165 | class Author: |
| 166 | def __init__(self, name): | 166 | def __init__(self, name): |
| @@ -198,7 +198,7 @@ | |||
| 198 | class Author(Base): | 198 | class Author(Base): |
| 199 | __tablename__ = "authors" | 199 | __tablename__ = "authors" |
| 200 | 200 | ||
| 201 | id = Column(Integer, primary_key=True) | 201 | author_id = Column(Integer, primary_key=True) |
| 202 | name = Column(String) | 202 | name = Column(String) |
| 203 | 203 | ||
| 204 | def __init__(self, name): | 204 | def __init__(self, name): |
| @@ -211,10 +211,10 @@ | |||
| 211 | class Book(Base): | 211 | class Book(Base): |
| 212 | __tablename__ = "books" # self.__table__ will be available for our objects | 212 | __tablename__ = "books" # self.__table__ will be available for our objects |
| 213 | 213 | ||
| 214 | id = Column(Integer, primary_key=True) | 214 | book_id = Column(Integer, primary_key=True) |
| 215 | title = Column(String) | 215 | title = Column(String) |
| 216 | description = Column(String) | 216 | description = Column(String) |
| 217 | author_id = Column(Integer, ForeignKey("authors.id")) | 217 | author_id = Column(Integer, ForeignKey("authors.author_id")) |
| 218 | author = relationship(Author, backref=backref("books", order_by=title)) | 218 | author = relationship(Author, backref=backref("books", order_by=title)) |
| 219 | 219 | ||
| 220 | def __init__(self, title, description, author): | 220 | def __init__(self, title, description, author): |
| @@ -225,7 +225,7 @@ | |||
| 225 | def __str__(self): | 225 | def __str__(self): |
| 226 | return self.title | 226 | return self.title |
| 227 | 227 | ||
| 228 | Base.metadata.create_all(engine)# create tables | 228 | Base.metadata.create_all(engine) # create tables |
| 229 | </code> | 229 | </code> |
| 230 | </pre> | 230 | </pre> |
| 231 | </section> | 231 | </section> |
| @@ -251,7 +251,8 @@ | |||
| 251 | session.add(author_2) | 251 | session.add(author_2) |
| 252 | session.add(book_1) | 252 | session.add(book_1) |
| 253 | session.add(book_2) | 253 | session.add(book_2) |
| 254 | session.add(book_3) # or simply session.add_all([author_1, author_2, book_1, book_2, book_3]) | 254 | session.add(book_3) |
| 255 | # or simply session.add_all([author_1, author_2, book_1, book_2, book_3]) | ||
| 255 | 256 | ||
| 256 | # session.flush() | 257 | # session.flush() |
| 257 | session.commit() # flushes (issues the statements and sends them to the RDBMS) and commits | 258 | session.commit() # flushes (issues the statements and sends them to the RDBMS) and commits |
| @@ -270,61 +271,77 @@ | |||
| 270 | <h4 data-id="code-title">Queries</h4> | 271 | <h4 data-id="code-title">Queries</h4> |
| 271 | <pre data-id="code-animation"> | 272 | <pre data-id="code-animation"> |
| 272 | <code class="python" data-trim data-line-numbers type="text/template"> | 273 | <code class="python" data-trim data-line-numbers type="text/template"> |
| 273 | session.query(Book).order_by(Book.id) # returns a Query instance with a .statement attribute | 274 | session.query(Book).order_by(Book.book_id) # returns a Query instance with a .statement attribute |
| 274 | session.query(Book).order_by(Book.id).all() # returns an object-list | 275 | session.query(Book).order_by(Book.book_id).all() # returns an object-list |
| 275 | 276 | ||
| 276 | # return all book objects where title == "The Selfish Gene" | 277 | # return all book objects where title == "The Selfish Gene" |
| 277 | session.query(Book).filter(Book.title == "The Selfish Gene").order_by(Book.id).all() | 278 | session.query(Book).filter(Book.title == "The Selfish Gene").order_by(Book.book_id).all() |
| 278 | 279 | ||
| 279 | # using LIKE | 280 | # using LIKE |
| 280 | session.query(Book).filter(Book.title.like("The%")).order_by(Book.id).all() | 281 | session.query(Book).filter(Book.title.like("The%")).order_by(Book.book_id).all() |
| 281 | 282 | ||
| 282 | query = session.query(Book).filter(Book.id == 9).order_by(Book.id) | 283 | query = session.query(Book).filter(Book.book_id == 9).order_by(Book.book_id) |
| 283 | query.count() # returns 0L | 284 | query.count() # returns 0 |
| 284 | query.all() # returns an empty list | 285 | query.all() # returns an empty list |
| 285 | query.first() # returns None | 286 | query.first() # returns None |
| 286 | query.one() # raises NoResultFound exception | 287 | query.one() # raises NoResultFound exception |
| 287 | 288 | ||
| 288 | query = session.query(Book).filter(Book.id == 1).order_by(Book.id) | 289 | query = session.query(Book).filter(Book.book_id == 1).order_by(Book.book_id) |
| 289 | book_1 = query.one() | 290 | book_1 = query.one() |
| 290 | book_1.description # returns "A popular science book" | 291 | book_1.description # returns "A popular science book" |
| 291 | book_1.author.books # returns a list of Book-objects representing all the books from the same author. | 292 | book_1.author.books # returns a list of Book-objects representing all the books from the same author. |
| 292 | 293 | ||
| 293 | # get a list of all Book-instances where the author"s name is "Richard Dawkins" | 294 | # get a list of all Book-instances where the author"s name is "Richard Dawkins" |
| 294 | session.query(Book).filter(Book.author_id == Author.id).filter(Author.name == "Richard Dawkins").all() | 295 | session.query(Book).filter(Book.author_id == Author.author_id).filter(Author.name == "Richard Dawkins").all() |
| 295 | session.query(Book).join(Author).filter(Author.name == "Richard Dawkins").all() | 296 | session.query(Book).join(Author).filter(Author.name == "Richard Dawkins").all() |
| 296 | session.query(Book).\ | 297 | session.query(Book).\ |
| 297 | from_statement("SELECT b.* FROM books b, authors a WHERE b.author_id = a.id AND a.name=:name").\ | 298 | from_statement("SELECT b.* FROM books b, authors a WHERE b.author_id = a.author_id AND a.name=:name").\ |
| 298 | params(name="Richard Dawkins").all() | 299 | params(name="Richard Dawkins").all() |
| 299 | </code> | 300 | </code> |
| 300 | </pre> | 301 | </pre> |
| 301 | </section> | 302 | </section> |
| 302 | 303 | ||
| 303 | <section data-background="http://i.giphy.com/90F8aUepslB84.gif"> | 304 | <section data-background="http://i.giphy.com/90F8aUepslB84.gif"> |
| 304 | <h2>... simply amazing!</h2> | 305 | <h2>... there is more???</h2> |
| 305 | </section> | 306 | </section> |
| 306 | 307 | ||
| 307 | <section> | 308 | <section> |
| 308 | <h2>Odin Data Access</h2> | 309 | <h2>Some nice features</h2> |
| 309 | <p><a href="https://gitlab.fifty.eu/odin/data-science/odin-data-access">https://gitlab.fifty.eu/odin/data-science/odin-data-access</a></p> | 310 | <pre data-id="code-animation"> |
| 310 | <p>Provides the <em>odin_data_access</em> module with some of the following entities:</p> | 311 | <code class="python" data-trim data-line-numbers type="text/template"> |
| 311 | <ul> | 312 | import pandas as pd |
| 312 | <li><em>session</em> module for creating Session objects in a Fifty environment via <em>get_session</em> and <em>get_session_from_env</em></li> | ||
| 313 | <li><em>DBBase</em> - a tiny base-class that can be used as a mixin for mapping</li> | ||
| 314 | <li><em>DBTableAccessor</em> - tiny wrapper for <em>Session</em> and <em>Table</em> as well a "facade" for <em>Query</em> and <em>pandas</em></li> | ||
| 315 | </ul> | ||
| 316 | <p>The <em>DBTableAccessor</em>:</p> | ||
| 317 | <ul> | ||
| 318 | <li>represents a single RDBMS-table that is introspected during instance initialization (<em>__init__</em>)</li> | ||
| 319 | <li>uses a mix of <em>ORM</em> and <em>SQL expression language</em> functionality</li> | ||
| 320 | <li>contins a mapper for mapping custom classes to its corresponding <em>Table</em></li> | ||
| 321 | <span class="fragment"><li>used by Batman and Chuck Norris (source: incomplete Google search)</li></span> | ||
| 322 | </ul> | ||
| 323 | </section> | ||
| 324 | 313 | ||
| 325 | <section> | 314 | from sqlalchemy import func |
| 326 | <h1>Demo</h1> | 315 | |
| 327 | <p>Now watch this well-rehearsed demo...</p> | 316 | class Book(Base): |
| 317 | # ... | ||
| 318 | author = relationship( | ||
| 319 | Author, backref=backref("books", lazy="dynamic", order_by=title) | ||
| 320 | ) | ||
| 321 | # ... | ||
| 322 | |||
| 323 | @hybrid_property | ||
| 324 | def newly_arrived(self): | ||
| 325 | return self.book_id > 2 | ||
| 326 | |||
| 327 | @newly_arrived.expression | ||
| 328 | def newly_arrived(cls): | ||
| 329 | return cls.book_id > 2 | ||
| 330 | # return func.abs(cls.book_id) > 2 | ||
| 331 | |||
| 332 | # .books is now a Query object | ||
| 333 | query = author_obj.books.filter(Book.title.ilike("%red%")) | ||
| 334 | |||
| 335 | session.query(Book).filter(Book.newly_arrived.is_(True)).all() | ||
| 336 | # Out: [<__main__.Book at 0x7f132bdf0130>] | ||
| 337 | # WHERE (abs(books.book_id) > ?) IS 1 ... in the case of func.abs | ||
| 338 | |||
| 339 | df = pd.read_sql_table("my_table", con=session.get_bind()) # or con=engine | ||
| 340 | |||
| 341 | df = pd.read_sql_query(query.statement, engine) | ||
| 342 | |||
| 343 | </code> | ||
| 344 | </pre> | ||
| 328 | </section> | 345 | </section> |
| 329 | 346 | ||
| 330 | <section> | 347 | <section> |
