Orator 0.5 is now out. This version introduces database migrations and attributes accessors and mutators.

Database migrations

Migrations are a type of version control for your database. They allow a team to modify the database schema and stay up to date on the current schema state. Migrations are typically paired with the Schema Builder to easily manage your database’s schema.

For the migrations to actually work, you need a configuration file describing your databases in a DATABASES dict, like so:

DATABASES = {
    'mysql': {
        'driver': 'mysql',
        'host': 'localhost',
        'database': 'database',
        'username': 'root',
        'password': '',
        'prefix': ''
    }
}

This file needs to be specified when using migrations commands.

Creating migrations

To create a migration, you can use the migrations:make command on the Orator CLI:

orator migrations:make create_users_table -c databases.py

This will create a migration file that looks like this:

from orator.migrations import Migration


class CreateTableUsers(Migration):

    def up(self):
        """
        Run the migrations.
        """
        pass

    def down(self):
        """
        Revert the migrations.
        """
        pass
By default, the migration will be placed in a `migrations` folder relative to where the command has been executed, and will contain a timestamp which allows the framework to determine the order of the migrations. If you want the migrations to be stored in another folder, use the `--path/-p` option:
orator migrations:make create_users_table -c databases.py -p my/path/to/migrations
The `--table` and `--create` options can also be used to indicate the name of the table, and whether the migration will be creating a new table:
orator migrations:make add_votes_to_users_table -c databases.py --table=users

orator migrations:make create_users_table -c databases.py --table=users --create
These commands would respectively create the following migrations:
from orator.migrations import Migration


class AddVotesToUsersTable(Migration):

    def up(self):
        """
        Run the migrations.
        """
        with self.schema.table('users') as table:
            pass

    def down(self):
        """
        Revert the migrations.
        """
        with self.schema.table('users') as table:
            pass
from orator.migrations import Migration


class CreateTableUsers(Migration):

    def up(self):
        """
        Run the migrations.
        """
        with self.schema.create('users') as table:
            table.increments('id')
            table.timestamps()

    def down(self):
        """
        Revert the migrations.
        """
        self.schema.drop('users')
### Running migrations To run all outstanding migrations, just use the `migrations:run` command:
orator migrations:run -c databases.py
### Rolling back migrations #### Rollback the last migration operation
orator migrations:rollback -c databases.py
#### Rollback all migrations
eloquent migrations:reset -c databases.py
### Getting migrations status To see the status of the migrations, just use the migrations:status command:
orator migrations:status -c databases.py
This would output something like this:
+----------------------------------------------------+------+
| Migration                                          | Ran? |
+----------------------------------------------------+------+
| 2015_05_02_04371430559457_create_users_table       | Yes  |
| 2015_05_04_02361430725012_add_votes_to_users_table | No   |
+----------------------------------------------------+------+
## Accessors & mutators Orator provides a convenient way to transform your model attributes when getting or setting them. ### Defining an accessor Simply use the `accessor` decorator on your model to declare an accessor:
from orator.orm import Model, accessor


class User(Model):

    @accessor
    def first_name(self):
        first_name = self.get_raw_attribute('first_name')

        return first_name[0].upper() + first_name[1:]
In the example above, the `first_name` column has an accessor.

The name of the decorated function must match the name of the column being accessed.

Defining a mutator

Mutators are declared in a similar fashion:

from orator.orm import Model, mutator


class User(Model):

    @mutator
    def first_name(self, value):
        self.set_raw_attribute('first_name', value)

If the column being mutated already has an accessor, you can use it has a mutator:

from orator.orm import Model, accessor


class User(Model):

    @accessor
    def first_name(self):
        first_name = self.get_raw_attribute('first_name')

        return first_name[0].upper() + first_name[1:]

    @first_name.mutator
    def set_first_name(self, value):
        self.set_raw_attribute(value.lower())

The inverse is also possible:

from orator.orm import Model, mutator


class User(Model):

    @mutator
    def first_name(self, value):
        self.set_raw_attribute(value.lower())

    @first_name.accessor
    def get_first_name(self):
        first_name = self.get_raw_attribute('first_name')

        return first_name[0].upper() + first_name[1:]

Appendable attributes

When converting a model to a dictionary or a JSON string, you may occasionally need to add dictionary attributes that do not have a corresponding column in your database. To do so, simply define an accessor for the value:

class User(Model):

    @accessor
    def is_admin(self):
        return self.get_raw_attribute('admin') == 'yes'

Once you have created the accessor, just add the value to the __appends__ property on the model:

class User(Model):

    __append__ = ['is_admin']

    @accessor
    def is_admin(self):
        return self.get_raw_attribute('admin') == 'yes'

Once the attribute has been added to the __appends__ list, it will be included in both the model's dictionary and JSON forms. Attributes in the __appends__ list respect the __visible__ and __hidden__ configuration on the model.

There is a lot more you can do with Orator, just give a look at the documentation to see all available features.

What's next?

Finally, here are some features targeted for future versions (actual roadmap to be determined):

  • Model events:
from models import User


def check_user(user):
    if not user.is_valid():
        return False

User.creating(check_user)
  • Extra functionalities, like caching.
View Comments