Here is an example of two database entities which share a one-to-one relationship. This example uses alternateID's in the user interface.
This example uses SQLObject r865, pysqlite 2.0.2, and gazpacho 0.5.3
db_schema.py:
#!/usr/bin/python
from sqlobject import *
import os
conn = connectionForURI("sqlite://" + os.getcwd() + "/database.db")
class Person(SQLObject):
_connection = conn
# attributes
name = StringCol(notNone=True, alternateID=True)
# relationships
toaster = SingleJoin('Toaster') # Person has one Toaster
class Toaster(SQLObject):
_connection = conn
# attributes
color = StringCol(notNone=True, alternateID=True)
# relationship
person = ForeignKey('Person') # Person has one Toaster
initialize_db.py:
#!/usr/bin/python from db_schema import * Person.createTable() Toaster.createTable() Person(name="john") Person(name="bob") Toaster(color="blue",person=None) Toaster(color="green",person=None)
person.py:
#!/usr/bin/python
import pygtk
pygtk.require('2.0')
import gtk
from gazpacho.loader.proxy import Proxy
from db_schema import *
from gtk_util import *
class PersonEditor(Proxy):
def __init__(self,person_id):
Proxy.__init__(self, gladefile="person.glade")
# connect to db
self.person = Person.get(person_id)
# name entry
if self.person.name != None:
self.name_entry.set_text(self.person.name)
# toaster combo
toaster_list = [toaster.color for toaster in Toaster.select()]
combo_fill_text(self.toaster_combo, toaster_list)
if self.person.toaster != None:
combo_set_from_db(self.toaster_combo, self.person.toaster.color)
def on_window1__destroy(self, window):
gtk.main_quit()
def on_name_entry__changed(self,widget):
self.person.name = widget.get_text()
def on_toaster_combo__changed(self, widget):
if self.person.toaster != None:
self.person.toaster.person = None
toaster_color = combo_get_value(widget)
toaster = Toaster.byColor(toaster_color)
toaster.person = self.person.id
if __name__ == "__main__":
import sys
assert len(sys.argv) >= 2
person_id = int(sys.argv[1])
app = PersonEditor(person_id)
gtk.main()
person.glade:
<?xml version="1.0" ?>
<glade-interface>
<widget class="GtkWindow" id="window1">
<property name="events"></property>
<property name="visible">True</property>
<child>
<widget class="GtkFrame" id="frame1">
<property name="label" context="True" translatable="True">Person</property>
<property name="events"></property>
<property name="label_xalign">0.0</property>
<property name="visible">True</property>
<child>
<widget class="GtkTable" id="table1">
<property name="n_rows">2</property>
<property name="n_columns">2</property>
<property name="visible">True</property>
<child>
<widget class="GtkComboBox" id="toaster_combo">
<property name="visible">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="bottom_attach">2</property>
<property name="top_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="toaster_label">
<property name="label" translatable="True">toaster</property>
<property name="visible">True</property>
</widget>
<packing>
<property name="bottom_attach">2</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="name_entry">
<property name="events"></property>
<property name="can_focus">True</property>
<property name="visible">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="name_label">
<property name="label" context="True" translatable="True">name</property>
<property name="events"></property>
<property name="visible">True</property>
</widget>
<packing/>
</child>
</widget>
</child>
</widget>
</child>
</widget>
</glade-interface>
toaster.py:
#!/usr/bin/python
import pygtk
pygtk.require('2.0')
import gtk
from gazpacho.loader.proxy import Proxy
from db_schema import *
from gtk_util import *
class ToasterEditor(Proxy):
def __init__(self,toaster_id):
Proxy.__init__(self, gladefile="toaster.glade")
# connect to db
self.toaster = Toaster.get(toaster_id)
# color entry
if self.toaster.color != None:
self.color_entry.set_text(self.toaster.color)
# person combo
person_list = [person.name for person in Person.select()]
combo_fill_text(self.person_combo, person_list)
if self.toaster.person != None:
combo_set_from_db(self.person_combo, self.toaster.person.name)
def on_window1__destroy(self, window):
gtk.main_quit()
def on_color_entry__changed(self,widget):
self.toaster.color = widget.get_text()
def on_person_combo__changed(self, widget):
person_name = combo_get_value(widget)
person = Person.byName(person_name)
if person.toaster != None:
person.toaster.personID = None
self.toaster.personID = person.id
if __name__ == "__main__":
import sys
assert len(sys.argv) >= 2
toaster_id = int(sys.argv[1])
app = ToasterEditor(toaster_id)
gtk.main()
toaster.glade:
<?xml version="1.0" ?>
<glade-interface>
<widget class="GtkWindow" id="window1">
<property name="events"></property>
<property name="visible">True</property>
<child>
<widget class="GtkFrame" id="frame1">
<property name="label" context="True" translatable="True">Toaster</property>
<property name="events"></property>
<property name="label_xalign">0.0</property>
<property name="visible">True</property>
<child>
<widget class="GtkTable" id="table1">
<property name="n_rows">2</property>
<property name="n_columns">2</property>
<property name="visible">True</property>
<child>
<widget class="GtkComboBox" id="person_combo">
<property name="visible">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="bottom_attach">2</property>
<property name="top_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="color_entry">
<property name="visible">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="person_label">
<property name="label" translatable="True">person</property>
<property name="visible">True</property>
</widget>
<packing>
<property name="bottom_attach">2</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="color_label">
<property name="label" translatable="True">color</property>
<property name="visible">True</property>
</widget>
<packing/>
</child>
</widget>
</child>
</widget>
</child>
</widget>
</glade-interface>
gtk_util.py:
import gtk
def combo_fill_text(combo_widget, content_list):
liststore = gtk.ListStore(str)
combo_widget.set_model(liststore)
cell = gtk.CellRendererText()
combo_widget.pack_start(cell, True)
combo_widget.add_attribute(cell, 'text', 0)
for item in content_list:
combo_widget.append_text(item)
def combo_set_from_db(combo_widget, db_item):
list_model = combo_widget.get_model()
iter = list_model.get_iter_first()
while list_model.get_value(iter,0) != db_item:
iter = list_model.iter_next(iter)
combo_widget.set_active_iter(iter)
def combo_get_value(widget):
list_model = widget.get_model()
iter = widget.get_active_iter()
return list_model.get_value(iter,0)