2011-06-09 15 views
31

sqlautocode - tiene problemas con muchos-a-muchos relación¿Cómo se refleja automáticamente la base de datos a declarativo sqlalchemy?

sqlsoup - no admite las relaciones

elixir - se nota generar automáticamente

¿Hay alguna otra cosa que podría intentar?

+1

¿Qué estás tratando de hacer exactamente? Si lo entiendo, ¿desea crear referencias declartivas a partir de tablas de bases de datos que no hayan sido definidas por un modelo sqlalchemy? No estoy seguro de que pueda llegar al punto declarativo, pero puede reflejar las propiedades de la tabla (creo que esto incluye cosas como [claves externas] (http://www.sqlalchemy.org/docs/05/metadata.html#reflecting). -tables)) – Raceyman

+1

Como se señala en [esta respuesta] (http://stackoverflow.com/a/14403228/3079302), SQLAlchemy tiene la [extensión Automap] (http://docs.sqlalchemy.org/en/latest /orm/extensions/automap.html?highlight=automap#module-sqlalchemy.ext.automap) desde la versión 0.9.1. Desde los documentos: defina una extensión para el sistema 'sqlalchemy.ext.declarative' que genera automáticamente clases y relaciones mapeadas a partir de un esquema de base de datos, aunque normalmente no necesariamente reflejado. – iled

Respuesta

21

Bueno, pasé por eso, probé en la base de datos de Northwind y parece prometedor. Aunque, tuve que agregar un campo de relación para poder seguir las relaciones con la base de datos.

Consideremos que no conozco las relaciones entre tablas en el momento de iniciar la aplicación, así que lo que necesito es una forma de generar automáticamente.

import unittest 

from sqlalchemy import * 
from sqlalchemy.orm import create_session 
from sqlalchemy.ext.declarative import declarative_base 
from datetime import datetime 
from sqlalchemy.orm import contains_eager, joinedload 
from sqlalchemy.orm import relationship 

#Create and engine and get the metadata 
Base = declarative_base() 
engine = create_engine('mssql://user:[email protected]', echo=True) 
metadata = MetaData(bind=engine) 


#Reflect each database table we need to use, using metadata 
class Customer(Base): 
    __table__ = Table('Customers', metadata, autoload=True) 
    orders = relationship("Order", backref="customer") 

class Shipper(Base): 
    __table__ = Table('Shippers', metadata, autoload=True) 
    orders = relationship("Order", backref="shipper") 

class Employee(Base): 
    __table__ = Table('Employees', metadata, autoload=True) 
# orders = relationship("Order", backref="employee") 
    territories = relationship('Territory', secondary=Table('Employeeterritories', metadata, autoload=True)) 

class Territory(Base): 
    __table__ = Table('Territories', metadata, autoload=True) 
    region = relationship('Region', backref='territories') 

class Region(Base): 
    __table__ = Table('Region', metadata, autoload=True) 


class Order(Base): 
    __table__ = Table('Orders', metadata, autoload=True) 
    products = relationship('Product', secondary=Table('Order Details', metadata, autoload=True)) 
    employee = relationship('Employee', backref='orders') 

class Product(Base): 
    __table__ = Table('Products', metadata, autoload=True) 
    supplier = relationship('Supplier', backref='products') 
    category = relationship('Category', backref='products') 

class Supplier(Base): 
    __table__ = Table('Suppliers', metadata, autoload=True) 

class Category(Base): 
    __table__ = Table('Categories', metadata, autoload=True) 


class Test(unittest.TestCase): 

    def setUp(self): 
     #Create a session to use the tables  
     self.session = create_session(bind=engine)   

    def tearDown(self): 
     self.session.close() 

    def test_withJoins(self): 
     q = self.session.query(Customer) 
     q = q.join(Order) 
     q = q.join(Shipper) 
     q = q.filter(Customer.CustomerID =='ALFKI') 
     q = q.filter(Order.OrderID=='10643') 
     q = q.filter(Shipper.ShipperID=='1') 
     q = q.options(contains_eager(Customer.orders, Order.shipper)) 
     res = q.all() 
     cus = res[0] 
     ord = cus.orders[0] 
     shi = ord.shipper 
     self.assertEqual(shi.Phone, '(503) 555-9831') 
66

En teoría, la reflexión en sqlalchemy debería funcionar para usted. En este caso estoy usando una base de datos mssql con dos mesas que tienen un simple Muchos-a-uno relación:

"Pruebas" con los campos:

  • Identificación
  • nombre_prueba
  • author_id (clave externa a la tabla Usuarios, campo Users.id)

"Usuarios" con los campos:

  • Identificación
  • nombre_completo

Así que el siguiente debe reflejar la base de datos:

from sqlalchemy import * 
from sqlalchemy.orm import create_session 
from sqlalchemy.ext.declarative import declarative_base 

#Create and engine and get the metadata 
Base = declarative_base() 
engine = create_engine('put your database connect string here') 
metadata = MetaData(bind=engine) 

#Reflect each database table we need to use, using metadata 
class Tests(Base): 
    __table__ = Table('Tests', metadata, autoload=True) 

class Users(Base): 
    __table__ = Table('Users', metadata, autoload=True) 

#Create a session to use the tables  
session = create_session(bind=engine) 

#Here I will just query some data using my foreign key relation, as you would 
#normally do if you had created a declarative data mode. 
#Note that not all test records have an author so I need to accomodate for Null records 
testlist = session.query(Tests).all()  

for test in testlist: 
    testauthor = session.query(Users).filter_by(id=test.author_id).first() 
    if not testauthor: 
     print "Test Name: {}, No author recorded".format(test.testname) 
    else: 
     print "Test Name: {}, Test Author: {}".format(test.testname, testauthor.fullname) 

Así que esto parece funcionar con relaciones entre tablas. Aunque todavía no has dado muchos detalles sobre exactamente lo que estás tratando de hacer.

+2

Este ejemplo no muestra el uso de una relación entre las tablas en el nivel del objeto, ¿verdad? en otras palabras, el hecho de que author_id sea una clave externa no es suficiente para que SQLAlchemy tenga automáticamente 'test.fullname' como el correcto' testauthor.fullname', ¿no? – EOL

+0

¿Hay bibliotecas para hacer este tipo de reflexión en otros lenguajes de programación además de python + SQLAlchemy? ¡Este increíble! – maxm

+1

La definición de MetaData no es necesaria.Creating Base también crea una instancia de MetaData a la que puedes acceder a través de 'Base.metadata'. – rgtk

23

Puede usar sqlacodegen para generar todos los modelos de la base de datos. Sin embargo, debe cuidar la clave externa manualmente.

+0

Esto me ayudó. ¡Gracias! – shaffooo

Cuestiones relacionadas