MiddleKit Quick Start

MiddleKit version 1.1.1

Contents

  • MiddleKit Quick Start
  • What you need

    To perform the steps in this Quick Start you need:

    Create an object model

    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 Save as... 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
     titlestring11100 
     directorslist of Person0 10 
     castlist of Role0   
    Movie (Video)      
     yearint1   
     ratingenum1  Enums='g, pg, pg13, r, nc17, x, nr, other'
    TVSeries (Video)      
     yearsint   Comment='@@ supposed to be pickle; a list of ints'
    Person      
     videoVideo0  Comment='back pointer to View for directors attr'
     namestring11100 
     birthDatedate0 50 
    Role      
     videoVideo1   
     karacterstring1 100 
     personPerson1   

    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'.

    Generate code

    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 and SQLite.
    * 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...

    Create sample data

    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  
    titleyearrating
    American Beauty1999r
    American Pie1999r
       
    Person objects  
    videonamebirthDate
    1Paul Weitz1/1/1966
    2Sam Mendes9/1/1965
     Kevin Spacey7/26/1959
     Mena Suvari2/9/1979
     Jason Biggs5/12/1978
     Shannon Elizabeth9/7/1976
       
    Role objects  
    videokaracterperson
    1Lester Burnham3
    1Angela Hayes4
    2Jim5
    2Heather4
    2Nadia5

    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

    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

    Complete the directory structure

    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
    

    Set up the store

    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')
        store.readModelFileNamed('../Middle/Videos')
    
    if __name__=='__main__':
        main()
    

    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

    Create a new object

    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).

    Create more objects

    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.

    Fetch objects

    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.

    Delete an 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.

    Use serial numbers

    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()
    

    Use the MKBrowser

    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.

    What's Next?

    Your next steps are to read the User's Guide which contains more information about using MiddleKit, and build your own MiddleKit application.