2012-05-16 14 views
6

Mi pregunta es realmente cómo configurar un modelo SQLAlchemy-declarativo al que se puede acceder desde la clase QTableView de PySide.Diseño del 'modelo' para QTableView en PySide + SQLAlchemy.

sólo estoy tratando de poner en práctica esencialmente una interfaz del Object Relational tutorial

Por desgracia, tengo un par de puntos de confusión. Trataré de explicar dónde estoy.

He seguido el tutorial de SQLAlchemy hasta el punto en que tengo dos tablas relacionadas y puedo manipular/consultar aquellas sin problemas. Intentar establecer un QTableView class aparentemente requiere un setData() method con mi propio modelo, o usar el modelo predeterminado requiere un setItem() method.

Entonces la pregunta es cómo diseñar el modelo. Supongo que esto significa definir uno de esos dos métodos para consultar/modificar la base de datos. No sé la manera correcta de hacer esto.

Se supone que el modelo es como el nombre de usuario y el apellido repetidos en algunas filas hasta que se hayan mostrado todas las direcciones, luego pase al siguiente usuario. Puedo hacer esto con los bucles anidados para imprimir esto en el prompt, pero no creo que hacer una gran lista es el camino a seguir, ya que eso parece frustrar el punto de tener la base de datos en primer lugar ...

Tampoco sé qué sucederá cuando la base de datos crezca, ¿se crea una instancia de toda la tabla y se mantiene en la memoria o Qt carga filas y columnas cuando aparecen cuando el usuario se desplaza?

Gran cantidad de texto aquí lo siento, pero tratando de ser claro. Si hay cosas adicionales que pueda agregar, házmelo saber. O si estoy totalmente en el camino equivocado ...

from PySide import QtCore, QtGui 
from sqlalchemy import Column, Integer, String, Text, Sequence, ForeignKey, Date, Boolean, create_engine 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy.orm import sessionmaker, relationship, backref, aliased 
import datetime 


engine = create_engine('sqlite:///reminder.db') 

Base = declarative_base() 

class User(Base): 
    __tablename__ = 'users_db' 
    id = Column(Integer, Sequence('user_id_seq'), primary_key=True) 
    lastname = Column(String) 
    firstname = Column(String) 
    contact = Column(String) 
    history = Column(Text) 
    notes = Column(Text) 

    addresses = relationship('Address', order_by='Address.id', 
           backref='user', cascade='all, delete, delete-orphan') 


    def __init__(self, firstname, lastname, contact): 
     self.firstname = firstname 
     self.lastname = lastname 
     self.contact = contact 

    def __repr__(self): 
     return "<User('{0}', '{1}', '{2}')>".format(self.firstname, self.lastname, self.contact) 


class Address(Base): 
    __tablename__ = 'addresses_db' 
    id = Column(Integer, primary_key=True) 
    address = Column(String(150)) 
    date = Column(Date) 
    check1 = Column(Boolean) 
    check2 = Column(Boolean) 

    user_id = Column(Integer, ForeignKey('users_db.id')) 

    def __init__(self, address, date): 
     self.address = address 
     self.date = date 
     self.check1 = False 
     self.check2 = False 

    def __repr__(self): 
     return "<Address('{0}', '{1}')>".format(self.address, self.date) 

if __name__ == '__main__': 
    Base.metadata.create_all(engine) 
    Session = sessionmaker(bind=engine) 
    session = Session() 
    header = [User.firstname, User.lastname, nextaddressfromUser] 

>>> for user in session.query(User).all(): 
...  for addr in user.addresses: 
...   print user.firstname, user.lastname, addr.address 

Respuesta

6

Primero, olvidemos la consulta y utilicemos el bucle que estamos utilizando. Lo que estás buscando en la interfaz de usuario es algo básico. He encontrado por la falta de documentos, que es mejor usar un QTableWidget (o cualquier cosa QWhateverWidget) mejor que QWhateverView para cosas básicas. No necesita definir su propio modelo para cosas simples. Entonces, te mostraré cómo hacerlo con QTableWidget. Necesita crear un QTableWidgetItem para cada celda en (fila, columna). Un problema que encontré fue tener que establecer el número de filas antes de agregarlas. De todos modos, aquí:

import sys 
from PySide import QtGui, QtCore 

class Test(QtGui.QWidget): 

    def __init__(self, rows): 
     super(Test, self).__init__() 

     self.table = QtGui.QTableWidget() 
     self.table.setColumnCount(3) 
     # Optional, set the labels that show on top 
     self.table.setHorizontalHeaderLabels(("First Name", "Last Name", "Address")) 

     self.table.setRowCount(len(rows)) 
     for row, cols in enumerate(rows): 
      for col, text in enumerate(cols): 
       table_item = QtGui.QTableWidgetItem(text) 
       # Optional, but very useful. 
       table_item.setData(QtCore.Qt.UserRole+1, user) 
       self.table.setItem(row, col, table_item) 

     # Also optional. Will fit the cells to its contents. 
     self.table.resizeColumnsToContents() 

     # Just display the table here. 
     layout = QtGui.QHBoxLayout() 
     layout.addWidget(self.table) 
     self.setLayout(layout) 

if __name__ == "__main__": 
    # ... 
    rows = [] 
    # Here I have to fill it in an array, because you need to know the number of rows before adding... There might be a better solution though. 
    for user in session.query(User).all(): 
     for addr in user.addresses: 
      # These are the columns on each row (firstname, lastname, address) 
      rows.append((user.firstname, user.lastname, addr.address)) 

    app = QtGui.QApplication(sys.argv) 
    test = Test(rows) 
    test.show() 
    app.exec_() 

Otra cosa que podría considerar el uso de un QTreeWidget, que es compatible con múltiples columnas, y se puede hacer que se vea como una mesa, pero sin las celdas editables por defecto, y puede ser que se adapte a sus datos Mejor aqui.

Ahora, para la consulta, es posible que desee hacer una sola consulta para evitar el bucle de los resultados y tener que realizar una consulta adicional para cada usuario. Algo así como:

query = session.query(User.firstname, User.lastname, Address.address).filter(Address.user_id == User.id) 
    for row in query.all(): 
     # firstname, lastname, address = row 
     rows.append(row) 

Para una gran cantidad de filas, creo que hay una solución, pero entonces tendría que definir su propio modelo y utilizar LIMIT s en sus consultas. Con la falta de documentos y tutoriales, no es tan fácil ...

Y como nota al margen, no es necesario definir el método __init__ en sus clases de Dirección y Usuario, ya que no está haciendo nada especial , SQLAlchemy puede hacer esto automáticamente por ti. Y puede definir valores predeterminados directamente en la definición de Columna.

ACTUALIZACIÓN: para un ejemplo del uso de QTableWidgetItem.setData, supongamos que queremos eliminar al usuario cuando se hace doble clic en él. Usaremos la señal itemDoubleClicked.

# in the __init__ function 
self.table.itemDoubleClicked.connect(self.onItemDoubleClick) 

# in onItemDoubleClicked function 
def onItemDoubleClicked(self, item): 
    # Every data has a role, you can specify your own (it's an integer) as long as it's greater than UserRole. Others are used internally, like DisplayRole and some others you can find in the QtCore package. 
    # You can use data with other widgets also, not just TableWidgets. 
    user = item.data(QtCore.Qt.UserRole+1) 
    # you get a session however you want, then delete the user. This object is the same as the one you passed earlier when creating the item, it can be whatever you like. 
    session.delete(user) 
    session.commit() 
+0

Se ve bien. Es tarde, así que no puedo probarlo ahora. ¿Puede explicar table_item.setData (QtCore.Qt.UserRole + 1, user) – jbbiomed

+0

Esto se utiliza para que pueda manipular el objeto User y no solo mostrar su nombre ... (por ejemplo). Actualizaré mi respuesta para mostrar un ejemplo. – jadkik94

+0

genial gracias por explicar. Supongo que solo está siendo sucinto en el primer código que publicó y que puedo agregar fácilmente una referencia al usuario correcto en el lugar correcto y que no hay magia que esté usando. – jbbiomed

Cuestiones relacionadas