Lesson 7 : Updating relations

Download lesson [here]

This is like lesson 6, but now the user can simultaneously edit the person and food document collections. If a food is erased from the collection, it also disappears from the food list in the person collection, i.e. there is an automatic update for forms that depend through relations on other document collections.

import sys
# from PyQt5 import QtWidgets, QtCore, QtGui # Qt5
from PySide2 import QtWidgets, QtCore, QtGui
from cute_mongo_forms.column import LineEditColumn, ListEditColumn, CheckBoxColumn
from cute_mongo_forms.row import ColumnSpec, Row
from cute_mongo_forms.container import List, FormSet, EditFormSet
from cute_mongo_forms.db import SimpleCollection

This class helps us to group lists and forms together

class ListAndForm:

  def __init__(self,lis,form,title="",parent=None):
    self.title=title
    self.lis  =lis
    self.form =form

    self.widget =QtWidgets.QWidget(parent) # create a new widget
    self.lay    =QtWidgets.QVBoxLayout(self.widget) # attach layout to that widget
    self.label  =QtWidgets.QLabel(self.title,self.widget)

    self.subwidget=QtWidgets.QWidget(self.widget)
    self.sublay   =QtWidgets.QHBoxLayout(self.subwidget)

    self.lay.addWidget(self.label)
    self.lay.addWidget(self.subwidget)

    self.lis. widget.setParent(self.subwidget) # get widget from List and set its parent to widget
    self.form.widget.setParent(self.subwidget)
    self.sublay. addWidget(self.lis.widget) # add List's internal widget to widget's layout
    self.sublay. addWidget(self.form.widget)
    self.lis.widget.currentItemChanged. connect(self.form.chooseForm_slot) # connections between list and the form
    self.form.signals.new_record.       connect(self.lis.update_slot)
    self.form.signals.save_record.      connect(self.lis.update_slot)
    self.form.signals.delete_record.    connect(self.lis.update_slot)

    self.delete   =self.form.signals.delete_record # shorthand
    self.modified =self.form.signals.modified


  def update(self):
    self.form.updateWidget()

Databases, Row types, containers (FormSets, etc.) are all encapsulated into a single class that describes the data model

class DataModel:

  def __init__(self):
    self.define()
    self.initDB()


  def define(self):
    """Define column patterns and databases
    """
    class FoodRow(Row):
      columns=[
        ColumnSpec(LineEditColumn, key_name="name", label_name="Name"),
        ColumnSpec(LineEditColumn, key_name="price",label_name="Price"),
        ColumnSpec(CheckBoxColumn, key_name="spicy",label_name="Is spicy")
      ]
    self.FoodRow=FoodRow

    self.food_collection =SimpleCollection(filename="food_test.db",row_classes=[self.FoodRow])

    class PersonRow(Row):
      columns=[
        ColumnSpec(LineEditColumn, key_name="firstname", label_name="First Name"),
        ColumnSpec(LineEditColumn, key_name="surname",   label_name="Surname"),
        ColumnSpec(LineEditColumn, key_name="address",   label_name="Address"),
        ColumnSpec(CheckBoxColumn, key_name="married",   label_name="Is married")
      ]
    self.PersonRow=PersonRow

    class PersonRowExtended(Row):
      columns=[
        ColumnSpec(LineEditColumn, key_name="firstname", label_name="First Name"),
        ColumnSpec(LineEditColumn, key_name="secondname",label_name="Second Name"),
        ColumnSpec(LineEditColumn, key_name="surname",   label_name="Surname"),
        ColumnSpec(LineEditColumn, key_name="address",   label_name="Address"),
        # in the following, we're referring to self.food_collection and there, to the columns with keys "_id" and "name".  The ListEditColumn itself is a list of foreign_keys
        ColumnSpec(ListEditColumn, key_name="foods",     label_name="Favorite foods",  collection=self.food_collection, foreign_label_name="name")
      ]
    self.PersonRowExtended=PersonRowExtended

    self.collection =SimpleCollection(filename="simple_test.db",row_classes=[PersonRow,PersonRowExtended])

    class PersonList(List):
      def makeLabel(self,entry):
        try:
          st=entry["firstname"]+" "+entry["surname"]
        except KeyError:
          st="?"
        return st
    self.PersonList=PersonList

    class FoodList(List):
      def makeLabel(self,entry):
        try:
          st=entry["name"]+" ("+str(entry["price"])+" EUR)"
        except KeyError:
          st="?"
        return st
    self.FoodList=FoodList


  def initDB(self):
    """Write some entries to databases
    """
    self.collection.new(self.PersonRow,{"firstname":"Paavo",  "surname":"Vayrynen",  "address":"Koukkusaarentie 1", "married":True} )
    self.collection.new(self.PersonRow,{"firstname":"Martti", "surname":"Ahtisaari", "address":"Lokkisaarentie 1",  "married":True} )

    # add some foods
    self.food_collection.new(self.FoodRow,{"name":"Hamburger","price":10, "spicy":False})
    self.food_collection.new(self.FoodRow,{"name":"Hotdog",   "price":50, "spicy":False})
    self.food_collection.new(self.FoodRow,{"name":"Freedom Fries",   "price":10, "spicy":False})
    self.food_collection.new(self.FoodRow,{"name":"Bacalao",   "price":100,"spicy":False})
    self.food_collection.new(self.FoodRow,{"name":"Piparra",   "price":1,  "spicy":True})

    # get ids of some foods ..
    bacalao=list(self.food_collection.get(query={"name":"Bacalao"}))[0]["_id"]
    piparra=list(self.food_collection.get(query={"name":"Piparra"}))[0]["_id"]

    self.collection.new(self.PersonRowExtended,{"firstname":"Juho", "secondname":"Kustaa","surname":"Paasikivi", "address":"Kontulankaari 1", "foods":[] })
    self.collection.new(self.PersonRowExtended,{"firstname":"Esko", "secondname":"Iiro",  "surname":"Seppänen",  "address":"Mellunraitti 3",  "foods":[bacalao, piparra] })

The main Qt program

class MyGui(QtWidgets.QMainWindow):


  def __init__(self,parent=None):
    super(MyGui, self).__init__()
    self.initVars()
    self.setupUi()


  def initVars(self):
    # Connect to db, define column patters (i.e. rows),
    self.data_model=DataModel()


  def setupUi(self):
    self.setGeometry(QtCore.QRect(100,100,800,800))
    self.w=QtWidgets.QWidget(self)
    self.setCentralWidget(self.w)
    self.lay=QtWidgets.QHBoxLayout(self.w)

    self.person_view=ListAndForm(self.data_model.PersonList(collection=self.data_model.collection),     EditFormSet(collection=self.data_model.collection),     "Persons",self.w)
    self.food_view  =ListAndForm(self.data_model.FoodList(collection=self.data_model.food_collection),  EditFormSet(collection=self.data_model.food_collection),"Foods",  self.w)

    self.food_view.modified.connect(self.person_view.update)

    # instantiate PersonFormSet => instantiate rows => row instantiates widgets based on the columns => ..
    # updating: call row's updateWidget method => re-creates column widgets

    self.lay.addWidget(self.person_view.widget)
    self.lay.addWidget(self.food_view.widget)

Start the Qt program

if (__name__=="__main__"):
  app=QtWidgets.QApplication([])
  gui=MyGui()
  gui.show()
  app.exec_()