I think about how to use ObjectListView fitting the Model-View-Controller Pattern with wxPython & SQLAlchemy. And I am not sure about it so I created a simple example as a working basis not as a solution.
The concrete question related to the code below is: What should happen if a new MyData object is generated?
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import wx
import sqlalchemy as sa
import sqlalchemy.ext.declarative as sad
import ObjectListView as olv
_Base = sad.declarative_base()
class MyData(_Base):
"""the database table representing class"""
__tablename__ = 'MyData'
__name = sa.Column('name', sa.String, primary_key=True)
__count = sa.Column('count', sa.Numeric(10, 2))
def __init__(self, name, count):
super(MyData, self).__init__()
self.__name = name
self.__count = count
def GetName(self):
return self.__name
def GetCount(self):
return self.__count
def CreateData():
"""
helper creating data
imagnine this as a SELECT * FROM on the database
"""
return [
MyData('Anna', 7),
MyData('Bana', 6)
]
class MyView(olv.ObjectListView):
def __init__(self, parent):
super(MyView, self).__init__(parent, wx.ID_ANY, style=wx.LC_REPORT)
self.SetColumns([
olv.ColumnDefn('Name', valueGetter='GetName'),
olv.ColumnDefn('Count', valueGetter='GetCount')
])
data = CreateData()
self.SetObjects(data)
def ColDef(self):
return
class MyApp(wx.App):
def OnInit(self):
frame = wx.Frame(None)
view = MyView(frame)
frame.Show()
return True
if __name__ == '__main__':
app = MyApp()
app.MainLoop()
What whould you think about... Create a controller "MyDataController" that handle all the sqlalchemy-stuff for MyData objects. e.g. GetAllMyDataObjects AddMyDataObjectToDatabase, QueryMyData, ... Related to the Observer-Pattern the ObjectListView observer the controller as the subject. I am not sure if this is an elegant solution. The point is the confusion about, what is the model? The one (and new) MyData instance or the list of all MyData instances? There is no intelligent list which could act like a model.
With regards to OLV. In my case when an update to the SA model happens I use pubsub to inform the world about the change add/update/delete.
Then my OLV base class subscribes to the 'itemAdded', 'itemModified' and 'itemDeleted' messages and the following are the methods called:
def pubListItemAdded(self, dbitem):
"""
Add list if dbitem instance matches dbScKlass
:param dbitem: an SA model instance
If dbitem instance matches the list controls model it is added.
"""
# protect from PyDeadObjectError
if self:
# E.g. Externalimp is a faked class and does not exist in db
# so we need to protect for that
if hasattr(db, self._klassName):
tList = self.getList()
cInst = getattr(db, self._klassName)
if isinstance(dbitem, cInst):
log.debug("olvbase - added: %s", dbitem)
log.debug("olvbase - added: %s", self)
# for some reason this creates dups on e.g. rating/tasting/consumption
# so, lets check if it is there and only add if not
idx = tList.GetIndexOf(dbitem)
if idx == -1:
tList.AddObject(dbitem)
else:
tList.RefreshObject(dbitem)
# bring it into view
self.resetSelection()
tList.SelectObject(dbitem, deselectOthers=True,
ensureVisible=True)
def pubListItemModified(self, dbitem):
"""
Update list if dbitem instance matches dbScKlass
:param dbitem: an SA model instance
If dbitem instance matches the list controls model it is updated.
"""
# protect from PyDeadObjectError
if self:
# E.g. Externalimp is a faked class and does not exist in db
# so we need to protect for that
if hasattr(db, self._klassName):
cInst = getattr(db, self._klassName)
if isinstance(dbitem, cInst):
log.debug("olvbase - modified: %s", dbitem)
log.debug("olvbase - modified: %s", self)
tList = self.getList()
# need to refresh to ensure relations are loaded
wx.GetApp().ds.refresh(dbitem)
tList.RefreshObject(dbitem)
tList.SelectObject(dbitem, deselectOthers=True,
ensureVisible=True)
# deselect all, so if we select same item again
# we will get a select event
tList.DeselectAll()
def pubListItemDeleted(self, dbitem):
"""
Delete from list if dbitem instance matches dbScKlass
:param dbitem: an SA model instance
If dbitem instance matches the list controls model it is updated.
"""
# protect from PyDeadObjectError
if self:
# E.g. Externalimp is a faked class and does not exist in db
# so we need to protect for that
if hasattr(db, self._klassName):
cInst = getattr(db, self._klassName)
if isinstance(dbitem, cInst):
log.debug("olvbase - deleted: %s", dbitem)
log.debug("olvbase - deleted: %s", self)
tList = self.getList()
tList.RemoveObject(dbitem)
self.currentObject = None
self.currentItemPkey = None