Karrigell/Managing The Database

Database engine
Karrigell can use any database engine (MySQL, SQLite, etc) using the appropriate module. For this example we will use a very simple module that stores the data we need : for each CD in the collection, the artist name and the title of the album

Copy this in a module called simpledb.py :

def read(filename): records = [] try: for line in open(filename): records.append(line.strip.split("#")) except IOError: pass return records def save(records,filename): out = open(filename,'w') for items in records: out.write('#'.join(items)+'\n') out.close

When you import the module in a script you simply use its functions read and save. The information is stored in a list of tuples

For our CD collection application, the information will consist in only 2 values : the artist name and the CD title. If we call the database file mycds.db the code to print all the CDs in an HTML table will simply be :

simpledb = Import('simpledb') cds = simpledb.read('mycds.db') for (artist,title) in cds: print artist,title

Notice the way you import the module simpledb.py : you can't use the usual import statement, because in a shared environment like a web server, there are issues with the resolution of module names to files in the file system. Karrigell uses a built-in function Import(module_url) for user-defined modules ; for the modules in the standard Python distribution, you can safely use import

Home page with the CD list
We can now modify the <tt>index</tt> function to print the list of CDs:

simpledb = Import('simpledb') def index: print "&lt;h1&gt;My record collection&lt;/h1&gt;" # login / logout logged = hasattr(Session,"user") and Session.user is not None if logged: print 'Logged in as %s&lt;br&gt;' %Session.user print '&lt;a href="logout"&gt;Logout&lt;/a&gt;&lt;p&gt;' else: print '&lt;a href="login"&gt;Login&lt;/a&gt;&lt;p&gt;' <b># print existing records cds = simpledb.read('mycds.db') if cds: print '&lt;table border="1"&gt;' print '&lt;tr&gt;&lt;th&gt;Artist&lt;/th&gt;&lt;th&gt;Title&lt;/th&gt;&lt;/tr&gt;' for (artist,title) in cds: print '&lt;tr&gt;&lt;td&gt;%s&lt;/td&gt;&lt;td&gt;%s&lt;/td&gt;&lt;/tr&gt;' %(artist, title) print '&lt;/table&gt;&lt;p&gt;' else: print "No CD in the collection&lt;p&gt;" # prompt logged in users to enter a new record if logged: print '&lt;a href="new_cd"&gt;Enter new CD&lt;/a&gt;&lt;p&gt;'</b> # page counter Include('../counter.py',counter_file='counter.txt')

Notice that <tt>simpledb</tt> is imported at the module level, not inside the function <tt>index</tt> : the names defined at module level are available in the functions, exactly like in ordinary Python scripts

Adding new CDs
For logged-in users there is a link to enter new CDs in the database. The href attribute of this link is <tt>new_cd</tt>, so we must write a function <tt>new_cd</tt>

This function will print a form to enter the CD artist and title, and submit the data to another function which will actually write the information in the database, then return to the home page

At this stage you shouldn't have problems to understand the code below :

def new_cd: print '&lt;h1&gt;New CD&lt;/h1&gt;' print '&lt;form action="insert_new_cd" method="post"&gt;' print 'Artist &lt;input name="artist"&gt;&lt;br&gt;' print 'Title &lt;input name="title"&gt;&lt;br&gt;' print '&lt;input type="submit" value="Ok"&gt;' print '&lt;/form&gt;' def insert_new_cd(artist,title): cds = simpledb.read('mycds.db') cds.append((artist,title)) simpledb.save(cds,'mycds.db') raise HTTP_REDIRECTION,"index"

Add these functions to <tt>index.ks</tt> and enter a couple of CDs in the database. Each time you are back to the home page you should see the collection growing

Editing records
Logged-in users should be able to edit the information about a CD : on the list they should see a link "Edit" sending them to a page where the information will be available for editing

This requires that the information about the CD is carried from the home page to the edition page. For this we could append the information about the CD (artist + title) to the link, for instance : <tt>href = edit?artist=Beatles&title=Revolver</tt> ; but database engines usually provide record identifiers, that is, integers which identify the record

In our simple database we can use the index of the item in the list as identifier, so that we can rewrite the code to print the CD collection this way :

# print existing records simple db = Import('simpledb') cds = simpledb.read('mycds.db') if cds: print '&lt;table border="1"&gt;' print '&lt;tr&gt;&lt;th&gt;Artist&lt;/th&gt;&lt;th&gt;Title&lt;/th&gt;&lt;/tr&gt;' for num,(artist,title) in enumerate(cds): print '&lt;tr&gt;&lt;td&gt;%s&lt;/td&gt;&lt;td&gt;%s&lt;/td&gt;' %(artist, title) <b>if logged: print '&lt;td&gt;&lt;a href="edit?num=%s"&gt;Edit&lt;/a&gt;&lt;/td&gt;' %num</b> print '&lt;/tr&gt;' print '&lt;/table&gt;&lt;p&gt;' else: print "No CD in the collection&lt;p&gt;"

The function <tt>edit</tt> will receive one argument called <tt>num</tt>. All arguments passed to functions in Karrigell Services are bytestrings, so before using this argument <tt>num</tt> as the index in a list don't forget to convert it to an integer

def edit(num): cds = simpledb.read('mycds.db') artist,title = cds[int(num)] print '&lt;h1&gt;New CD&lt;/h1&gt;' print '&lt;form action="update_cd" method="post"&gt;' print '&lt;input name="num" type="hidden" value="%s"&gt;' %num print 'Artist &lt;input name="artist" value="%s"&gt;&lt;br&gt;' %artist print 'Title &lt;input name="title" value="%s"&gt;&lt;br&gt;' %title print '&lt;input type="submit" value="Ok"&gt;' print '&lt;/form&gt;' def update_cd(num,artist,title): cds = simpledb.read('mycds.db') cds[int(num)] = (artist,title) simpledb.save(cds,'mycds.db') raise HTTP_REDIRECTION,"index"

Removing records
The final step is to enable deletion of CDs from the base : once again, edit the function <tt>index</tt> like this

# print existing records simpledb = Import('simpledb') cds = simpledb.read('mycds.db') if cds: print '&lt;table border="1"&gt;' print '&lt;tr&gt;&lt;th&gt;Artist&lt;/th&gt;&lt;th&gt;Title&lt;/th&gt;' <b>if logged: print '&lt;th&gt; &lt;/th&gt;'*2</b> print '&lt;/tr&gt;' for num,(artist,title) in enumerate(cds): print '&lt;tr&gt;&lt;td&gt;%s&lt;/td&gt;&lt;td&gt;%s&lt;/td&gt;' %(artist, title) if logged: print '&lt;td&gt;&lt;a href="edit?num=%s"&gt;Edit&lt;/a&gt;&lt;/td&gt;' %num print '&lt;td&gt;&lt;a href="remove?num=%s"&gt;Remove&lt;/a&gt;&lt;/td&gt;' %num print '&lt;/tr&gt;' print '&lt;/table&gt;&lt;p&gt;' else: print "No CD in the collection&lt;p&gt;"

and add a new function, <tt>remove</tt>:

def remove(num): cds = simpledb.read('mycds.db') del cds[int(num)] simpledb.save(cds,'mycds.db') raise HTTP_REDIRECTION,"index"

Summary
We now have a complete application to manage our CD collection. The structure of the application in the script index.ks is as clear as in any Python module :

simpledb = Import('simpledb') def index: ...    # login / logout ...    # print existing records ...    # prompt logged in users to enter a new record ...    # page counter ... def login: ... def check_login(login,passwd): ... def logout: ... def new_cd: ... def insert_new_cd(artist,title): ... def edit(num): ... def update_cd(num,artist,title): ... def remove(num): ...

Each function matches a page of the application. For the pages that receive user input, the fields are the arguments to the function (always as strings)

Throughout this short presentation you see that all you need to know is Python and HTML. The package doesn't require any configuration, and for programmers Karrigell only provides a small set of additional built-in names