| 1 | ======== |
|---|
| 2 | Tutorial |
|---|
| 3 | ======== |
|---|
| 4 | |
|---|
| 5 | .. CHECKME: this is not rendered? |
|---|
| 6 | |
|---|
| 7 | --------- |
|---|
| 8 | Diving in |
|---|
| 9 | --------- |
|---|
| 10 | |
|---|
| 11 | 1. Installation |
|---|
| 12 | --------------- |
|---|
| 13 | |
|---|
| 14 | In this tutorial, we will show you how to create a small and simple model. |
|---|
| 15 | Before we start please make sure that you already have the Elixir package |
|---|
| 16 | installed. If not, do so by typing [#]_: |
|---|
| 17 | |
|---|
| 18 | :: |
|---|
| 19 | |
|---|
| 20 | easy_install Elixir |
|---|
| 21 | |
|---|
| 22 | .. [#] If you don't even have "``easy_install``" yet, please visit the |
|---|
| 23 | `EasyInstall website |
|---|
| 24 | <http://peak.telecommunity.com/DevCenter/EasyInstall>`_ first and find |
|---|
| 25 | out how to use it (it's pretty easy, like the name promises). |
|---|
| 26 | |
|---|
| 27 | 2. A very simple model |
|---|
| 28 | ---------------------- |
|---|
| 29 | |
|---|
| 30 | Now fire up your favorite text editor and create a file "``model.py``" |
|---|
| 31 | containing the following lines: |
|---|
| 32 | |
|---|
| 33 | :: |
|---|
| 34 | |
|---|
| 35 | from elixir import * |
|---|
| 36 | |
|---|
| 37 | metadata.connect("sqlite:///movies.sqlite") |
|---|
| 38 | |
|---|
| 39 | class Movie(Entity): |
|---|
| 40 | with_fields( |
|---|
| 41 | title = Field(Unicode(30)), |
|---|
| 42 | year = Field(Integer), |
|---|
| 43 | description = Field(Unicode) |
|---|
| 44 | ) |
|---|
| 45 | |
|---|
| 46 | def __repr__(self): |
|---|
| 47 | return '<Movie "%s" (%d)>' % (self.title, self.year) |
|---|
| 48 | |
|---|
| 49 | |
|---|
| 50 | What does this snippet do? First of all it connects to an SQLite-database |
|---|
| 51 | [#]_. Then it declares a ``Movie``-entity (ie. a class, that inherits Elixir's |
|---|
| 52 | ``Entity``-baseclass). This entity will hold three fields: |
|---|
| 53 | |
|---|
| 54 | - ``title``: holds up to 30 unicode chars, which represent the movie's title |
|---|
| 55 | - ``year``: an integer containing the year the movie was released |
|---|
| 56 | - ``description``: this could be a plot summary, a review, or any long string |
|---|
| 57 | of text that you like. |
|---|
| 58 | |
|---|
| 59 | The ``__repr__()``-method below is totally unrelated to Elixir, it just tells |
|---|
| 60 | the python interpreter to print objects in a human-readable way. It's nice to |
|---|
| 61 | have, but fully optional. We have put this into our model so that we can |
|---|
| 62 | easily trace what is happening in an interactive python interpreter. |
|---|
| 63 | |
|---|
| 64 | Also, please note that elixir currently provide two different ways to declare |
|---|
| 65 | the fields on your entities. We have not decided yet on which one we like best, |
|---|
| 66 | or if we will always keep both. The other way to declare your fields is using |
|---|
| 67 | the ``has_field`` statement, rather than the ``with_fields`` statement. The |
|---|
| 68 | ``Movie`` example above can be declared using the ``has_field`` statement like |
|---|
| 69 | so: |
|---|
| 70 | |
|---|
| 71 | :: |
|---|
| 72 | |
|---|
| 73 | from elixir import * |
|---|
| 74 | |
|---|
| 75 | metadata.connect("sqlite:///movies.sqlite") |
|---|
| 76 | |
|---|
| 77 | class Movie(Entity): |
|---|
| 78 | has_field('title', Unicode(30)) |
|---|
| 79 | has_field('year', Integer) |
|---|
| 80 | has_field('description', Unicode) |
|---|
| 81 | |
|---|
| 82 | def __repr__(self): |
|---|
| 83 | return '<Movie "%s" (%d)>' % (self.title, self.year) |
|---|
| 84 | |
|---|
| 85 | |
|---|
| 86 | If you have a strong preference for one syntax over the other, please let us |
|---|
| 87 | know so that we can make a good decision before our final stable release! |
|---|
| 88 | |
|---|
| 89 | |
|---|
| 90 | .. [#] This assumes you have `pysqlite <http://pysqlite.org/>`_ |
|---|
| 91 | installed. You may use any other database instead, as long as it's |
|---|
| 92 | `supported by SQLAlchemy <http://www.sqlalchemy.org/features.myt>`_. |
|---|
| 93 | |
|---|
| 94 | |
|---|
| 95 | 3. Action! |
|---|
| 96 | ---------- |
|---|
| 97 | |
|---|
| 98 | What time is it? It's database-table-creation-time! Fire up your python |
|---|
| 99 | interpreter of choice (preferably `IPython <http://ipython.scipy.org/>`_) and |
|---|
| 100 | hack in the lines below [#]_ (only lines starting with "``>>>``", of course): |
|---|
| 101 | |
|---|
| 102 | :: |
|---|
| 103 | |
|---|
| 104 | >>> from model import * |
|---|
| 105 | >>> create_all() |
|---|
| 106 | >>> Movie(title="Blade Runner", year=1982) |
|---|
| 107 | <Movie "Blade Runner" (1982)> |
|---|
| 108 | |
|---|
| 109 | You've created all necessary tables by executing ``create_all()`` and then |
|---|
| 110 | instantiated a first movie-object. You could add more movies here, but so far |
|---|
| 111 | "Blade Runner" does the job. |
|---|
| 112 | |
|---|
| 113 | Because SQLAlchemy tries to do as many operations as possible in one single |
|---|
| 114 | operation (a so called `Unit of Work`), which is very efficient, the data has |
|---|
| 115 | not been written to the database table yet. You can tell SQLAlchemy to do so |
|---|
| 116 | by typing: |
|---|
| 117 | |
|---|
| 118 | :: |
|---|
| 119 | |
|---|
| 120 | >>> objectstore.flush() |
|---|
| 121 | |
|---|
| 122 | This will tell SQLAlchemy to generate all of the SQL to insert the Movie into |
|---|
| 123 | the database, and then execute that SQL. Now, to see a list of all the movies |
|---|
| 124 | in our database, simply type: |
|---|
| 125 | |
|---|
| 126 | :: |
|---|
| 127 | |
|---|
| 128 | >>> Movie.select() |
|---|
| 129 | [<Movie "Blade Runner" (1982)>] |
|---|
| 130 | |
|---|
| 131 | Not many, but exactly what we expected. Close the interpreter now and delete |
|---|
| 132 | [#]_ the database file (``movies.sqlite``), we will recreate and populate it |
|---|
| 133 | in the next step. |
|---|
| 134 | |
|---|
| 135 | .. [#] Make sure, you're running your interpreter from the directory where you |
|---|
| 136 | saved the ``model.py`` file. |
|---|
| 137 | .. [#] If you're using any other DBMS than SQLite, just drop the created table |
|---|
| 138 | (most probably something like ``DROP TABLE model_movie;``). |
|---|
| 139 | |
|---|
| 140 | 4. Relationships |
|---|
| 141 | ---------------- |
|---|
| 142 | |
|---|
| 143 | So far you've seen how to declare simple entities, create objects, store them |
|---|
| 144 | to the database and retrieve them again. Not too much magic, but a lot more |
|---|
| 145 | pleasant to the eye compared to calling lowlevel SQL-statements. |
|---|
| 146 | |
|---|
| 147 | Now we will do something more advanced. Movies need genres, so we'll add a new |
|---|
| 148 | entity "``Genre``" to our ``model.py``: |
|---|
| 149 | |
|---|
| 150 | :: |
|---|
| 151 | |
|---|
| 152 | class Genre(Entity): |
|---|
| 153 | with_fields( |
|---|
| 154 | name = Field(Unicode(15), unique=True) |
|---|
| 155 | ) |
|---|
| 156 | |
|---|
| 157 | def __repr__(self): |
|---|
| 158 | return '<Genre "%s">' % self.name |
|---|
| 159 | |
|---|
| 160 | The ``__repr__()``-method is totally optional, again. We're limiting the |
|---|
| 161 | length of our genres to 15 characters and demand that those names are |
|---|
| 162 | unique. There's no use in having two genres with the same name, right? |
|---|
| 163 | |
|---|
| 164 | We could start the interpreter again and instantiate some genres, but before |
|---|
| 165 | we do that, we want to tell Elixir how to relate movies and genres to add more |
|---|
| 166 | excitement. Add two lines to your ``model.py``, so it reads: |
|---|
| 167 | |
|---|
| 168 | :: |
|---|
| 169 | |
|---|
| 170 | class Movie(Entity): |
|---|
| 171 | with_fields( |
|---|
| 172 | title = Field(Unicode(30)), |
|---|
| 173 | year = Field(Integer), |
|---|
| 174 | description = Field(Unicode) |
|---|
| 175 | ) |
|---|
| 176 | |
|---|
| 177 | belongs_to('genre', of_kind='Genre') # add this line |
|---|
| 178 | |
|---|
| 179 | def __repr__(self): |
|---|
| 180 | return '<Movie "%s" (%d)>' % (self.title, self.year) |
|---|
| 181 | |
|---|
| 182 | |
|---|
| 183 | class Genre(Entity): |
|---|
| 184 | with_fields( |
|---|
| 185 | name = Field(Unicode(15)) |
|---|
| 186 | ) |
|---|
| 187 | |
|---|
| 188 | has_many('movies', of_kind='Movie') # and this one |
|---|
| 189 | |
|---|
| 190 | def __repr__(self): |
|---|
| 191 | return '<Genre "%s">' % self.name |
|---|
| 192 | |
|---|
| 193 | A movie belongs to a genre and a genre may have many movies, that's what it |
|---|
| 194 | says. We could let movies have multiple genres by changing the ``belongs_to``- |
|---|
| 195 | and ``has_many``-statements in both lines to ``has_and_belongs_to_many``, but |
|---|
| 196 | one genre per movie is enough for our example. |
|---|
| 197 | |
|---|
| 198 | Again, start your python interpreter, ensure that the old database table has |
|---|
| 199 | been deleted, and then create your new model's tables: |
|---|
| 200 | |
|---|
| 201 | :: |
|---|
| 202 | |
|---|
| 203 | >>> from model import * |
|---|
| 204 | >>> create_all() |
|---|
| 205 | |
|---|
| 206 | Create a genre and a couple of titles: |
|---|
| 207 | |
|---|
| 208 | :: |
|---|
| 209 | |
|---|
| 210 | >>> scifi = Genre(name="Science-Fiction") |
|---|
| 211 | >>> alien = Movie(title="Alien", year=1979) |
|---|
| 212 | >>> starwars = Movie(title="Star Wars", year=1977) |
|---|
| 213 | >>> brunner = Movie(title="Blade Runner", year=1982) |
|---|
| 214 | |
|---|
| 215 | Now add the movies to the genre (just like you'd do if it was a plain old |
|---|
| 216 | python list), and flush the objectstore: |
|---|
| 217 | |
|---|
| 218 | :: |
|---|
| 219 | |
|---|
| 220 | >>> scifi.movies.append(alien) |
|---|
| 221 | >>> scifi.movies.append(starwars) |
|---|
| 222 | >>> scifi.movies.append(brunner) |
|---|
| 223 | >>> objectstore.flush() |
|---|
| 224 | |
|---|
| 225 | Let's demonstrate some magic now, enter these two lines: |
|---|
| 226 | |
|---|
| 227 | :: |
|---|
| 228 | |
|---|
| 229 | >>> m = Movie.get_by(title="Alien") |
|---|
| 230 | >>> m.title, m.genre |
|---|
| 231 | ('Alien', <Genre "Science-Fiction">) |
|---|
| 232 | |
|---|
| 233 | As you can see, all the dirty work has been done for you - automatically and |
|---|
| 234 | out of your way. Primary [#]_ and foreign keys have been generated |
|---|
| 235 | automatically, and if you'd used ``has_and_belongs_to_many``, even the |
|---|
| 236 | necessary secondary tables. |
|---|
| 237 | |
|---|
| 238 | .. [#] If you don't specify any primary keys by passing ``primary_key=True`` |
|---|
| 239 | as a keyword-argument to the ``Field()``-construct, Elixir will |
|---|
| 240 | automatically create one for you, which will then be accessible under |
|---|
| 241 | the name ``id``. |
|---|
| 242 | |
|---|
| 243 | |
|---|
| 244 | 5. Where to go from here? |
|---|
| 245 | ------------------------- |
|---|
| 246 | |
|---|
| 247 | You have created a simple model with some entities and set up some |
|---|
| 248 | relationships between them. Now that you've seen how it basically works, take |
|---|
| 249 | a closer look at Elixir's `API-docs <module-index.html>`_, `examples |
|---|
| 250 | <examples.html>`_ and `testcases |
|---|
| 251 | <http://elixir.ematia.de/svn/elixir/trunk/tests/>`_, which come with the |
|---|
| 252 | source distribution, and see how to create other types of relationships and |
|---|
| 253 | fine tweak Elixir's options to your liking. |
|---|
| 254 | |
|---|
| 255 | To learn more about the available datatypes, how to retrieve and modify data |
|---|
| 256 | and about lower level features please consult `SQLAlchemy's detailed |
|---|
| 257 | documentation <http://www.sqlalchemy.org/docs/>`_. |
|---|
| 258 | |
|---|
| 259 | Enjoy database development the easy way and let us know when you created |
|---|
| 260 | something cool! |
|---|
| 261 | |
|---|
| 262 | |
|---|