SQLAlchemy

SQLAlchemy Tutorial with Python by Vinay Kudari

What is SQLAlchemy

SQLAlchemy is the Python SQL toolkit and Object Relational Mapper that gives application developers the full power and flexibility of SQL.

Object Relational Mapper (ORM):

![Object Relational Mapping PowerPoint Template SketchBubble](https://azatai.s3.amazonaws.com/2020-08-09-141522.png)

It provides a full suite of well known enterprise-level persistence patterns, designed for efficient and high-performing database access, adapted into a simple and Pythonic domain language.

QLAlchemy considers the database to be a relational algebra engine, not just a collection of tables. Rows can be selected from not only tables but also joins and other select statements; any of these units can be composed into a larger structure. SQLAlchemy’s expression language builds on this concept from its core.

SQLAlchemy is most famous for its object-relational mapper (ORM), an optional component that provides the data mapper pattern, where classes can be mapped to the database in open ended, multiple ways - allowing the object model and database schema to develop in a cleanly decoupled way from the beginning.

The main goal of SQLAlchemy is to change the way you think about databases and SQL!

ORM: Rethinking Data as Objects. Object-Relational Mapping (ORM ...

Overview

The SQLAlchemy SQL Toolkit and Object Relational Mapper is a comprehensive set of tools for working with databases and Python.

_images/sqla_arch_small.png

Above, the two most significant front-facing portions of SQLAlchemy are the Object Relational Mapper and theSQL Expression Language. SQL Expressions can be used independently of the ORM. When using the ORM, the SQL Expression language remains part of the public facing API as it is used within object-relational configurations and queries.

Object Relational Tutorial

The SQLAlchemy Object Relational Mapper presents a method of associating user-defined Python classes with database tables, and instances of those classes (objects) with rows in their corresponding tables. It includes a system that transparently synchronizes all changes in state between objects and their related rows, called a unit of work, as well as a system for expressing database queries in terms of the user defined classes and their defined relationships between each other.

Unit of Work Design Pattern - CodeProject

The ORM is in contrast to the SQLAlchemy Expression Language, upon which the ORM is constructed. Whereas the SQL Expression Language, introduced in SQL Expression Language Tutorial, presents a system of representing the primitive constructs of the relational database directly without opinion, the ORM presents a high level and abstracted pattern of usage, which itself is an example of applied usage of the Expression Language.

contrast:

img

SQL Expression Language:

While there is overlap among the usage patterns of the ORM and the Expression Language, the similarities are more superficial than they may at first appear. One approaches the structure and content of data from the perspective of a user-defined domain model which is transparently persisted and refreshed from its underlying storage model. The other approaches it from the perspective of literal schema and SQL expression representations which are explicitly composed into messages consumed individually by the database.

Overlap:

overlap - Liberal Dictionary

domain model:

A domain model in problem solving and software engineering is a conceptual model of all the topics related to a specific problem. It describes the various entities, their attributes, roles, and relationships, plus the constraints that govern the problem domain.

A successful application may be constructed using the Object Relational Mapper exclusively. In advanced situations, an application constructed with the ORM may make occasional usage of the Expression Language directly in certain areas where specific database interactions are required.

The following tutorial is in doctest format, meaning each >>> line represents something you can type at a Python command prompt, and the following text represents the expected return value.

Version Check

import sqlalchemy
sqlalchemy.__version__

image-20200809210943107

Connecting

from sqlalchemy import create_engine
engine = create_engine('sqlite:///:memory:',echo=True)

The echo flag is a shortcut to setting up SQLAlchemy logging, which is accomplished via Python’s standard logging module. With it enabled, we’ll see all the generated SQL produced.

image-20200809211303452

The return value of create_engine() is an instance of Engine, and it represents the core interface to the database, adapted through a dialect that handles the details of the database and DBAPI in use. In this case the SQLite dialect will interpret instructions to the Python built-in sqlite3 module.

database storage engines:

img

DBAPI :

Python DB-API Quick Reference

The first time a method like Engine.execute() or Engine.connect() is called, the Engine establishes a real DBAPI connection to the database, which is then used to emit the SQL. When using the ORM, we typically don’t use the Engine directly once created; instead, it’s used behind the scenes by the ORM as we’ll see shortly.

Lazy Connecting

The Engine, when first returned by create_engine(), has not actually tried to connect to the database yet; that happens only the first time it is asked to perform a task against the database.

Declare a Mapping

When using the ORM, the configurational process starts by describing the database tables we’ll be dealing with, and then by defining our own classes which will be mapped to those tables. In modern SQLAlchemy, these two tasks are usually performed together, using a system known as Declarative, which allows us to create classes that include directives to describe the actual database table they will be mapped to.

Configurational process :

  1. Describing the database
  2. Defining the class that will be mapped to the database tables.

Classes mapped using the Declarative system are defined in terms of a base class which maintains a catalog of classes and tables relative to that base - this is known as the declarative base class. Our application will usually have just one instance of this base in a commonly imported module. We create the base class using the declarative_base() function, as follows:

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

image-20200809213740480

Now that we have a “base”, we can define any number of mapped classes in terms of it. We will start with just a single table called users, which will store records for the end-users using our application. A new class called Userwill be the class to which we map this table. Within the class, we define details about the table to which we’ll be mapping, primarily the table name, and names and datatypes of columns:

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    fullname = Column(String)
    nickname = Column(String)

    def __repr__(self):
        return "<User(name='%s', fullname='%s', nickname='%s')>" % (self.name, self.fullname, self.nickname)

A class using Declarative at a minimum needs a __tablename__ attribute, and at least one Column which is part of a primary key 1. SQLAlchemy never makes any assumptions by itself about the table to which a class refers, including that it has no built-in conventions for names, datatypes, or constraints. But this doesn’t mean boilerplate is required; instead, you’re encouraged to create your own automated conventions using helper functions and mixin classes, which is described in detail at Mixin and Custom Base Classes.

The User class defines a __repr__() method, but note that is optional;

4 Boilerplate SEO Tips That Haven't Changed Over The Years - Nine ...

Mixin:

Mixins - Learning JavaScript Design Patterns [Book]

When our class is constructed, Declarative replaces all the Column objects with special Python accessors known as descriptors; this is a process known as instrumentation. The “instrumented” mapped class will provide us with the means to refer to our table in a SQL context as well as to persist and load the values of columns from the database.

![Declarative Sentence (statement) Grammar EnglishClub](https://azatai.s3.amazonaws.com/2020-08-09-155850.png)
![Instrumentation database architecture. Download Scientific Diagram](https://azatai.s3.amazonaws.com/2020-08-09-160223.png)

Outside of what the mapping process does to our class, the class remains otherwise mostly a normal Python class, to which we can define any number of ordinary attributes and methods needed by our application.

Further information posted here:

Sample Python Code:

import sqlalchemy as sa
from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship

from sqlalchemy.orm import sessionmaker

engine = create_engine("postgresql://sqlalchemy:sqlalchemy@localhost/sqlalchemy", echo=True)

Base = declarative_base()

Session = sessionmaker(bind=engine)

session = Session()


class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    fullname = Column(String)
    nickname = Column(String)

    addresses = relationship("Address", back_populates="user")

    def __repr__(self):
        return f"<User(name = {self.name},fullname={self.fullname}, nickname={self.nickname})>"


class Address(Base):
    __tablename__ = 'addresses'
    id = Column(Integer, primary_key=True)
    email_address = Column(String, nullable=False)
    user_id = Column(Integer, ForeignKey('users.id'))

    user = relationship("User", back_populates='addresses')

    def __repr__(self):
        return f"<Address(email_address={self.email_address})>"


Base.metadata.create_all(engine)

jack = User(name='jack', fullname='Jack bean', nickname='jk')
print(jack.addresses)

jack.addresses = [
    Address(email_address='[email protected]'),
    Address(email_address='[email protected]')
]

print(jack.addresses[1])

session.add(jack)
# session.commit()

for instance in session.query(User).order_by(User.id):
    print(instance.addresses)

for u, a in session.query(User, Address).filter(User.id == Address.user_id).filter(
        Address.email_address == '[email protected]').all():
    print(u, a)
# session.commit()

session.delete(jack)
session.commit()
session.close()