.. _quickstart:

Quickstart
==========

If you've read our :ref:`history` then you know about *jrnl*.

Let's use jrnl to show how to use alkali.

Entry
------

::

    from __future__ import unicode_literals, absolute_import
    from alkali import Model, fields, tznow

    from .storage import JournalStorage

    class Entry(Model):
        class Meta:
            storage = JournalStorage

        date  = fields.DateTimeField(primary_key = True)
        title = fields.StringField()
        body  = fields.StringField()

Lets break this down a bit.

Model
+++++

::

    class Entry(Model):

Inherit from :class:`alkali.model.Model` to make a new model. A ``Model`` class
the equivalent to a database table schema. A ``Model`` instance is the
equivalent to a row in that table.

Meta
++++

::

    class Meta:
        storage = JournalStorage

A ``Meta`` class is optional but it is handy to specify behaviour of the
given model. In this case we're using ``JournalStorage`` to save this
model.

Known ``Meta`` properties are:

* ``storage``: specify the storage class that reads/writes to persistent storage. This value
  overrides the database default.
* ``filename``: specify the actual file to read/write to. If omitted, the filename will
  default to *<model name>.<storage.extension>*. The ``Database`` can override this of course.

.. * ``ordering``: specify the default order that the storage class reads/writes its entries

Fields
++++++

::

    date  = fields.DateTimeField(primary_key = True)
    title = fields.StringField()
    body  = fields.StringField()

At the class level, define some *variables* of type :class:`alkali.fields.IField`.

Feel free to make your own if the few that come with
alkali are insufficient. It would not be hard to make more
complicated fields like a *BitmapField*, all one would have
to do is override :func:`alkali.fields.IField.dumps` and
:func:`alkali.fields.IField.loads`.

alkali ships with the following fields:

* :class:`alkali.fields.IntField`
* :class:`alkali.fields.FloatField`
* :class:`alkali.fields.StringField`, a unicode string
* :class:`alkali.fields.DateTimeField`, complete with timezone

Fields can take a keyword ``primary_key``. Unlike Django, alkali doesn't automatically
create an ``id`` field that is the primary key, you must specify your own. Not only that,
you can specify multiple fields as ``primary_key`` to create a compound primary key.

Journal
-------

The main parent object in alkali is the
:class:`alkali.database.Database`. You'll probably want to encapsulate
the ``Database`` in something, in jrnls case that would be the
``Journal`` class.

::

    from alkali import Database

    class Journal(object):
        def __init__(self, filename=None, save_on_exit=True ):

            # set the filename in Meta so future Storage calls have a
            # file to work with
            Entry.Meta.filename = filename

            self._db = Database( models=[Entry], save_on_exit=save_on_exit )
            self._db.load()

Lets break this down a bit.

Database
++++++++

::

    self._db = Database( models=[Entry], save_on_exit=save_on_exit )
    self._db.load()

Create a ``Database`` object. The only required parameter is ``models``,
a ``list`` of ``Model`` classes that comprise the database.

``save_on_exit`` tells the database to save all its data when it goes out of scope. This
means the developer doesn't have to explicitly call :func:`alkali.database.Database.store`.

Meta
++++

::

    Entry.Meta.filename = filename

You can set the ``Model.Meta.filename`` at definition time or set it later at runtime.

By Our Powers Combined
----------------------

So lets make an entry and save it to the database.

::

    from alkali import Database, tznow

    db = Database(models=[Entry], save_on_exit=True)

    e = Entry(date=tznow(), title="my first entry", body="alkali is pretty good")
    e.save()    # adds model instance to Entry.objects

    db.store()  # saved to ./Entry.json because those are the defaults

    e = Entry.objects.get( title="my first entry" )
    e.body = "alkali is the bestest"

    # updated entry will be saved when database goes out of scope
    # because save_on_exit is True
