Herramientas de usuario

Herramientas del sitio


fw:elixir

¡Esta es una revisión vieja del documento!


Elixir

Básico

Inicio

Pasos

  1. Importaremos los elementos de Elixir mediante from elixir import *.
  2. Indicaremos la base de datos mediante metadata.bind
  3. Definiremos el modelo con clases Entity y relaciones.
  4. Crearemos el modelo con métodos setup y create.

Definir la base de datos

Definición de una DB SQLite

Para indicar que nuestra base de datos será una SQLite en un fichero con nombre movies.sqliteque está en el directorio del usuario haremos:

metadata.bind = "sqlite:///movies.sqlite"

Crear el modelo

Una vez tengamos el modelo definido utilizaremos los métodos siguientes:

  • setup_all enlaza los objetos tabla que hemos indicado, es el método que tendremos que llamar para iniciar un modelo (tanto si existe como si no). Si le pasásemos True como argumento crearía todas las tablas de una vez.
  • create_all lanza los SQL de creación a la base de datos.

Cosas a saber

  • Si metadata.bind.echo es asignado a True se irá mostrando por consola las acciones que se vayan realizando.
  • Si una clase Entity tiene el método __repr__ definido Elixir mostrará sus objetos por consola tal y como este devuelva en formato string.

Ejemplo de definición

Definiremos el modelo en un archivo denominado model.py que contendrá lo siguiente:

from elixir import *
 
metadata.bind = "sqlite:///movies.sqlite"
metadata.bind.echo = True
 
class Movie(Entity):
    title = Field(Unicode(30))
    year = Field(Integer)
    description = Field(UnicodeText)
 
    def __repr__(self):
        return '<Movie "%s" (%d)>' % (self.title, self.year)

Cuando queramos que se cree la DB ejecutaremos:

from model import *
setup_all()
create_all()

Manipulación de registros

Manipularemos los registros a partir de objetos de los tipos que hayamos definido. Cuando llamemos a session.commit() se ejecutarán los comandos sobre la DB.

Inserción de registros

Una vez llamado a session.commit() se insertarán los objetos de los tipos de las tablas que hemos creado en memoria.
Agregaríamos un registro película de la siguiente forma:

Movie(title=u"Blade Runner", year=1982)
session.commit()

Modificación de registros

movie = Movie.query.first() 
movie.year = 1983
session.commit()

Eliminación de registros

movie = Movie.query.first() 
movie.delete()
session.commit()

Elementos del modelo

Tabla

Para crear una tabla definiremos una clase que herede de Entity. Los campos serán atributos del tipo Field a los cuales les indicaremos el tipo.

class Movie(Entity):
    title = Field(Unicode(30))
    year = Field(Integer)
    description = Field(UnicodeText)

Tipos de datos posibles

  • Unicode(n), texto de hasta n carácteres.
  • Integer
  • UnicodeText, texto largo.
  • BigInteger
  • Boolean
  • Date
  • DateTime
  • Enum, para hacer enumeraciones:
class MyStuff(Entity):
    e = Field(Enum(u'foobar', u'baz', u'quux', None))

Sobre los campos...

  • Podemos indicar que un campo sea clave primaria pasándole por parámetro primary_key=True. Si no lo hacemos la clave se generará una automáticamente.
  • Si indicamos en dos campos que sean claves primarias entonces esta será compuesta por los dos.
  • Podemos indicar que un campo es único utilizando la propiedad unique=True.
class Person(Entity):
    his_unique_number = Field(Integer, unique=True)
  • Podemos indicar si un campo puede ser nulo mediante el parámetro nullable.
  • Podemos indicar que se indexe un campo agregando el parámetro index=True:
class Article(Entity):
    aid = Field(UnicodeText, index = True)
    title = Field(UnicodeText, nullable = True)
  • Si se crea el campo identificador automáticamente será accesible desde la propiedad id.

Relaciones

Para indicar las relaciones entre clases\tablas utilizaremos las funciones ManyToOne y OneToMany, y ManyToMany según la cardinalidad:

class Movie(Entity):
    title = Field(Unicode(30))
    year = Field(Integer)
    description = Field(UnicodeText)
    director = ManyToOne('Director')
 
    def __repr__(self):
        return '<Movie "%s" (%d)>' % (self.title, self.year)
 
class Director(Entity):
    name = Field(Unicode(60))
    movies = OneToMany('Movie')
 
    def __repr__(self):
        return '<Director "%s">' % self.name

A partir de ahora podremos insertar los datos de formas distintas:

rscott = Director(name=u"Ridley Scott")
glucas = Director(name=u"George Lucas")
alien = Movie(title=u"Alien", year=1979)
swars = Movie(title=u"Star Wars", year=1977)
brunner = Movie(title=u"Blade Runner", year=1982)
 
rscott.movies.append(brunner) 
rscott.movies.append(alien)
 
swars.director = glucas

Para las relaciones muchos a muchos utilizaremos ManyToMany:

class Genre(Entity):
    name = Field(Unicode(15), primary_key=True)
    movies = ManyToMany('Movie')
 
    def __repr__(self):
        return '<Genre "%s">' % self.name
 
class Movie(Entity):
    title = Field(Unicode(30), primary_key=True) 
    year = Field(Integer, primary_key=True)      
    description = Field(UnicodeText)
    director = ManyToOne('Director')
    genres = ManyToMany('Genre')                 
 
    def __repr__(self):
        return '<Movie "%s" (%d)>' % (self.title, self.year)
 
scifi = Genre(name=u"Science-Fiction")
rscott = Director(name=u"Ridley Scott")
glucas = Director(name=u"George Lucas")
alien = Movie(title=u"Alien", year=1979, director=rscott, genres=[scifi, Genre(name=u"Horror")])
brunner = Movie(title=u"Blade Runner", year=1982, director=rscott, genres=[scifi])
swars = Movie(title=u"Star Wars", year=1977, director=glucas, genres=[scifi])

Si quisieramos que en una relación ManyToOne la foreign key sea parte de la primary key haremos…

class User(Entity):
    name = Field(String(30), primary_key=True)
class Post(Entity):
    id = Field(Integer, primary_key=True)
    user = ManyToOne('User', primary_key=True)

En el siguiente ejemplo User no contiene clave privada, en Post la clave primaria es compuesta por id y user_name, que será generado.

Para que una tabla tenga referencias consigo misma:

class Employee(Entity):
    name = Field(String(32))
    supervisor = ManyToOne('Employee')
    employees = OneToMany('Employee')  # esta línea es opcional
 
class  User(Entity):
    using_options(tablename="user")
    friends = ManyToMany('User', inverse="is_friend_of")
    is_friend_of = ManyToMany('User', inverse="friends")
    enemies = ManyToMany('User', inverse="is_enemy_of")
    is_enemy_of = ManyToMany('User', inverse="enemies")

Opciones

A partir de las opciones podemos indicar a Elixir cómo crear las tablas, para ello utilizaremos using_options en la definición de la tabla:

class Movie(Entity):
    using_options(tablename='movies')
    title = Field(Unicode(30))
    year = Field(Integer)
    description = Field(UnicodeText)

Las propiedades que podemos definir son:

  • tablename, el nombre de la tabla que ha de crear.
  • inheritance, el tipo de herencia de clases.
  • autoload, carga la definición de columnas de una DB ya existente.
  • shortnames, indica el tipo de generación de los nombres de tablas, si su valor es False (por defecto) se agregará el nombre del módulo al que pertenece.
  • auto_primarykey, se indica el nombre de la columna que será el identificador (automático).
  • order_by es un string (o lista de strings) correspondientes a los nombres de columna a partir de los cuales se ordenarán los resultados.
  • session indica el tipo de sesión para el contexto de la entidad.

Herencia entre tablas

Para ello haremos la herencia entre clases:

class Person(Entity):
    using_options(inheritance='multi')
    name = Field(Unicode(60))
 
    def __repr__(self):
        return '<Person "%s">' % self.name
 
class Actor(Person):
    using_options(inheritance='multi')
    movies = ManyToMany('Movie')
 
    def __repr__(self):
        return '<Actor "%s">' % self.name
 
class Director(Person):
    using_options(inheritance='multi')
    movies = OneToMany('Movie')
 
    def __repr__(self):
        return '<Director "%s">' % self.name

Elixir soporta la herencia del tipo single table y joined/multi-table de SQLAlchemy, en este ejemplo se ha utilizado la multi-table, lo que significa que para cada cada una de las entidades se generará una sola tabla.
Es obligatorio indicar en cada una de las entidades (incluyendo el padre) qué tipo de herencia tendrá.

>>> rscott = Director(name=u"Ridley Scott")
>>> glucas = Director(name=u"George Lucas")
>>> hford = Actor(name=u"Harrison Ford")
>>> mhamill = Actor(name=u"Mark Hamill")
>>> sweaver = Actor(name=u"Sigourney Weaver")
>>> session.commit()
 
>>> Person.query.all()
[<Director "Ridley Scott">,
 <Director "George Lucas">,
 <Actor "Harrison Ford">,
 <Actor "Mark Hamill">,
 <Actor "Sigourney Weaver">]
 
>>> Actor.query.all()
[<Actor "Harrison Ford">, <Actor "Mark Hamill">, <Actor "Sigourney Weaver">]

Consultas

Aunque las consultas se basan en las de SQLAlchemy y podemos lanzar con ese formato Elixir también nos agiliza dicha tarea.

Consultas básicas

  • Recoger todos: clase.query.all()
  • Recoger el primero: clase.query.first()

Filtros

Movie.query.filter_by(title=u"Alien").one()
Movie.query.filter(Movie.year > 1980).all()
Movie.query.filter(Movie.director.has(name=u'Ridley Scott')).all()
Movie.query.filter(Movie.director.has(Director.name.endswith(u'Scott'))).all()
Movie.query.filter(Movie.genres.any(name=u"Horror")).all()

Generar una query

d = Director.get_by(name=u'Ridley Scott') # Class.get_by(xxx) is a shortcut for Class.query.filter_by(xxx).first()
q = Movie.query.filter_by(director=d)
q.filter_by(year=1979).all()
from sqlalchemy import desc
q.order_by(desc(Movie.year)).all()

Otros

Consultar por identificador

e = Element.get(identifier)

Consultar una lista vacía

vendors = Vendors.query.filter(~Vendors.items.any())

Lanzar código SQL directamente

from elixir import *
for result in session.execute('select * from people'):
     print result['name']

Avanzado

Sesiones

Elixir utilizar una sesión global y todas las entidades de Elixir están linkadas a esta. Si se quiere usar otra sesión para otras entindades únicamente se ha de indicar utilizando la opción adecuada.

Notas

Otros

  • Ejecutar SQL al iniciar la aplicación:
class MySetup:
   def connect(self, dbapi_con, con_record):
       dbapi_con.execute('alter session set NLS_SORT=BINARY_CI')
       dbapi_con.execute('alter session set NLS_COMP=LINGUISTIC')
engine = create_engine('oracle:...', listeners=[MySetup()])
elixir.metadata.bind = engine
fw/elixir.1321872451.txt.gz · Última modificación: 2020/05/09 09:24 (editor externo)