---
# System prepended metadata

title: M06 Accés a dades. ORMs.
tags: [' M06', ' ORM', DAM]

---

---
title: M06 Accés a dades. ORMs.
tags: DAM, M06, ORM
---

[Link en MarkDown](https://hackmd.io/@JdaXaviQ/HyqU6GBoi)

![](https://i.imgur.com/7NeluAg.png "Obtinguda de: https://miro.medium.com/max/2428/1*9TZHTGKjIyUOAvmQkV1RNA.png")


# M06 Accés a dades. UF02. ORMs
## Què és un ORM?
Un ORM (Object-Relational Mapping) és una tècnica de programació que permet als desenvolupadors treballar amb dades d'una base de dades en una forma orientada a objectes. Això significa que les dades de la base de dades es representen com objectes en el codi de l'aplicació, en lloc de treballar amb instruccions SQL directament. Això pot fer que el codi sigui més fàcil de mantenir i més portable entre diferents bases de dades.

L'ús d'ORMs és molt popular avui en dia, en part per la proliferació de frameworks que els incloen a les seves eines. Els ORMs proporcionen una manera de treballar amb dades de bases de dades en una forma orientada a objectes, que pot fer que el codi sigui més fàcil de mantenir i més **portable entre diferents bases de dades**. Això es pot fer sense haver de escriure les consultes SQL directament, cosa que pot ser una gran ajuda per a desenvolupadors que no són experts en SQL.

Existeixen molts ORMs disponibles per a diferents llenguatges de programació, com ara Python, Java, C#, Ruby, i PHP. Alguns dels ORMs més populars són:

    SQLAlchemy per a Python
    Hibernate per a Java
    Entity Framework per a C#
    ActiveRecord per a Ruby
    Eloquent per a PHP

Així doncs, l'ús d'ORMs proporciona una manera fàcil i eficient de treballar amb dades de bases de dades, sense haver d'escriure les consultes SQL directament i independitzant el nostre codi del motor de bases de dades.

### SQLAlchemy
SQLAlchemy és un ORM popular per a Python que permet als desenvolupadors interactuar amb diferents bases de dades utilitzant una interfície d'objectes. Això significa que es pot escriure codi Python que es veu i es comporta com si estigués treballant amb objectes Python normals, en lloc de escriure consultes SQL directament.

Per exemple, per crear una taula en una base de dades, en lloc d'escriure una consulta SQL, es pot utilitzar codi com aquest:
```python=
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

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

engine = create_engine('sqlite:///users.db')
Base.metadata.create_all(engine)
```
Aquest codi crea una taula "users" amb tres columnes: "id", "name" i "age". Aquesta taula es crea en una base de dades SQLite anomenada "users.db".

Per fer una consulta a la taula es pot fer aixi:
```python=
from sqlalchemy.orm import sessionmaker

Session = sessionmaker(bind=engine)
session = Session()

result = session.query(User).filter(User.name == 'John').first()
print(result.id, result.name, result.age)
```
Aquest codi crea una sessió amb l'engine anterior i fa una consulta per seleccionar l'usuari amb nom 'John' i mostra el seu id, nom i edat.

Així doncs, SQLAlchemy fa que sigui fàcil treballar amb dades de bases de dades sense haver de escriure les consultes SQL directament. Això pot fer que el codi sigui més fàcil de mantenir i més portable entre diferents bases de dades

SQLAlchemy inclou suport natiu per a moltes bases de dades populars com ara:

    MySQL
    PostgreSQL
    Oracle
    SQLite
    Microsoft SQL Server
    Firebird
    Sybase

Així doncs, és possible utilitzar SQLAlchemy amb moltes bases de dades diferents, el que fa que sigui una opció molt flexible i portable.

Un altre avantatge d'SQLAlchemy és que permet l'ús de sentències SQL si el programador ho considera adient. Però hem de tenir molta cura si fem ús d'aquest recurs i no perdre la portabilitat entre base de dades.


### Instal·lació d'SQLAlchemy
Per començar a treballar amb SQLAlchemy, primer haureu de tenir Python instal·lat al vostre sistema Linux. Això es pot fer instal·lant el paquet "python" a través del gestor de paquets del vostre sistema.

Després, haureu d'instal·lar SQLAlchemy i el connector per a la base de dades que utilitzeu. Podeu fer-ho utilitzant el gestor de paquets de Python, pip, executant el següent comandament a la línia de comandes:
```python=
pip3 install sqlalchemy
```
Després, depenent de la base de dades que voleu utilitzar, haureu d'instal·lar el connector adequat. Per exemple, si voleu utilitzar una base de dades MySQL, haureu d'instal·lar el connector mysql-python amb la següent comanda:
```python=
pip3 install mysql-python
```
Si voleu utilitzar una base de dades PostgreSQL, haureu d'instal·lar el connector psycopg2 amb la següent comanda:
```python=
pip3 install psycopg2
```
Si voleu utilitzar una base de dades SQLite, no cal instal·lar cap connector especial ja que SQLAlchemy ho inclou per defecte.

Una vegada que hàgiu instal·lat SQLAlchemy i el connector per a la base de dades que voleu utilitzar, ja estareu llestos per començar a treballar amb SQLAlchemy.

### Connexió amb la base de dades
La manera general de connectar amb una base de dades és:
```=
dialect[+driver]://user:password@host/dbname
```
D'aquesta forma per connectar amb una base de dades SQLite utilitzaríem:
```=
engine = create_engine('sqlite:///college.db', echo = True)
```

Si volem connectar amb una base de dades MySql utilitzaríem:
```=
engine = create_engine("mysql://user:pwd@localhost/college",echo = True)
```
Però si volem especificar que utilitzi el driver pymysql:
```=
mysql+pymysql://<username>:<password>@<host>/<dbname>
```
**Nota:** el flag __echo__ a _True_ indica que volem que SQLAlchemy realitzi entrades al logging de l'aplicació, en cas contrari el podem setejar a _None_.

La funció create engine retorna un objecte de tipus [Engine](https://docs.sqlalchemy.org/en/20/core/connections.html#sqlalchemy.engine.Engine)

Alguns mètodes interessants de l'objecte engine són:
1. dispose(): Tanca el pool de connexions amb la base de dades.
2. driver: Ens retorna el nom del driver que està utilitzant.
3. table_names(): Retorna una llista amb el nom de les taules de la base de dades en ús.
### Consultes d'SQLAlchemy amb join
```python=
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Order(Base):
    __tablename__ = 'orders'
    id = Column(Integer, primary_key=True)
    customer_id = Column(Integer, ForeignKey('customers.id'))
    date = Column(String)
    customer = relationship("Customer", back_populates="orders")

class Customer(Base):
    __tablename__ = 'customers'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    orders = relationship("Order", order_by=Order.id, back_populates="customer")

engine = create_engine('sqlite:///db.sqlite')
Base.metadata.create_all(engine)

Session = sessionmaker(bind=engine)
session = Session()

# query using join
result = session.query(Customer, Order).join(Order).filter(Order.date == '2022-01-01').all()

for customer, order in result:
    print(customer.name, order.date)
```
En aquest exemple, s'han creat dos classes, Order i Customer, que representen les taules "orders" i "customers" de la base de dades. Les classes contenen relacions entre elles, customer_id i orders respectivament, i una relació de relació back_populates on Order té una relació amb Customer i aquesta amb Order.

Després, s'utilitza una sessió de SQLAlchemy per fer una consulta que uneix les taules "orders" i "customers" i filtra les files per la data '2022-01-01'. El resultat és una llista de tuples, on cada tuple conté un objecte Customer i un objecte Order associat.

Així, en aquest exemple s'ha pogut fer una consulta amb un join entre les taules "customers" i "orders" i seleccionar les files que compleixen una determinada condició.

### Sessions
En SQLAlchemy, una sessió és un objecte que proporciona una interfície per comunicar-se amb la base de dades. Una sessió és la capa que es posa entre el codi de l'aplicació i la base de dades, i es fa responsable de traduir les operacions de l'aplicació a les operacions de la base de dades.

Una sessió s'encarrega de:

* Gestionar les connexions amb la base de dades.
* Realitzar transaccions (inici, commit i rollback)
* Rastrejar els objectes de l'aplicació que s'han carregat o desat a la base de dades.
* Realitzar consultes a la base de dades i retornar els resultats com a objectes de l'aplicació.
* Sincronitzar els canvis realitzats en els objectes de l'aplicació amb la base de dades (inserir, actualitzar, esborrar)

Així doncs, una sessió és una part important de SQLAlchemy que es fa responsable de la comunicació amb la base de dades, i permet als desenvolupadors treballar amb dades de bases de dades en una forma orientada a objectes.

### Flask amb SQLAlchemy
Flask incorpora a SQLAlchemy com a ORM per defecte, per a començar a utilitzar-lo necessitem instal·lar el següent paquet:
```python=
pip3 install flask-sqlalchemy
```
I ja el tindriem preparat per a utilitzar-lo com en aquest exemple:
```python=
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.debug = True

# adding configuration for using a sqlite database
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'

# Creating an SQLAlchemy instance
db = SQLAlchemy(app)

if __name__ == '__main__':
	app.run()
```

#### Creació dels models
```python=
from flask import Flask, request, redirect
from flask.templating import render_template
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.debug = True

# adding configuration for using a sqlite database
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'

# Creating an SQLAlchemy instance
db = SQLAlchemy(app)

# Models
class Profile(db.Model):
	# Id : Field which stores unique id for every row in
	# database table.
	# first_name: Used to store the first name of the user
	# last_name: Used to store last name of the user
	# Age: Used to store the age of the user
	id = db.Column(db.Integer, primary_key=True)
	first_name = db.Column(db.String(20), unique=False, nullable=False)
	last_name = db.Column(db.String(20), unique=False, nullable=False)
	age = db.Column(db.Integer, nullable=False)

	# repr method represents how one object of this datatable
	# will look like
	def __repr__(self):
		return f"Name : {self.first_name}, Age: {self.age}"

if __name__ == '__main__':
	app.run()
```

#### Generació de la base de dades
```bash
$ python3
from app import db
db.create_all()
```
I ja hauriem de tenir el fitxer site.db a la nostra carpeta.

#### Migracions
Les migracions ens permeten mantenir la nostra base de dades al nostre sistema. És un procés per mantenir les estructures de les taules de la base de dades sincronitzades amb les classes de models de l'aplicació. Les migracions s'utilitzen per fer canvis en les estructures de les taules, com ara afegir o eliminar columnes, crear o eliminar taules, i així successivament, sense perdre les dades existents en la base de dades.

SQLAlchemy no proporciona un sistema de migracions integrat, però hi ha diferents eines de tercers que es poden utilitzar per a aquesta finalitat, com ara Alembic o Flask-Migrate. Aquestes eines proporcionen comandaments per crear i aplicar migracions, i es fan responsables de mantenir un registre de les migracions aplicades per assegurar que les estructures de les taules estiguin sincronitzades amb les classes de models de l'aplicació.

#### Proves amb JupyterNotebook
[Link to Google colab](https://colab.research.google.com/drive/1ZpNFiktv_3OZtNrgxAc0VBJXgTw_VRE4)