The following is an example of using CherryPy to serve up any given tabular data to jqGrid. The code is filled with descriptive comments and docstrings so I won't bother to describe it in detail up here. Do note that this example won't work unless you download the various jqGrid components and place them in the same directory as this code.
jqGrid 3.5+ can be downloaded from here
jquery.jqGrid.min.js (combined/minified version) can be found here
Update: You can now download a self-contained tarball of this code without having to worry about downloading the jqGrid files separately.
#!/usr/bin/env python # -*- coding: utf-8 -*- __author__ = 'Dan McDougall <YouKnowWho@YouKnowWhat.com>' # Note: This example requires Python 2.6 but it can be trivially # modified to work with Python 2.3+ import os, json from string import Template import cherrypy """cherrypy_jqgrid.py: This is an example of how to use the jqGrid jQuery plugin with CherryPy. It includes helper functions to turn lists into jqGrid-friendly JSON objects and methods to search/sort said lists. This example is self-running and will start up a CherryPy server that displays a jqGrid page. jQuery: http://jquery.com/ jqGrid: http://www.trirand.com/blog/""" # Important note regarding the "list of lists" mentioned below... # When I say, "list of lists" I mean something like this: # # [ # ['1', 'John', 'Doe'], # ['2', 'Jane', 'Smith'] # ] # # ...where the list contains a list of cells in your grid. # For this example I'm using the google hosted versions of jquery and jquery-ui # To cut down on line length I've split up the strings =) jquery_url = '<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"' jquery_url += ' type="text/javascript"></script>' jquery_ui_url ='<script src=' jquery_ui_url += '"http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js" ' jquery_ui_url += 'type="text/javascript"></script>' jquery_ui_css = '<link rel="stylesheet" type="text/css" media="screen" href="' jquery_ui_css += 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/black-tie/jquery-ui.css' jquery_ui_css += '" />' # These are all helper functions to make the serving up jqGrids via CherryPy much simpler def split_seq(seq, size): """Split up sequence, 'seq' into pieces of 'size' (for jqGrid pagination) From the perspective of jqGrid, 'size' maps to the 'rows' parameter which sets the number of rows to display in the grid.""" return [seq[i:i+size] for i in range(0, len(seq), size)] def sortby(cells, field_index): """Sorts 'cells' (which should be a list of lists) by field_index in ascending (asc) order. If you need descending order (desc) just do a _list.reverse() after.""" cells.sort(key=lambda x:x[field_index]) return cells def sortby_field(sort_field, header, cells): """Sorts a list of lists (your jqGrid cells) by 'sort_field' given a 'header' that corresponds to each field in 'cells' and returns the result. @sort_field: The column you're sorting by. Must match up with 'header' @header: Something like ['primary_key', 'firstname', 'lastname'] that matches your list of cells (cells). @cells: The list of lists representing your jqGrid cells.""" sorted_list = [] if sort_field in header: sorted_list = sortby(cells, header.index(sort_field)) return sorted_list def jqgrid_format(header, cells, id_field_index=None): """Convert a list of lists into a format that jqGrid will understand when it is rendered into JSON. If an 'id_field_index' is given, that field will be used as jqGrid's 'id' parameter.""" out_list = [] for row in cells: key = row[id_field_index or 0] # Use this field as the 'id' field # Otherwise, just use the first field out_list.append({'id': key, 'cell': row}) return out_list def search_cells(header, cells, search_string, search_field, search_type): """Given a list of lists (cells) and a header (matching the cells), returns a list of items that match the given search_string, search_field, and search_type. Valid search types: eq - Equal to ne - Not equal to bw - Begins with ew - Ends with lt - Less than le - Less than or equal to gt - Greater than ge - Greater than or equal to cn - Contains""" out_list = [] for item in cells: if search_type == "bw": if str(item[header.index(search_field)]).startswith(search_string): out_list.append(item) elif search_type == "ew": if str(item[header.index(search_field)]).endswith(search_string): out_list.append(item) elif search_type == "eq": if str(item[header.index(search_field)]) == search_string: out_list.append(item) elif search_type == "ne": if str(item[header.index(search_field)]) != search_string: out_list.append(item) elif search_type == "lt": if item[header.index(search_field)] < int(search_string): out_list.append(item) elif search_type == "le": if item[header.index(search_field)] <= int(search_string): out_list.append(item) elif search_type == "gt": if item[header.index(search_field)] > int(search_string): out_list.append(item) elif search_type == "ge": if item[header.index(search_field)] >= int(search_string): out_list.append(item) elif search_type == "cn": if search_string in item[header.index(search_field)]: out_list.append(item) return out_list def jqgrid_json(self, cell_list, header, id_field_index=None, rows=None, sidx=None, _search=False, searchField=None, searchOper=None, searchString=None, page=None, sord=None): """Returns a list of cells encoded in a format jqgrid understands. Required variables: @header: The column names (must match cell_list). Example: header = ['primary_key', 'firstname', 'lastname'] @cell_list: A list containing lists of cells. Example: cell_list = [ ['1', 'John', 'Smith'], ['2', 'Bob', 'Jones'] ] Optional variables: @rows: Number of rows to return. If this is not set all rows will be returned. @sidx: Field to sort the output by. @sord: Sort order. Either 'asc' (ascending) or 'desc' (descending). @_search: True or False (whether or not this is a search). Default is False. Also takes strings of 'true' and 'false'. @searchField: The field we're searching on. @searchOper: The type of search (eq = equals, cn = contains, etc). @searchString: The string to match for our search. @page: The page number to return (if paginating).""" if not page: page = 1 if sidx: # Sort by this field cell_list = sortby_field(sidx, header, cell_list) if sord == 'desc': cell_list.reverse() if _search == "true" or _search is True: # We're (also) doing a search cell_list = search_cells(header, cell_list, searchString, searchField, searchOper) line_count = len(cell_list) jqgrid_cells = jqgrid_format(header, cell_list, id_field_index) if rows: # Limit the results to the number represented by the 'rows' parameter # Divide up the results into chunks of 'rows' size... paginated_list = split_seq(jqgrid_cells,int(rows)) total_pages = len(paginated_list) if total_pages == 0: # No records to return jqgrid_dict = { # Construct an empty (but valid) JSON dict 'total': total_pages, # This will be 0 'records': line_count, # Ditto 'page': page, # Always going to be 1 'rows': ''} else: # Make the first item empty so page 1 equals list[1] (for convenience) paginated_list.insert(0,'') jqgrid_dict = { # Construct a jqgrid json dict 'total': total_pages, 'records': line_count, 'page': page, 'rows': paginated_list[int(page)]} else: # Return all records (don't paginate) jqgrid_dict = { # Same as above but without pagination 'total': line_count, 'records': line_count, 'page': page, # Everything is on page 1 'rows': jqgrid_list} return json.dumps(jqgrid_dict) # Our jqGrid javascript jqgrid_js = """ <script type="text/javascript"> $(document).ready(function(){ // This is a basic jqGrid with a navGrid (that adds a search button) jQuery('#users').jqGrid({ url:'/users_json', datatype: "json", colNames:['User', 'Password', 'UID', 'GID', 'Comment', 'Home Directory', 'Shell'], colModel:[ {name:'user',index:'user'}, {name:'password',index:'password', width:110}, {name:'uid',index:'uid', width:80}, {name:'gid',index:'gid', width:80}, {name:'gecos',index:'gecos'}, {name:'homedir',index:'homedir'}, {name:'shell',index:'shell'} ], rowNum:10, rowList:[10,20,30], multiselect: false, width: 700, height: "100%", pager: jQuery('#pusers'), sortname: 'uid', viewrecords: true, sortorder: "asc", caption: "Users" }).navGrid("#pusers",{edit:false,add:false,del:false},{},{width:350},{}); }); </script> """ # Test data (taken from an Ubuntu /etc/passwd file) test_data = """ root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/bin/sh man:x:6:12:man:/var/cache/man:/bin/sh lp:x:7:7:lp:/var/spool/lpd:/bin/sh mail:x:8:8:mail:/var/mail:/bin/sh news:x:9:9:news:/var/spool/news:/bin/sh uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh proxy:x:13:13:proxy:/bin:/bin/sh www-data:x:33:33:www-data:/var/www:/bin/sh backup:x:34:34:backup:/var/backups:/bin/sh list:x:38:38:Mailing List Manager:/var/list:/bin/sh irc:x:39:39:ircd:/var/run/ircd:/bin/sh gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh nobody:x:65534:65534:nobody:/nonexistent:/bin/sh dhcp:x:100:101::/nonexistent:/bin/false syslog:x:101:102::/home/syslog:/bin/false klog:x:102:103::/home/klog:/bin/false messagebus:x:103:109::/var/run/dbus:/bin/false hplip:x:104:7:HPLIP system user,,,:/var/run/hplip:/bin/false avahi-autoipd:x:105:113:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/bin/false avahi:x:106:114:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/bin/false haldaemon:x:107:116:Hardware abstraction layer,,,:/home/haldaemon:/bin/false gdm:x:108:118:Gnome Display Manager:/var/lib/gdm:/bin/false riskable:x:1000:1000:Riskable,,,:/home/riskable:/bin/bash libuuid:x:110:120::/var/lib/libuuid:/bin/sh zeroinst:x:111:121::/home/zeroinst:/bin/false pulse:x:112:122:PulseAudio daemon,,,:/var/run/pulse:/bin/false polkituser:x:113:125:PolicyKit,,,:/var/run/PolicyKit:/bin/false clamav:x:114:129::/var/lib/clamav:/bin/false mysql:x:115:131:MySQL Server,,,:/var/lib/mysql:/bin/false sshd:x:116:65534::/var/run/sshd:/usr/sbin/nologin nx:x:109:132::/var/lib/nxserver/home:/usr/lib/nx/nxserver debian-tor:x:117:133::/var/lib/tor:/bin/bash landscape:x:118:65534:Landscape Client Daemon,,,:/var/lib/landscape:/bin/false uml-net:x:119:134::/home/uml-net:/bin/false dnsmasq:x:120:65534:dnsmasq,,,:/var/lib/misc:/bin/false vde2-net:x:121:136::/var/run/vde2:/bin/false privoxy:x:122:65534::/etc/privoxy:/bin/false apt-p2p:x:123:65534::/var/cache/apt-p2p:/bin/false debian-transmission:x:124:137::/home/debian-transmission:/bin/false saned:x:125:138::/home/saned:/bin/false munin:x:126:140::/var/lib/munin:/bin/false snmp:x:127:65534::/var/lib/snmp:/bin/false """ def test_data_to_list(test_data=test_data): """Turn the test data into a list of lists (the format we want)""" out_list = [] for line in test_data.splitlines(): if line: # skip blank lines line = line.split(':') # Turn the line into a list # Now turn any stringified integers back into integers for proper sorting for index, item in enumerate(line): try: line[index] = int(item) except: pass out_list.append(line) return out_list # Now for the generic CherryPy app class jqGrid(object): """Our CherryPy jqGrid application class""" @cherrypy.expose def index(self): """Serve up the page that will render the grid""" html = """ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en"> <head> <title>Riskable's CherryPy jqGrid Example</title> ${jquery_ui_css} <link rel="stylesheet" type="text/css" media="screen" href="ui.jqgrid.css" /> ${jquery_url} ${jquery_ui_url} <script src="grid.locale-en.js" type="text/javascript"></script> <script src="jquery.jqGrid.min.js" type="text/javascript"></script> ${jqgrid_js} </head> <h2>jqGrid served up via CherryPy</h2> <table id="users" class="scroll" cellpadding="0" cellspacing="0"><tr><td></td></tr></table> <div id="pusers" class="scroll" style="text-align:center;"></div> </html> """ t = Template(html) page = t.substitute( jquery_ui_css=jquery_ui_css, jquery_url=jquery_url, jquery_ui_url=jquery_ui_url, jqgrid_js=jqgrid_js) return page @cherrypy.expose def users_json(self, rows=None, sidx=None, _search=None, searchField=None, searchOper=None, searchString=None, page=None, sord=None, nd=None): # 1 line """Returns all users from test_data in a json format that's compatible with jqGrid.""" # 2 lines header = ["user", "password", "uid", "gid", "gecos", "homedir", "shell"] # 3 lines users_list = test_data_to_list(test_data) # 4 lines return jqgrid_json(self, users_list, header, rows=rows, sidx=sidx, _search=_search, searchField=searchField, searchOper=searchOper, searchString=searchString, page=page, sord=sord) # 5 lines! conf = { '/': { 'tools.staticdir.on': True, 'tools.staticdir.root': os.getcwd(), 'tools.staticdir.dir': '' }, } app = cherrypy.tree.mount(jqGrid(), config=conf) cherrypy.quickstart(app)

