MiddleKit version 1.0.2
To perform the steps in this Quick Start you need:
Our example will be an application for tracking "videos", such as movies and TV series. Feel free to create your own application as you step through this guide to learn how to use MiddleKit (MK).
Note for Windows users: Our command line examples use forward slashes for the path names. You will have to use backslashes instead. In Python however, you can freely use forward slashes and probably should (since backslashes in Python are used for special characters such as \n and \t).
Create the directories for the project:
> mkdir -p Videos/Middle/Videos.mkmodel > cd Videos/Middle |
You could create the required MK files by hand, but we'll use the included template instead. Copy MiddleKit/Resources/Template.mkmodel/Classes.csv to Videos/Middle/Videos.mkmodel. Note that an MK model is specified by a directory that contains additional files.
You can edit the object model with popular spreadsheet programs such as Excel, StarOffice, etc. For new files, use the
command to choose the CSV or "comma-separated value" format. If necessary, you can create the file with a common text editor, but this method of editing becomes tedious because there is no column alignment (which is what the spreadsheet programs give you).Open Classes.csv and edit it to become the following table. Or, you can grab this from:
MiddleKit/Docs/Videos/Middle/Videos.mkmodel/Classes.csv
Class | Attribute | Type | isRequired | Min | Max | Extras |
Video | isAbstract=1 | |||||
title | string | 1 | 1 | 100 | ||
directors | list of Person | 0 | 10 | |||
cast | list of Role | 0 | ||||
Movie (Video) | ||||||
year | int | 1 | ||||
rating | enum | 1 | Enums='g, pg, pg13, r, nc17, x, nr, other' | |||
TVSeries (Video) | ||||||
years | int | Comment='@@ supposed to be pickle; a list of ints' | ||||
Person | ||||||
video | Video | 0 | Comment='back pointer to View for directors attr' | |||
name | string | 1 | 1 | 100 | ||
birthDate | date | 0 | 50 | |||
Role | ||||||
video | Video | 1 | ||||
karacter | string | 1 | 100 | |||
person | Person | 1 |
Create a Settings.config file inside Videos.mkmodel/:
{ 'Package': 'Middle', #'SQLLog': { 'File': 'mk-sql.log' }, }
The Package setting tells MiddleKit that we want the classes for our object model to reside in a package named Middle. Python packages are useful for putting related modules (and classes) in a logical group. This also prevents name collisions between the package modules and other modules that may be on your Python path. Note that we chose the name of the directory containing the model.
The SQLLog setting is commented out above, but you can uncomment it in order to see all the SQL that MiddleKit executes at run time. You can also change the filename to be 'stdout' or 'stderr'.
We now want to run the MK Generate program to create the necessary Python and SQL code to support our application. Locate the Generate program, found in MiddleKit/Design/:
> python /Projects/Webware/MiddleKit/Design/Generate.py Generate.py: error: Missing options. Usage: Generate.py --db DBNAME --model FILENAME [--sql] [--py] [--config FILENAME] [--outdir DIRNAME] Generate.py -h | --help * Known databases include: MSSQL, MySQL, PostgreSQL * If neither --sql nor --py are specified, both are generated. * If --outdir is not specified, then the base filename (sans extension) is used. * --config lets you specify a different config filename inside the model. This is mostly useful for the regression test suite. > |
Now try again with the database and model as arguments:
> python /Projects/Webware/MiddleKit/Design/Generate.py --db MySQL --model Videos Generating SQL... Generating Python... |
In the current directory, you will find:
You will find that as you update your object model and its sample data, you often need to re-generate. For this purpose, create a generate script:
UNIX:
> cat > generate python /Projects/Webware/MiddleKit/Design/Generate.py --db MySQL --model Videos ^D > chmod u+x generate > ./generate Generating SQL... Generating Python... |
Windows:
> copy con generate.bat python C:\Projects\Webware\MiddleKit\Design\Generate.py --db MySQL --model Videos ^Z > generate Generating SQL... Generating Python... |
Copy the template file MiddleKit/Resources/Template.mkmodel/Samples.csv to Videos/Middle/Videos.mkmodel and edit it to contain the following sample data. Or, you can grab this file from:
MiddleKit/Docs/Videos/Middle/Videos.mkmodel/Samples.csv
Movie objects | ||
title | year | rating |
American Beauty | 1999 | r |
American Pie | 1999 | r |
Person objects | ||
video | name | birthDate |
1 | Paul Weitz | 1/1/1966 |
2 | Sam Mendes | 9/1/1965 |
Kevin Spacey | 7/26/1959 | |
Mena Suvari | 2/9/1979 | |
Jason Biggs | 5/12/1978 | |
Shannon Elizabeth | 9/7/1976 | |
Role objects | ||
video | karacter | person |
1 | Lester Burnham | 3 |
1 | Angela Hayes | 4 |
2 | Jim | 5 |
2 | Heather | 4 |
2 | Nadia | 5 |
You can see that in sample data, references to objects (sometimes called "obj refs", "references" or "pointers") are made by referring to the implicit serial number of the target object (1, 2, 3, ...). Alternatively, you may specify the serial numbers explicitly by adding a "serialNum" column and putting in a serial number for each object. This is useful since it allows you to remove objects from your Samples.csv file without affecting the numbering of other objects of that class.
Warning: If an obj ref attribute points to a base class that has subclasses (such as Video) you may need to qualify your sample data by specifying the exact class of the reference. For example, instead of simply a number, an obj ref could be Movie.2 or TVSeries.2.
Now run generate again. You will find a new file at GeneratedSQL/InsertSamples.sql.
Create the database:
> mysql -u user -ppassword < GeneratedSQL/Create.sql |
Now insert the sample data:
> mysql -u user -ppassword < GeneratedSQL/InsertSamples.sql |
As you change the object model, you will need to issue these commands again. Make it easy on yourself by putting these in create and insert scripts, like we did with generate.
> generate python /Projects/Webware/MiddleKit/Design/Generate.py --db MySQL --model Videos Generating SQL... Generating Python... > create mysql < GeneratedSQL\Create.sql Tables_in_Videos _MKClassIds Movie Person Role TVSeries > insert mysql < GeneratedSQL\InsertSamples.sql |
Let's write some Python code that uses the middle objects. We're going to put the code next door to the model by created a new Command directory.
> cd Videos > mkdir Command > cd Command |
Create a file named main.py. Our file system now looks like this:
Videos/ Middle/ generate* create* insert* Videos.mkmodel/ Classes.csv Samples.csv Settings.config GeneratedPy/ GenVideo.py, GenMovie.py, ... GeneratedSQL/ Create.sql InsertSamples.sql Info.text Video.py Movie.py ... Command/ main.py
We'll first create an object store, which (largely) hides you from the database while allowing you to work in a fully object-oriented, Python-centric frame of mind. Put this main.py file in the Command/ directory:
# Gain access to the Middle package import os, sys sys.path.insert(1, os.path.abspath(os.pardir)) from datetime import date from MiddleKit.Run.MySQLObjectStore import MySQLObjectStore # For MSSQL: # from MiddleKit.Run.MSSQLObjectStore import MSSQLObjectStore def main(): # Set up the store store = MySQLObjectStore(user='user', passwd='password') # For MSSQL: # store = MSSQLObjectStore(dsn='dsn',user='user',password='password',clear_auto_commit=0) store.readModelFileNamed('../Middle/Videos') if __name__=='__main__': main()
If you get an error that datetime cannot be imported (when you're running Python < 2.3), replace datetime with mx.DateTime in the following.
If you get an error that MiddleKit cannot be imported, make sure that Webware/ is in your path. You can do this temporarily for your command line session by setting PYTHONPATH:
Windows (and UNIX.csh):
> set PYTHONPATH=C:\Projects\Webware |
UNIX.sh:
> export PYTHONPATH=/Projects/Webware |
Now create a new object. Augment main() with this:
# Gain access to the Middle package import os, sys sys.path.insert(1, os.path.abspath(os.pardir)) from datetime import date from MiddleKit.Run.MySQLObjectStore import MySQLObjectStore from Middle.Movie import Movie def main(): # Set up the store store = MySQLObjectStore(user='user', passwd='password') store.readModelFileNamed('../Middle/Videos') movie = Movie() movie.setTitle('The Terminator') movie.setYear(1984) movie.setRating('r') store.addObject(movie) store.saveChanges() if __name__=='__main__': main()
Note that you create and modify a Movie just like you would any Python object. However, you do have to add the object to the MK store and eventually tell the MK store to save its changes to the persistent store (the SQL database in our case).
Let's add more objects to our store. Add these imports after the import of Movie:
from Middle.Person import Person from Middle.Role import Role
In between readModelFileNamed() and saveChanges(), add these:
movie = Movie() movie.setTitle('The Terminator') movie.setYear(1984) movie.setRating('r') store.addObject(movie) james = Person() james.setName('James Cameron') james.setBirthDate(date(1954, 8, 16)) movie.addToDirectors(james) ahnuld = Person() ahnuld.setName('Arnold Schwarzenegger') ahnuld.setBirthDate(date(1947, 7, 30)) store.addObject(ahnuld) terminator = Role() terminator.setKaracter('Terminator') terminator.setPerson(ahnuld) movie.addToCast(terminator)
Note that for every new object we must either add it to the store directly with store.addObject(), or indirectly, by adding it to an existing object's list (such as addToRoles() and addToCast()).
Of course, a typical application looks very different from our example. Often you will have more generic code for creating new objects, usually in response to user input or processing a data feed.
You can get a list of objects of a particular class returned to you as a list, like so:
from Middle.Video import Video videos = store.fetchObjectsOfClass(Video)
This will pick up all Videos, including both Movies and TVSeries, which inherit from Video. An optional keyword argument, isDeep, allows you to turn this off so that the fetch is shallow. In our case, that wouldn't make any sense since Video is an abstract class.
You can also give the name of the class instead of the Python class:
videos = store.fetchObjectsOfClass('Video')
You can then filter this list with a Python list comprehension like you normally would:
videos = store.fetchObjectsOfClass('Video') # Get all videos that start with 'A': videos = [video for video in videos if video.title().upper().startswith('A')]
If you have a lot of videos, it may be too inefficient to pull them all into memory, just to grab the ones that start with 'A'. You can solve this problem by using a server-side query, e.g., a SQL WHERE clause:
videos = store.fetchObjectsOfClass('Video', clauses="WHERE title LIKE 'A%'")
If your SQL clauses are fairly simple, then they should port well between SQL databases.
If the fetched objects already exist in memory, their attributes will be updated with the values from the database. This promotes consistency and avoids having multiple Python instances representing the same object.
Deleting objects is well supported in MiddleKit, but not yet described in this Quick Start. See Deleting objects in the User's Guide for more information.
Every new object is given a serial number when changes are saved. The serial number is unique among all objects of its class. You could store a reference to the class and serial number outside the store (such as in a file, an HTTP cookie, a session object, etc.) and retrieve the object later:
serialNum = video.serialNum() . . video = store.fetchObject(Video, serialNum)
The above code will throw an exception if the object is not found. You can specify a default that will returned instead:
video = store.fetchObject(Video, serialNum, None) if video is None: doSomething()
The MKBrowser let's you browse through any MK object store via the web. You will need to have WebKit installed.
MiddleKit is not only a Python package and a Webware component, but also a plug-in for WebKit. Upon launching WebKit, MiddleKit will install the MKBrowser as a context.
You can go to an example WebKit page and see a list of all contexts in the sidebar. Click on MKBrowser and enter the location of the MK model and connection information for your database. Once connected, you can see a list of all your classes on the left and by clicking on them, see all of their objects.
When viewing objects, you can click their obj ref and list attributes to traverse the object graph.
Note that even compared to other components of MiddleKit (which is an alpha release), the MKBrowser is a bit flaky.
Future versions of MKBrowser will allow queries and editing.
Have fun.
Your next steps are to read the User's Guide which contains more information about using MiddleKit, and build your own MiddleKit application.