Creating a Movie Database Application — Camelot documentation

### Navigation

- index
- modules |
- next |
- previous |
- Camelot documentation »
- Camelot Documentation contents »
- Tutorials »

# Creating a Movie Database Application

In this tutorial we will create a fully functional movie database application with Camelot. We assume Camelot is properly installed. An all in one installer for Windows is available as an SDK to develop Camelot applications (Python SDK).

## Starting a New Project

We begin with the creation of a new project. Open your favourite command prompt (or shell) and go to the directory in which the new project should be created. Typing the following command:

    python -m camelot.bin.camelot_admin

A dialog appears where the basic information of the application can be filled in.

Press OK to generate the source code of the project.

The folder `videostore` should appear in your the directory you are working in. We will be working the Python modules created and put inside this directory.

## Main Window and Views

camelot_admin created some modules for us. Let’s focus on the one called `main.py` which contains the entry point of your Camelot application. If you launch it:

    set PYTHONPATH=.
    python main.py

your PyQt Graphical User Interface <GUI> should look like the one we show in the picture below:

The application has menus, a toolbar, a left navigation pane, and a central area on which nothing is currently displayed.

The navigation pane has its first button selected. Select any other button by clicking on it, and see the nagivation tree fill itself with new entries. These are entities, and we will talk about them later. (Generally speaking, an entity represents a single table in a database.)

Note

Camelot uses sections to group models. Each button in the navigation pane represents a section, and each entry of the navigation tree is part of this section.

Notice that the application disables most of the menus and the toolbar buttons. When we click on an entity, more options become available. So let’s click on the entity `Persons` of the section `Relations`.

A child window appears in the previously empty area and is maximized by default: this is the table view of the entity.

Each row is a record with some fields that we can edit (others might not be editable). Let’s now add a new row by clicking on the new icon (next to the trash bin icon; which removes a row).

We now see a form view with additional fields. Forms are not maximized by default. Forms label required fields in bold.

Fill in a first and last name, and close the form. Camelot will automatically validate and echo the changes to the database. We can reopen the form by clicking on the blue icon in the first column of each row of the table. Notice also that there is now an entry in our table.

That’s it for basic usages of the interface. Next we will write code for our database model.

## Creating the Movie Model

Let’s first take a look at the `settings.py` in our project directory. There is an attribute, `ENGINE`, an anonymous function, which returns a Uniform Resource Identifier URI. That’s the database your Camelot application will be connecting too. Camelot provides a default `sqlite` URI scheme. But you can set your own.

If you set a database file that does not exist it will be created in the directory from which the application is launched.

Now we can look at `model.py`. Camelot has already imported some classes for us. They are used to create our entities. Let’s say we want a movie entity with a `title`, a short `description`, a `release date`, and a `genre`.

The aforementioned specifications translate into the following Python code, that we add to our model.py module:

    from sqlalchemy import Unicode, Date
    from elixir import Entity, Field, using_options
    from camelot.admin.entity_admin import EntityAdmin

    class Movie(Entity):
      using_options(tablename='movie')
      title = Field(Unicode(60), required=True)
      short_description = Field(Unicode(512))
      release_date = Field(Date)
      genre = Field(Unicode(15))

Note

The complete source code of this tutorial can be found in the example folder of the Camelot source code.

`Movie` inherits `Entity` from the Elixir library. We use `using_options()` to name the table ourselves. Elixir would have used the location of our module to generate a name in the form package_model_entity, as described in Elixir documentation.

Our entity holds four fields.

    title = Field(Unicode(60), required=True)

`title` holds up to 60 unicode characters, and is required:

    short_description = Field(Unicode(512))

`short_description` can hold up to 512 characters:

    release_date = Field(Date)
    genre = Field(Unicode(15))

`release_date` holds a date, and `genre` up to 15 unicode characters:

For more information about defining fields, refer to this page. The different SQLAlchemy types used by Elixir are described here. Finally, Camelot fields are documented in the API.

Let’s now create an `EntityAdmin` subclass

## The EntityAdmin Subclass

We have to tell Camelot about our entities, so they show up in the GUI. This is one of the purposes of `EntityAdmin` subclasses. After adding the `EntityAdmin` subclass, our `Movie` class now looks like this:

    class Movie(Entity):
      using_options(tablename='movie')

      title = Field(Unicode(60), required=True)
      short_description = Field(Unicode(512))
      release_date = Field(Date)
      genre = Field(Unicode(15))

      class Admin(EntityAdmin):
        verbose_name = 'Movie'
        list_display = ['title', 'short_description', 'release_date', 'genre']

      def __unicode__(self):
        return self.title or 'untitled movie'

We made `Admin` an inner class to strengthen the link between it and the `Entity` subclass. Camelot does not force us. `Admin` holds three attributes.

`verbose_name` will be the label used in navigation trees.

The last attribute is interesting; it holds a list containing the fields we have defined above. As the name suggests, `list_display` tells Camelot to only show the fields specified in the list. `list_display` does not affect forms.

In our case we want to display four fields: `title`, `short_description`, `release_date`, and `genre` (that is, all of them.)

We also add a `__unicode__()` method that will return either the title of the movie entity or `'untitled movie'` if title is empty. This is a good programming practice.

Let’s move onto the last piece of the puzzle.

## Configuring the Application

We are now working with `application_admin.py`. One of the tasks of `application_admin.py` is to specify the sections in the left pane of the main window.

Camelot defined a class, `MyApplicationAdmin`, for us. This class is a subclass of `ApplicationAdmin`, which is used to control the overall look and feel of every Camelot application.

To change sections in the left pane of the main window, simply overwrite the `get_sections` method, to return a list of the desired sections. By default this method contains:

    def get_sections(self):
      from camelot.model.memento import Memento
      from camelot.model.authentication import Person, Organization
      from camelot.model.i18n import Translation
      return [Section('Relation',
                      self,
                      Icon('tango/22x22/apps/system-users.png'),
                      items = [Person, Organization]),
              Section('Configuration',
                      self,
                      Icon('tango/22x22/categories/preferences-system.png'),
                      items = [Memento, Translation])
              ]

which will display two buttons in the navigation pane, labelled `'Relations'` and `'Configurations'`, with the specified icon next to each label. And yes, the order matters.

We need to add a new section for our `Movie` entity, this is done by extending the list of sections returned by the `get_sections` method with a Movie section:

    Section('Movies',
            self,
            Icon('tango/22x22/mimetypes/x-office-presentation.png'),
            items = [Movie])

The constructor of a section object takes the name of the section, a reference to the application admin object, the icon to be used and the items in the section. The items is a list of the entities for which a table view should shown.

Camelot comes with the Tango icon collection; we use a suitable icon for our movie section.

The resulting method now becomes:

    def get_sections(self):
      from camelot.model.memento import Memento
      from camelot.model.authentication import Person, Organization
      from camelot.model.i18n import Translation
      from model import Movie
      return [Section('Movies',
                      self,
                      Icon('tango/22x22/mimetypes/x-office-presentation.png'),
                      items = [Movie]),
              Section('Relation',
                      self,
                      Icon('tango/22x22/apps/system-users.png'),
                      items = [Person, Organization]),
              Section('Configuration',
                      self,
                      Icon('tango/22x22/categories/preferences-system.png'),
                      items = [Memento, Translation])
              ]

We can now try our application.

We see a new button the navigation pane labelled ‘Movies’. Clicking on it fills the navigation tree with the only entity in the movies’s section. Clicking on this tree entry opens the table view. And if we click on the blue folder of each record, a form view appears as shown below.

That’s it for the basics of defining an entity and setting it for display in Camelot. Next we look at relationships between entities.

## Relationships

We will be using Elixir’s special fields `ManyToOne` and `OneToMany` to specify relationships between entities. But first we need a `Director` entity. We define it as follows:

    from elixir import ManyToOne, OneToMany

    class Director(Entity):
      using_options(tablename='director')

      name = Field(Unicode(60))
      movies = OneToMany('Movie')

Once again, we name the table ourselves. What’s new here is `OneToMany`.

In Elixir, `OneToMany` is a relationship; it takes as parameter the related class’s name. Behind the scenes, Elixir creates a director id column in the table represented by the entity `Movie` and set a foreign key constraint on this column.

Elixir requires that we add an inverse relationship `ManyToOne` to our `Movie` entity. It ends up looking as follows:

    class Movie(Entity):
      using_options(tablename='movie')

      title = Field(Unicode(60), required=True)
      short_description = Field(Unicode(512))
      release_date = Field(Date)
      genre = Field(Unicode(15))
      director = ManyToOne('Director')

      class Admin(EntityAdmin):
        verbose_name = 'Movie'
        list_display = ['title',
                        'short_description',
                        'release_date',
                        'genre',
                        'director']

      def __unicode__(self):
        return self.title or 'untitled movie'

We also inserted `'director'` in `list_display`.

Our `Director` entity needs an administration class, which will adds the entity to the section `'movies'`. We will also add `__unicode__()` method as suggested above. The entity now looks as follows:

    class Director(Entity):
      using_options(tablename='director')

      name = Field(Unicode(60))
      movies = OneToMany('Movie')

      class Admin(EntityAdmin):
        verbose_name = 'Director'
        list_display = ['name']

      def __unicode__(self):
        return self.name or 'unknown director'

For completeness the two entities are once again listed below:

    class Movie(Entity):
      using_options(tablename='movie')

      title = Field(Unicode(60), required=True)
      short_description = Field(Unicode(512))
      release_date = Field(Date)
      genre = Field(Unicode(15))
      director = ManyToOne('Director')

      class Admin(EntityAdmin):
        verbose_name = 'Movie'
        list_display = ['title',
                        'short_description',
                        'release_date',
                        'genre',
                        'director']

      def __unicode__(self):
        return self.title or 'untitled movie'

    class Director(Entity):
      using_options(tablename='director')

      name = Field(Unicode(60))
      movies = OneToMany('Movie')

      class Admin(EntityAdmin):
        verbose_name = 'Director'
        list_display = ['name']

      def __unicode__(self):
        return self.name or 'unknown director'

The last step is to fix `application_admin.py` by adding the following lines to the Director entity to the Movie section:

    Section('Movies',
            self,
            Icon('tango/22x22/mimetypes/x-office-presentation.png'),
            items = [Movie, Director])

This takes care of the relationship between our two entities. Below is the new look of our video store application.

We have just learned the basics of Camelot, and have a nice movie database application we can play with. In another tutorial, we will learn more advanced features of Camelot.

### Navigation

- index
- modules |
- next |
- previous |
- Camelot documentation »
- Camelot Documentation contents »
- Tutorials »

Comments
blog comments powered by Disqus