diff options
| author | Simeon Simeonov | 2021-03-16 00:32:48 +0100 |
|---|---|---|
| committer | Simeon Simeonov | 2021-03-16 00:32:48 +0100 |
| commit | f5b94931274107ae70587a4c1ac0c3756b7f16ab (patch) | |
| tree | c505b28fc11543dcd97bfa284c62a3df2172b5c0 | |
| parent | 9c6854707d2f3fd0866e82560ce95d1a87e2d794 (diff) | |
Move reveal.js/sqlalchemy.html to reveal.js/dataaccess.html and make a new reveal.js/sqlalchemy.html
| -rwxr-xr-x | reveal.js/dataaccess.html | 360 | ||||
| -rw-r--r-- | reveal.js/dist/theme/statnett.css | 297 | ||||
| -rwxr-xr-x | reveal.js/sqlalchemy.html | 115 |
3 files changed, 723 insertions, 49 deletions
diff --git a/reveal.js/dataaccess.html b/reveal.js/dataaccess.html new file mode 100755 index 0000000..4bafccd --- /dev/null +++ b/reveal.js/dataaccess.html | |||
| @@ -0,0 +1,360 @@ | |||
| 1 | <!doctype html> | ||
| 2 | <html lang="en"> | ||
| 3 | <head> | ||
| 4 | <meta charset="utf-8"> | ||
| 5 | <title>SQLAlchemy & Odin Data Access</title> | ||
| 6 | <meta name="author" content="Simeon Simeonov"> | ||
| 7 | <meta name="apple-mobile-web-app-capable" content="yes"> | ||
| 8 | <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> | ||
| 9 | <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| 10 | |||
| 11 | <link rel="stylesheet" href="dist/reset.css"> | ||
| 12 | <link rel="stylesheet" href="dist/reveal.css"> | ||
| 13 | |||
| 14 | <link rel="stylesheet" href="dist/theme/fifty.css" id="theme"> | ||
| 15 | |||
| 16 | <!-- Theme used for syntax highlighting of code --> | ||
| 17 | <link rel="stylesheet" href="plugin/highlight/monokai.css" id="highlight-theme"> | ||
| 18 | <!-- <link rel="stylesheet" href="plugin/highlight/zenburn.css" id="highlight-theme"> --> | ||
| 19 | </head> | ||
| 20 | <body> | ||
| 21 | <div class="reveal"> | ||
| 22 | |||
| 23 | <!-- Any section element inside of this container is displayed as a slide --> | ||
| 24 | <div class="slides"> | ||
| 25 | |||
| 26 | <section> | ||
| 27 | <h2>SQLAlchemy & Odin Data Access</h2> | ||
| 28 | <h4>Fifty Data Science</h4> | ||
| 29 | </br> | ||
| 30 | <p><small>Simeon Simeonov</small></p> | ||
| 31 | </section> | ||
| 32 | |||
| 33 | <section> | ||
| 34 | |||
| 35 | <section id="fragments"> | ||
| 36 | <h2>Agenda</h2> | ||
| 37 | </br> | ||
| 38 | <ul> | ||
| 39 | <span class="fragment"><li>SQLAlchemy - Design & overview</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> | ||
| 43 | </ul> | ||
| 44 | </section> | ||
| 45 | |||
| 46 | </section> | ||
| 47 | |||
| 48 | <section> | ||
| 49 | <h2>What is SQLAlchemy?</h2> | ||
| 50 | </br> | ||
| 51 | <p><em>SQLAlchemy</em> is a Python library created by Mike Bayer to provide a high-level Pythonic interface to RDBMS such as <em>PostgreSQL</em>, <em>SQLite</em>, <em>MySQL</em>, <em>Oracle</em>, <em>DB2</em>.</p> | ||
| 52 | <p><em>SQLAlchemy</em> includes RDBMS-independent SQL expression language and an <em>object-relational mapper (ORM)</em>.</p> | ||
| 53 | </section> | ||
| 54 | |||
| 55 | <section> | ||
| 56 | <h2>Why use SQLAlchemy?</h2> | ||
| 57 | </br> | ||
| 58 | <ul> | ||
| 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> | ||
| 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> | ||
| 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> | ||
| 65 | </ul> | ||
| 66 | </section> | ||
| 67 | |||
| 68 | <section> | ||
| 69 | <h2>Basic architecture</h2> | ||
| 70 | <p><em>SQLAlchemy</em> consists of several components, including the the <em>ORM</em>.</p> | ||
| 71 | <ul> | ||
| 72 | <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>Session</em> - establishes all conversations with the RDBMS and represents a "holding zone" for all the objects which you've loaded or associated with it during its lifespan</li> | ||
| 75 | <li><em>SQL expression language</em> - provides an API to execute your queries and updates against your tables, all from Python, and all in a database-independent way (low-level interface)</li> | ||
| 76 | <li><em>ORM</em> - provides a convenient way to add database persistence to your Python objects withoutrequiring you to design your objects around the database, or the database around the objects (high-level interface)</li> | ||
| 77 | </ul> | ||
| 78 | <img src="images/sqlalchemy/sqla_arch.png"></img> | ||
| 79 | </section> | ||
| 80 | |||
| 81 | <section data-auto-animate> | ||
| 82 | <h2>Example</h2> | ||
| 83 | <p><em>SQLAlchemy</em> gives us the choice between <em>classical mapping</em> and the newer <em>declarative mapping</em></p> | ||
| 84 | <pre data-id="code-animation"> | ||
| 85 | <code class="python" data-trim data-line-numbers type="text/template"> | ||
| 86 | import sqlalchemy | ||
| 87 | sqlalchemy.__version__ | ||
| 88 | |||
| 89 | from sqlalchemy import create_engine | ||
| 90 | |||
| 91 | # engine = create_engine("postgresql+psycopg2://user:zipassword@localhost/mydb" , echo=True) | ||
| 92 | # The string form of the URL is dialect+driver://user:password@host/dbname[?key=value..], | ||
| 93 | # where dialect is a database name such as mysql, oracle, postgresql, etc., | ||
| 94 | # and driver the name of a DBAPI, such as psycopg2, pyodbc, cx_oracle | ||
| 95 | # The echo flag is a shortcut to setting up SQLAlchemy logging, | ||
| 96 | # which is accomplished via Python’s standard logging module. | ||
| 97 | # With it enabled, we’ll see all the generated SQL produced. | ||
| 98 | # engine = create_engine("sqlite:///library.db", echo=True) | ||
| 99 | engine = create_engine("sqlite:///:memory:", echo=True) | ||
| 100 | |||
| 101 | metadata = MetaData() | ||
| 102 | |||
| 103 | from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey | ||
| 104 | |||
| 105 | authors_table = Table( | ||
| 106 | "authors", | ||
| 107 | metadata, | ||
| 108 | Column("id", Integer, primary_key=True), | ||
| 109 | Column("name", String), | ||
| 110 | ) # Column("name", String(50)) is possible | ||
| 111 | |||
| 112 | books_table = Table( | ||
| 113 | "books", | ||
| 114 | metadata, | ||
| 115 | Column("id", Integer, primary_key=True), | ||
| 116 | Column("title", String), | ||
| 117 | Column("description", String), | ||
| 118 | Column("author_id", ForeignKey('authors.id')), | ||
| 119 | ) | ||
| 120 | |||
| 121 | metadata.create_all(engine) # creates the tables | ||
| 122 | </code> | ||
| 123 | </pre> | ||
| 124 | </section> | ||
| 125 | |||
| 126 | <section data-auto-animate> | ||
| 127 | <h2>Example (cont...)</h2> | ||
| 128 | <h4 data-id="code-title">Use of <em>SQL expression language</em></h4> | ||
| 129 | <pre data-id="code-animation"> | ||
| 130 | <code class="python" data-trim data-line-numbers type="text/template"> | ||
| 131 | insert_stmt = authors_table.insert(bind=engine) | ||
| 132 | type(insert_stmt) | ||
| 133 | # Out: <class 'sqlalchemy.sql.expression.Insert'> | ||
| 134 | print(insert_stmt) | ||
| 135 | # Out: INSERT INTO authors (id, name) VALUES (:id,:name) | ||
| 136 | |||
| 137 | compiled_stmt = insert_stmt.compile() | ||
| 138 | print(compiled_stmt.params) | ||
| 139 | # Out: {'id': None, 'name': None} | ||
| 140 | |||
| 141 | 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 | |||
| 144 | 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 | result = select_stmt.execute() | ||
| 147 | result.fetchall() | ||
| 148 | # Out: [(1, u'Mr X')] | ||
| 149 | |||
| 150 | del_stmt = authors_table.delete() | ||
| 151 | del_stmt.execute(whereclause=text("name='Mr Y'")) | ||
| 152 | del_stmt.execute() # delete all | ||
| 153 | </code> | ||
| 154 | </pre> | ||
| 155 | </section> | ||
| 156 | |||
| 157 | <section data-auto-animate> | ||
| 158 | <h2>Example (cont...)</h2> | ||
| 159 | <h4 data-id="code-title">Use of <em>classical mapping</em></h4> | ||
| 160 | <pre data-id="code-animation"> | ||
| 161 | <code class="python" data-trim data-line-numbers type="text/template"> | ||
| 162 | from sqlalchemy.orm import mapper | ||
| 163 | from sqlalchemy.orm import relationship, backref | ||
| 164 | |||
| 165 | class Author: | ||
| 166 | def __init__(self, name): | ||
| 167 | self.name = name | ||
| 168 | |||
| 169 | def __str__(self): | ||
| 170 | return self.name | ||
| 171 | |||
| 172 | |||
| 173 | class Book: | ||
| 174 | def __init__(self, title, description, author): | ||
| 175 | self.title = title | ||
| 176 | self.description = description | ||
| 177 | self.author = author | ||
| 178 | |||
| 179 | def __str__(self): | ||
| 180 | return self.title | ||
| 181 | |||
| 182 | mapper(Book, books_table) | ||
| 183 | mapper(Author, authors_table, properties = {"books": relation(Book, backref="author")}) | ||
| 184 | </code> | ||
| 185 | </pre> | ||
| 186 | </section> | ||
| 187 | |||
| 188 | <section data-auto-animate> | ||
| 189 | <h2>Example (cont...)</h2> | ||
| 190 | <h4 data-id="code-title">Doing the same thing the easy way with <em>declarative mapping</em></h4> | ||
| 191 | <pre data-id="code-animation"> | ||
| 192 | <code class="python" data-trim data-line-numbers type="text/template"> | ||
| 193 | from sqlalchemy.ext.declarative import declarative_base | ||
| 194 | from sqlalchemy.orm import relationship, backref | ||
| 195 | |||
| 196 | Base = declarative_base() | ||
| 197 | |||
| 198 | class Author(Base): | ||
| 199 | __tablename__ = "authors" | ||
| 200 | |||
| 201 | id = Column(Integer, primary_key=True) | ||
| 202 | name = Column(String) | ||
| 203 | |||
| 204 | def __init__(self, name): | ||
| 205 | self.name = name | ||
| 206 | |||
| 207 | def __str__(self): | ||
| 208 | return self.name | ||
| 209 | |||
| 210 | |||
| 211 | class Book(Base): | ||
| 212 | __tablename__ = "books" # self.__table__ will be available for our objects | ||
| 213 | |||
| 214 | id = Column(Integer, primary_key=True) | ||
| 215 | title = Column(String) | ||
| 216 | description = Column(String) | ||
| 217 | author_id = Column(Integer, ForeignKey("authors.id")) | ||
| 218 | author = relationship(Author, backref=backref("books", order_by=title)) | ||
| 219 | |||
| 220 | def __init__(self, title, description, author): | ||
| 221 | self.title = title | ||
| 222 | self.description = description | ||
| 223 | self.author = author | ||
| 224 | |||
| 225 | def __str__(self): | ||
| 226 | return self.title | ||
| 227 | |||
| 228 | Base.metadata.create_all(engine)# create tables | ||
| 229 | </code> | ||
| 230 | </pre> | ||
| 231 | </section> | ||
| 232 | |||
| 233 | <section data-auto-animate> | ||
| 234 | <h2>Example (cont...)</h2> | ||
| 235 | <h4 data-id="code-title">Creating instances</h4> | ||
| 236 | <pre data-id="code-animation"> | ||
| 237 | <code class="python" data-trim data-line-numbers type="text/template"> | ||
| 238 | from sqlalchemy.orm import sessionmaker | ||
| 239 | |||
| 240 | Session = sessionmaker(bind=engine) # bound session | ||
| 241 | session = Session() | ||
| 242 | |||
| 243 | author_1 = Author("Richard Dawkins") | ||
| 244 | author_2 = Author("Matt Ridley") | ||
| 245 | |||
| 246 | book_1 = Book("The Red Queen", "A popular science book", author_2) | ||
| 247 | book_2 = Book("The Selfish Gene", "A popular science book", author_1) | ||
| 248 | book_3 = Book("The Blind Watchmaker", "The theory of evolutio", author_1) # typo | ||
| 249 | |||
| 250 | session.add(author_1) | ||
| 251 | session.add(author_2) | ||
| 252 | session.add(book_1) | ||
| 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]) | ||
| 255 | |||
| 256 | # session.flush() | ||
| 257 | session.commit() # flushes (issues the statements and sends them to the RDBMS) and commits | ||
| 258 | |||
| 259 | book_3.description = "The theory of evolution" # update the object | ||
| 260 | book_3 in session # check whether the object is in the session | ||
| 261 | # Out: True | ||
| 262 | |||
| 263 | session.commit() | ||
| 264 | </code> | ||
| 265 | </pre> | ||
| 266 | </section> | ||
| 267 | |||
| 268 | <section data-auto-animate> | ||
| 269 | <h2>Example (cont...)</h2> | ||
| 270 | <h4 data-id="code-title">Queries</h4> | ||
| 271 | <pre data-id="code-animation"> | ||
| 272 | <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.id).all() # returns an object-list | ||
| 275 | |||
| 276 | # 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 | |||
| 279 | # using LIKE | ||
| 280 | session.query(Book).filter(Book.title.like("The%")).order_by(Book.id).all() | ||
| 281 | |||
| 282 | query = session.query(Book).filter(Book.id == 9).order_by(Book.id) | ||
| 283 | query.count() # returns 0L | ||
| 284 | query.all() # returns an empty list | ||
| 285 | query.first() # returns None | ||
| 286 | query.one() # raises NoResultFound exception | ||
| 287 | |||
| 288 | query = session.query(Book).filter(Book.id == 1).order_by(Book.id) | ||
| 289 | book_1 = query.one() | ||
| 290 | 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 | |||
| 293 | # 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).join(Author).filter(Author.name == "Richard Dawkins").all() | ||
| 296 | session.query(Book).\ | ||
| 297 | from_statement("SELECT b.* FROM books b, authors a WHERE b.author_id = a.id AND a.name=:name").\ | ||
| 298 | params(name="Richard Dawkins").all() | ||
| 299 | </code> | ||
| 300 | </pre> | ||
| 301 | </section> | ||
| 302 | |||
| 303 | <section data-background="http://i.giphy.com/90F8aUepslB84.gif"> | ||
| 304 | <h2>... simply amazing!</h2> | ||
| 305 | </section> | ||
| 306 | |||
| 307 | <section> | ||
| 308 | <h2>Odin Data Access</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 | <p>Provides the <em>odin_data_access</em> module with some of the following entities:</p> | ||
| 311 | <ul> | ||
| 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 | |||
| 325 | <section> | ||
| 326 | <h1>Demo</h1> | ||
| 327 | <p>Now watch this well-rehearsed demo...</p> | ||
| 328 | </section> | ||
| 329 | |||
| 330 | <section> | ||
| 331 | <h1>Q & A</h1> | ||
| 332 | </section> | ||
| 333 | |||
| 334 | </div> | ||
| 335 | </div> | ||
| 336 | |||
| 337 | <script src="dist/reveal.js"></script> | ||
| 338 | <script src="plugin/zoom/zoom.js"></script> | ||
| 339 | <script src="plugin/notes/notes.js"></script> | ||
| 340 | <script src="plugin/search/search.js"></script> | ||
| 341 | <script src="plugin/markdown/markdown.js"></script> | ||
| 342 | <script src="plugin/highlight/highlight.js"></script> | ||
| 343 | <script> | ||
| 344 | |||
| 345 | // Also available as an ES module, see: | ||
| 346 | // https://revealjs.netlify.app/initialization/ | ||
| 347 | Reveal.initialize({ | ||
| 348 | controls: true, | ||
| 349 | progress: true, | ||
| 350 | center: true, | ||
| 351 | hash: true, | ||
| 352 | |||
| 353 | // Learn about plugins: https://revealjs.netlify.app/plugins/ | ||
| 354 | plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight ] | ||
| 355 | }); | ||
| 356 | |||
| 357 | </script> | ||
| 358 | |||
| 359 | </body> | ||
| 360 | </html> | ||
diff --git a/reveal.js/dist/theme/statnett.css b/reveal.js/dist/theme/statnett.css new file mode 100644 index 0000000..5f5ab00 --- /dev/null +++ b/reveal.js/dist/theme/statnett.css | |||
| @@ -0,0 +1,297 @@ | |||
| 1 | /** | ||
| 2 | * A simple theme for reveal.js presentations, similar | ||
| 3 | * to the default theme. The accent color is brown. | ||
| 4 | * | ||
| 5 | * This theme is Copyright (C) 2012-2013 Owen Versteeg, http://owenversteeg.com - it is MIT licensed. | ||
| 6 | */ | ||
| 7 | .reveal a { | ||
| 8 | line-height: 1.3em; } | ||
| 9 | |||
| 10 | section.has-dark-background, section.has-dark-background h1, section.has-dark-background h2, section.has-dark-background h3, section.has-dark-background h4, section.has-dark-background h5, section.has-dark-background h6 { | ||
| 11 | color: #fff; } | ||
| 12 | |||
| 13 | /********************************************* | ||
| 14 | * GLOBAL STYLES | ||
| 15 | *********************************************/ | ||
| 16 | :root { | ||
| 17 | --background-color: #F0F1EB; | ||
| 18 | --main-font: Palatino Linotype, Book Antiqua, Palatino, FreeSerif, serif; | ||
| 19 | --main-font-size: 40px; | ||
| 20 | --main-color: #000; | ||
| 21 | --block-margin: 20px; | ||
| 22 | --heading-margin: 0 0 20px 0; | ||
| 23 | --heading-font: Palatino Linotype, Book Antiqua, Palatino, FreeSerif, serif; | ||
| 24 | --heading-color: #383D3D; | ||
| 25 | --heading-line-height: 1.2; | ||
| 26 | --heading-letter-spacing: normal; | ||
| 27 | --heading-text-transform: none; | ||
| 28 | --heading-text-shadow: none; | ||
| 29 | --heading-font-weight: normal; | ||
| 30 | --heading1-text-shadow: none; | ||
| 31 | --heading1-size: 3.77em; | ||
| 32 | --heading2-size: 2.11em; | ||
| 33 | --heading3-size: 1.55em; | ||
| 34 | --heading4-size: 1em; | ||
| 35 | --code-font: monospace; | ||
| 36 | --link-color: #51483D; | ||
| 37 | --link-color-hover: #8b7c69; | ||
| 38 | --selection-background-color: #26351C; | ||
| 39 | --selection-color: #fff; } | ||
| 40 | |||
| 41 | .reveal-viewport { | ||
| 42 | background: #F0F1EB; | ||
| 43 | background-color: #F0F1EB; } | ||
| 44 | |||
| 45 | .reveal { | ||
| 46 | font-family: "Palatino Linotype", "Book Antiqua", Palatino, FreeSerif, serif; | ||
| 47 | font-size: 22px; | ||
| 48 | font-weight: normal; | ||
| 49 | color: #000; } | ||
| 50 | |||
| 51 | .reveal ::selection { | ||
| 52 | color: #fff; | ||
| 53 | background: #26351C; | ||
| 54 | text-shadow: none; } | ||
| 55 | |||
| 56 | .reveal ::-moz-selection { | ||
| 57 | color: #fff; | ||
| 58 | background: #26351C; | ||
| 59 | text-shadow: none; } | ||
| 60 | |||
| 61 | .reveal .slides section, | ||
| 62 | .reveal .slides section > section { | ||
| 63 | line-height: 1.3; | ||
| 64 | font-weight: inherit; } | ||
| 65 | |||
| 66 | /********************************************* | ||
| 67 | * HEADERS | ||
| 68 | *********************************************/ | ||
| 69 | .reveal h1, | ||
| 70 | .reveal h2, | ||
| 71 | .reveal h3, | ||
| 72 | .reveal h4, | ||
| 73 | .reveal h5, | ||
| 74 | .reveal h6 { | ||
| 75 | margin: 0 0 20px 0; | ||
| 76 | color: #383D3D; | ||
| 77 | font-family: "Palatino Linotype", "Book Antiqua", Palatino, FreeSerif, serif; | ||
| 78 | font-weight: normal; | ||
| 79 | line-height: 1.2; | ||
| 80 | letter-spacing: normal; | ||
| 81 | text-transform: none; | ||
| 82 | text-shadow: none; | ||
| 83 | word-wrap: break-word; } | ||
| 84 | |||
| 85 | .reveal h1 { | ||
| 86 | font-size: 3.77em; } | ||
| 87 | |||
| 88 | .reveal h2 { | ||
| 89 | font-size: 2.11em; } | ||
| 90 | |||
| 91 | .reveal h3 { | ||
| 92 | font-size: 1.55em; } | ||
| 93 | |||
| 94 | .reveal h4 { | ||
| 95 | font-size: 1em; } | ||
| 96 | |||
| 97 | .reveal h1 { | ||
| 98 | text-shadow: none; } | ||
| 99 | |||
| 100 | /********************************************* | ||
| 101 | * OTHER | ||
| 102 | *********************************************/ | ||
| 103 | .reveal p { | ||
| 104 | margin: 20px 0; | ||
| 105 | line-height: 1.3; } | ||
| 106 | |||
| 107 | /* Remove trailing margins after titles */ | ||
| 108 | .reveal h1:last-child, | ||
| 109 | .reveal h2:last-child, | ||
| 110 | .reveal h3:last-child, | ||
| 111 | .reveal h4:last-child, | ||
| 112 | .reveal h5:last-child, | ||
| 113 | .reveal h6:last-child { | ||
| 114 | margin-bottom: 0; } | ||
| 115 | |||
| 116 | /* Ensure certain elements are never larger than the slide itself */ | ||
| 117 | .reveal img, | ||
| 118 | .reveal video, | ||
| 119 | .reveal iframe { | ||
| 120 | max-width: 96%; | ||
| 121 | max-height: 96%; } | ||
| 122 | |||
| 123 | .reveal strong, | ||
| 124 | .reveal b { | ||
| 125 | font-weight: bold; } | ||
| 126 | |||
| 127 | .reveal em { | ||
| 128 | font-style: italic; } | ||
| 129 | |||
| 130 | .reveal ol, | ||
| 131 | .reveal dl, | ||
| 132 | .reveal ul { | ||
| 133 | display: inline-block; | ||
| 134 | text-align: left; | ||
| 135 | margin: 0 0 0 1em; } | ||
| 136 | |||
| 137 | .reveal ol { | ||
| 138 | list-style-type: decimal; } | ||
| 139 | |||
| 140 | .reveal ul { | ||
| 141 | list-style-type: disc; } | ||
| 142 | |||
| 143 | .reveal ul ul { | ||
| 144 | list-style-type: square; } | ||
| 145 | |||
| 146 | .reveal ul ul ul { | ||
| 147 | list-style-type: circle; } | ||
| 148 | |||
| 149 | .reveal ul ul, | ||
| 150 | .reveal ul ol, | ||
| 151 | .reveal ol ol, | ||
| 152 | .reveal ol ul { | ||
| 153 | display: block; | ||
| 154 | margin-left: 40px; } | ||
| 155 | |||
| 156 | .reveal dt { | ||
| 157 | font-weight: bold; } | ||
| 158 | |||
| 159 | .reveal dd { | ||
| 160 | margin-left: 40px; } | ||
| 161 | |||
| 162 | .reveal blockquote { | ||
| 163 | display: block; | ||
| 164 | position: relative; | ||
| 165 | width: 70%; | ||
| 166 | margin: 20px auto; | ||
| 167 | padding: 5px; | ||
| 168 | font-style: italic; | ||
| 169 | background: rgba(255, 255, 255, 0.05); | ||
| 170 | box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); } | ||
| 171 | |||
| 172 | .reveal blockquote p:first-child, | ||
| 173 | .reveal blockquote p:last-child { | ||
| 174 | display: inline-block; } | ||
| 175 | |||
| 176 | .reveal q { | ||
| 177 | font-style: italic; } | ||
| 178 | |||
| 179 | .reveal pre { | ||
| 180 | display: block; | ||
| 181 | position: relative; | ||
| 182 | width: 95%; | ||
| 183 | margin: 20px auto; | ||
| 184 | text-align: left; | ||
| 185 | font-size: 0.60em; | ||
| 186 | font-family: monospace; | ||
| 187 | line-height: 1.1em; | ||
| 188 | word-wrap: break-word; } | ||
| 189 | /* box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); } */ | ||
| 190 | |||
| 191 | .reveal code { | ||
| 192 | font-family: monospace; | ||
| 193 | text-transform: none; } | ||
| 194 | |||
| 195 | .reveal pre code { | ||
| 196 | display: block; | ||
| 197 | padding: 4px; | ||
| 198 | overflow: auto; | ||
| 199 | max-height: 520px; | ||
| 200 | word-wrap: normal; } | ||
| 201 | |||
| 202 | .reveal table { | ||
| 203 | margin: auto; | ||
| 204 | border-collapse: collapse; | ||
| 205 | border-spacing: 0; } | ||
| 206 | |||
| 207 | .reveal table th { | ||
| 208 | font-weight: bold; } | ||
| 209 | |||
| 210 | .reveal table th, | ||
| 211 | .reveal table td { | ||
| 212 | text-align: left; | ||
| 213 | padding: 0.2em 0.5em 0.2em 0.5em; | ||
| 214 | border-bottom: 1px solid; } | ||
| 215 | |||
| 216 | .reveal table th[align="center"], | ||
| 217 | .reveal table td[align="center"] { | ||
| 218 | text-align: center; } | ||
| 219 | |||
| 220 | .reveal table th[align="right"], | ||
| 221 | .reveal table td[align="right"] { | ||
| 222 | text-align: right; } | ||
| 223 | |||
| 224 | .reveal table tbody tr:last-child th, | ||
| 225 | .reveal table tbody tr:last-child td { | ||
| 226 | border-bottom: none; } | ||
| 227 | |||
| 228 | .reveal sup { | ||
| 229 | vertical-align: super; | ||
| 230 | font-size: smaller; } | ||
| 231 | |||
| 232 | .reveal sub { | ||
| 233 | vertical-align: sub; | ||
| 234 | font-size: smaller; } | ||
| 235 | |||
| 236 | .reveal small { | ||
| 237 | display: inline-block; | ||
| 238 | font-size: 0.6em; | ||
| 239 | line-height: 1.2em; | ||
| 240 | vertical-align: top; } | ||
| 241 | |||
| 242 | .reveal small * { | ||
| 243 | vertical-align: top; } | ||
| 244 | |||
| 245 | .reveal img { | ||
| 246 | margin: 20px 0; } | ||
| 247 | |||
| 248 | /********************************************* | ||
| 249 | * LINKS | ||
| 250 | *********************************************/ | ||
| 251 | .reveal a { | ||
| 252 | color: #51483D; | ||
| 253 | text-decoration: none; | ||
| 254 | transition: color .15s ease; } | ||
| 255 | |||
| 256 | .reveal a:hover { | ||
| 257 | color: #8b7c69; | ||
| 258 | text-shadow: none; | ||
| 259 | border: none; } | ||
| 260 | |||
| 261 | .reveal .roll span:after { | ||
| 262 | color: #fff; | ||
| 263 | background: #25211c; } | ||
| 264 | |||
| 265 | /********************************************* | ||
| 266 | * Frame helper | ||
| 267 | *********************************************/ | ||
| 268 | .reveal .r-frame { | ||
| 269 | border: 4px solid #000; | ||
| 270 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); } | ||
| 271 | |||
| 272 | .reveal a .r-frame { | ||
| 273 | transition: all .15s linear; } | ||
| 274 | |||
| 275 | .reveal a:hover .r-frame { | ||
| 276 | border-color: #51483D; | ||
| 277 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); } | ||
| 278 | |||
| 279 | /********************************************* | ||
| 280 | * NAVIGATION CONTROLS | ||
| 281 | *********************************************/ | ||
| 282 | .reveal .controls { | ||
| 283 | color: #51483D; } | ||
| 284 | |||
| 285 | /********************************************* | ||
| 286 | * PROGRESS BAR | ||
| 287 | *********************************************/ | ||
| 288 | .reveal .progress { | ||
| 289 | background: rgba(0, 0, 0, 0.2); | ||
| 290 | color: #51483D; } | ||
| 291 | |||
| 292 | /********************************************* | ||
| 293 | * PRINT BACKGROUND | ||
| 294 | *********************************************/ | ||
| 295 | @media print { | ||
| 296 | .backgrounds { | ||
| 297 | background-color: #F0F1EB; } } | ||
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> |
