Eloquent 0.4 is now out. This version introduces the schema builder, soft deletes, mixins and scopes.
Schema Builder
The new schema builder is here to help manipulate your databases structure in an intuitive manner.
For that purpose you can use the
Schema class which provides a database
agnostic way of manipulating tables.
from eloquent import DatabaseManager, Schema config = { 'mysql': { 'driver': 'mysql', 'host': 'localhost', 'database': 'database', 'username': 'root', 'password': '', 'prefix': '' } } db = DatabaseManager(config) schema = Schema(db)
Creating and dropping tables
To create a new database table, the
create method is used:
with schema.create('users') as table: table.increments('id')
To rename an existing database table, the
rename method can be used:
schema.rename('from', 'to')
To specify which connection the schema operation should take place on, use the
connection method:
with schema.connection('foo').create('users') as table: table.increments('id')
To drop a table, you can use the
drop or
drop_if_exists methods:
schema.drop('users') schema.drop_if_exists('users')
Adding columns
To update an existing table, you can use the
table method:
with schema.table('users') as table: table.string('email') table.integer('votes')
The table builder contains a variety of column types that you can use. See the official documentation for the full list.
Changing columns
Sometimes you may need to modify an existing column.
For example, you may wish to increase the size of a string column.
To do so, you can use the
change method:
with schema.table('user') as table: table.string('name', 50).nullable().change()
The change column feature, while tested, is still considered in beta stage. Please report any encountered issue or bug on the Github Project
Renaming columns
To rename a column, you can use use the
rename_column method on the Schema builder:
with schema.table('users') as table: table.rename('from', 'to')
Prior to MySQL 5.6.6, foreign keys are NOT automatically updated when renaming columns. Therefore, you will need to drop the foreign key constraint, rename the column and recreate the constraint to avoid an error.
with schema.table('posts') as table: table.drop_foreign('posts_user_id_foreign') table.rename('user_id', 'author_id') table.foreign('author_id').references('id').on('users')
In future versions, Eloquent might handle this automatically.
The rename column feature, while tested, is still considered in beta stage (especially for SQLite). Please report any encountered issue or bug on the Github Project
Dropping columns
To drop a column, you can use use the
drop_column method on the
Schema builder:
# Dropping a single column with schema.table('users') as table: table.drop_column('votes') # Dropping multiple columns with schema.table('users') as table: table.drop_column('votes', 'avatar', 'location')
Foreign keys
Eloquent also provides support for adding foreign key constraints to your tables:
table.integer('user_id').unsigned() table.foreign('user_id').references('id').on('users')
In this example, we are stating that the
user_id
column references the
id column on the
users table.
Make sure to create the foreign key column first!
You may also specify options for the "on delete" and "on update" actions of the constraint:
table.foreign('user_id')\ .references('id').on('users')\ .on_delete('cascade')
To drop a foreign key, you may use the
drop_foreign method.
A similar naming convention is used for foreign keys as is used for other indexes:
table.drop_foreign('posts_user_id_foreign')
There is more you can do with the schema builder, like handling indexes. Referer to the documentation to see all the available features.
Soft deleting
When soft deleting a model, it is not actually removed from your database.
Instead, a
deleted_at timestamp is set on the record.
To enable soft deletes for a model, make it inherit from the
SoftDeletes mixin:
from eloquent import Model, SoftDeletes class User(Model, SoftDeletes): __dates__ = ['deleted_at']_trashed method on the query:
users = User.with_trashed().where('account_id', 1).get()
The
with_trashed method may be used on a defined relationship:
user.posts().with_trashed().get()
Query Scopes
Defining a query scope
Scopes allow you to easily re-use query logic in your models.
To define a scope, simply prefix a model method with
scope:
from eloquent Model, scope class User(Model): def scope_popular(self, query): return query.where('votes', '>', 100) def scope_women(self, query): return query.where_gender('W')
Using a query scope
users = User.popular().women().order_by('created_at').get()
Dynamic scopes
Sometimes you may wish to define a scope that accepts parameters. Just add your parameters to your scope function:
class User(Model): def scope_of_type(self, query, type): return query.where_type(type)
Then pass the parameter into the scope call:
users = User.of_type('member').get()
Global Scopes
Sometimes you may wish to define a scope that applies to all queries performed on a model.
In essence, this is how Eloquent's own "soft delete" feature works.
Global scopes are defined using a combination of mixins and an implementation of the
Scope class.
First, let's define a mixin. For this example, we'll use the
SoftDeletes that ships with Eloquent:
from eloquent import SoftDeletingScope class SoftDeletes(object): @classmethod def boot_soft_deletes(cls, model_class): """ Boot the soft deleting mixin for a model. """ model_class.add_global_scope(SoftDeletingScope())
If an Eloquent model inherits from a mixin that has a method matching the
boot_name_of_trait
naming convention, that mixin method will be called when the Eloquent model is booted,
giving you an opportunity to register a global scope, or do anything else you want.
A scope must be an instance of the
Scope class, which specifies two methods:
apply and
remove.
The apply method receives, it would look something like this:
from eloquent import Scope class SoftDeletingScope(Scope): def apply(self, builder, model): """ Apply the scope to a given query builder. :param builder: The query builder :type builder: eloquent.orm.builder.Builder :param model: The model :type model: eloquent.orm.Model """ builder.where_null(model.get_qualified_deleted_at_column()) def remove(self, builder, model): """ Remove the scope from a given query builder. :param builder: The query builder :type builder: eloquent.orm.builder.Builder :param model: The model :type model: eloquent.orm.Model """ column = model.get_qualified_deleted_at_column() query = builder.get_query() wheres = [] for where in query.wheres: # If the where clause is a soft delete date constraint, # we will remove it from the query and reset the keys # on the wheres. This allows the developer to include # deleted model in a relationship result set that is lazy loaded. if not self._is_soft_delete_constraint(where, column): wheres.append(where) query.wheres = wheres
What's next?
Finally, here are some features targeted for the 0.5 version:
- Database migrations based on the schema builder:
from eloquent import Migration class UserMigration(Migration): def up(self): """ Run the migrations """ with self.schema.table('users') as table: # ... def down(self): """ Revert the migrations """ with self.schema.table('users') as table: # ...
- Support for model accessors and mutators.
You can check the advancement on the Github project