7

我的问题实际上是如何设置可以从 PySide 的 QTableView 类访问的 SQLAlchemy 声明性模型。

我只是想从本质上实现对象关系教程的前端

不幸的是,我有几点困惑。我会试着解释我在哪里。

我遵循了 SQLAlchemy 教程,直到我有两个相关的表,并且可以毫无问题地操作/查询这些表。尝试建立QTableView 类显然需要使用我自己的模型的setData() 方法,或者使用默认模型需要setItem() 方法

所以问题是如何设计模型。我认为这意味着定义这两种方法之一来查询/修改数据库。我不知道这样做的正确方法。

该模型应该像 User firstname 和 lastname 在几行上重复,直到显示所有地址,然后转到下一个 User。我可以使用嵌套的 for 循环来执行此操作,以便在提示符下打印此内容,但我不认为制作一个大列表是要走的路,因为这似乎违背了首先拥有数据库的意义......

我也不知道当数据库增长时会发生什么,整个表会被实例化并保存在内存中,还是 Qt 在用户滚动时加载行和列以查看它们?

很多文字在这里很抱歉,但试图清楚。如果有任何其他我可以添加的内容,请告诉我。或者,如果我完全走错了路......

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
4

1 回答 1

7

首先,让我们忘记查询,并使用您正在使用的循环。您在 UI 中寻找的是基本的东西。我从缺乏文档中发现,对于基本的事情,最好使用比 QWhateverView 更好的QTableWidget(或任何东西)。QWhateverWidget您不需要为简单的事情定义自己的模型。因此,我将向您展示如何使用 QTableWidget 来做到这一点。您需要为(行,列)的每个单元格创建一个 QTableWidgetItem。我遇到的一个问题是在添加它们之前必须设置行数。无论如何,这里:

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_()

您可能会考虑使用的另一件事是QTreeWidget,它支持多列,您可以让它看起来像一个表格,但默认情况下没有可编辑的单元格,它可能更适合您的数据。

现在对于查询,您可能希望将其设为一个查询,以避免循环遍历结果并且必须为每个用户进行一个额外的查询。就像是:

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)

对于很多行,我认为有一个解决方案,但是您需要定义自己的模型并LIMIT在查询中使用 s 。由于缺乏文档和教程,这并不容易......

附带说明一下,您不需要__init__在 Address 和 User 类上定义方法,因为您没有做任何特别的事情,SQLAlchemy 可以自动为您完成。您可以直接在 Column 定义中定义默认值。

更新:对于使用QTableWidgetItem.setData的示例,假设我们要在双击时删除用户。我们将使用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()
于 2012-05-16T09:24:01.703 回答