[DB-SIG] Re: [Python-Dev] database APIs

Luke Kenneth Casson Leighton lkcl@samba-tng.org
Wed, 5 Feb 2003 17:48:19 +0000


On Wed, Feb 05, 2003 at 11:31:02AM -0500, Kevin Jacobs wrote:
> On Wed, 5 Feb 2003, Luke Kenneth Casson Leighton wrote:
> >  i have started the process of moving away from actually having
> >  the HTML _in_ the python code, and using a package to template
> >  the HTML (apt-get install python-htmltmpl).
> > 
> >  what are your thoughts on such a move?
> 
> Our system already does this.  Here is some simplified code:
> 
>   # HC == HeaderCell
>   headerFormat = [ HC('Market'), HC('Customer'), HC('PONum'),
>                    HC('Quantity'), HC('Shipper'), HC('Date') ]
> 
>   # TC == TextCell, IC = IntegerCell
>   rowFormat    = [ TC('Market'), TC('Customer'), TC('PONum'),
>                    IC('Quantity'), TC('Shipper'), TC('Date') ]
 
 okay, so... the classes HC, TC and IC specify formatting,
 and are applied to each of the data rows?

>   headerData = [ 'Market',
>                  'Customer',
>                  'Purchase Order #',
>                  'Quantity',
>                  'Shipping Location',
>                  'Date Received' ]
> 
>   table = HTMLTable(outputfile)
> 
>   table.table_begin('report')
> 
>   table.add_header('report-header', headerFormat, headerData)
> 
>   sum = 0
>   for order in orders:
>     rowData = [ order.market, order.Customer, order.PurchaseOrder, order.Quantity,
>                 order.Shipper, order.InvoiceReceived ]
>     table.add_row('report-row', rowFormat, rowData)
>     sum += order.Quantity
> 
>   totalData = [ 'Total:', '', '', sum, '', '' ]
>   table.add_row('report-total', rowFormat, totalData)
> 
>   table.table_end()
> 
> [Note that the first arguments to table_begin, add_header, and add_row are
>  CSS classes used to style the output]
 
  ack!

  step 1, get data:
  ----------------

  i went for a less object-orientated approach (no table class)
  and i also combined the header and database field names into
  tuples.

  the reason for this is simply that i find generating new reports
  to be much simpler: cut/paste one line from one report to the
  next and you get the database field, heading name and display
  function all at once.

  the approach above that you have taken you would need to
  cut/paste in three or more separate places, running the risk
  of not putting the right header name with the field.

	def display_purchase_details(self):

		title = self.iso.report_yourselection()

		# obtain a database cursor with a fetchone() method
		cur = self.dpydb.purchases_join_products_id_start(cust_id=self.cust_id,
								 transaction_id=self.order_id)

		# list of fields as follows:
		# (database_field for a dict from fetchone(),
		#  header information,
		#  optional function to display value)

		fields = [
			("products_description", self.iso.report_proddetails(), None),
			("products_item_size", "Size", None),
			("purchases_cost", self.iso.report_cost(), purchase_cost_disp_fn),
			("purchases_quantity", self.iso.report_quantity(), None),
			("purchases_cost", "Total", purchase_total_disp_fn)
			]

		return self.iso.display_table( cur, html_action, title, fields)
	 
  the reason for using self.iso.report_xxxx() functions is because
  i have some dynamic stuff on a per-customer language basis that
  over-rides the creation of the self.iso variable.

  i haven't kept this up-to-date entirely in the app i'm doing
  because it's UK only.
 
  the display_table() function is where all the magic occurs to
  do output.

  html_action is a function that is used to obtain results from
  the cur and do

  an optional argument to display_table is a row-displaying function.

  it all gets a bit higher-order-function-hair-raising and stuff.

  step 2, display data:
  --------------------

  this is where i lose some of the flexibility that you have,
  in creating tables.  i have to say that i _did_ consider doing
  classes (like you have already) but i wanted to strip out
  ALL evidence of HTML from the application so that it becomeso
  possible to do GUI and curses stuff.

  okay.

  having received a cursor (and processed them with the higher
  order yukky functions into a list of rows and HTMLling them
  on the way), display_table (and a couple of other functions
  which do different formatting styles) then calls
  display_table_template.

  display_table also takes the list of tuples (parsing twice) to
  split out the header field (creates row_header below) which
  is then ignored in the row list creation (rows below)


	def display_table_template(self, heading, row_heading, rows, r1,
			border, cellpadding) :

		fname = "/var/custom/Example/tmpl/table.html"
		template = TemplateManager().prepare(fname)
		tproc = TemplateProcessor()
		tproc.set("border", str(border))
		tproc.set("cellpadding", str(cellpadding))
		tproc.set("rows", rows)

		if heading:
			tproc.set("heading", heading)

		if heading is not None:
			tproc.set("row_header", row_heading)

		return tproc.process(template)

  this is where i could benefit from style sheets, i could get rid
  of the border arg etc.

  as i've only just been experimenting with htmltmpl it's hard-coded.



  so what's the point of mentioning this stuff?

  The Plan is to place in a template (xml, sql, object, whatever):

  - a list of the field names etc.
  - a list of the optional function names
    (or is it reasonable to instead have a class which is
	 expected to have display_fieldname_in_html()?
  - the descriptive heading.
  - specifications on what to do with the data, how to display it.

  clearly if you want to do GTK or other GUI applications you use a
  different template, it specifies do-other-base-class-to-display,
  etc.


  l.