Eloquent Documentation: Release 0.4
Eloquent Documentation: Release 0.4
Eloquent Documentation: Release 0.4
Release 0.4
Sbastien Eustace
Contents
Installation
Basic Usage
2.1 Configuration . . . . . . .
2.2 Read / Write connections .
2.3 Running queries . . . . .
2.4 Database transactions . .
2.5 Accessing connections . .
2.6 Query logging . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5
5
5
6
7
7
8
Query Builder
3.1 Introduction . . .
3.2 Selects . . . . . .
3.3 Joins . . . . . . .
3.4 Advanced where .
3.5 Aggregates . . . .
3.6 Raw expressions .
3.7 Inserts . . . . . .
3.8 Updates . . . . . .
3.9 Deletes . . . . . .
3.10 Unions . . . . . .
3.11 Pessimistic locking
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
9
9
9
11
12
12
13
13
14
14
14
15
The ORM
4.1 Introduction . . . . . . . .
4.2 Basic Usage . . . . . . . .
4.3 Mass assignment . . . . . .
4.4 Insert, update and delete . .
4.5 Soft deleting . . . . . . . .
4.6 Relationships . . . . . . . .
4.7 Querying relations . . . . .
4.8 Eager loading . . . . . . . .
4.9 Inserting related models . .
4.10 Touching parent timestamps
4.11 Working with pivot table . .
4.12 Timestamps . . . . . . . . .
4.13 Query Scopes . . . . . . . .
4.14 Global Scopes . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
17
17
17
19
20
21
22
27
29
30
32
32
33
33
34
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
4.15
4.16
4.17
4.18
5
ii
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
35
36
37
38
Schema Builder
5.1 Introduction . . . . . . . . . . . . .
5.2 Creating and dropping tables . . . . .
5.3 Adding columns . . . . . . . . . . .
5.4 Changing columns . . . . . . . . . .
5.5 Renaming columns . . . . . . . . . .
5.6 Dropping columns . . . . . . . . . .
5.7 Checking existence . . . . . . . . . .
5.8 Adding indexes . . . . . . . . . . . .
5.9 Dropping indexes . . . . . . . . . . .
5.10 Foreign keys . . . . . . . . . . . . .
5.11 Dropping timestamps and soft deletes
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
41
41
41
42
43
43
43
44
44
44
44
45
Migrations
6.1 Creating Migrations . .
6.2 Running Migrations . .
6.3 Rolling back migrations
6.4 Getting migrations status
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
47
47
49
49
49
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Contents
Contents
CHAPTER 1
Installation
Chapter 1. Installation
CHAPTER 2
Basic Usage
2.1 Configuration
All you need to get you started is the configuration describing your database connections and passing it to a
DatabaseManager instance.
from eloquent import DatabaseManager
config = {
'mysql': {
'driver': 'mysql',
'host': 'localhost',
'database': 'database',
'user': 'root',
'password': '',
'prefix': ''
}
}
db = DatabaseManager(config)
If you have multiple databases configured you can specify which one is the default:
config = {
'default': 'mysql',
'mysql': {
'driver': 'mysql',
'host': 'localhost',
'database': 'database',
'user': 'root',
'password': '',
'prefix': ''
}
}
Note that two keys have been added to the configuration dictionary: read and write. Both of these keys have dictionary values containing a single key: host. The rest of the database options for the read and write connections will
be merged from the main mysql dictionary. So, you only need to place items in the read and write dictionaries
if you wish to override the values in the main dictionary. So, in this case, 192.168.1.1 will be used as the read
connection, while 192.168.1.2 will be used as the write connection. The database credentials, prefix, character
set, and all other options in the main mysql dictionary will be shared across both connections.
Note: The update and delete statements return the number of rows affected by the operation.
Note: Any exception thrown within a transaction block will cause the transaction to be rolled back automatically.
Sometimes you may need to start a transaction yourself:
db.begin_transaction()
Warning: By default, all underlying DBAPI connections are set to be in autocommit mode meaning that you
dont need to explicitly commit after each operation.
You also can access the raw, underlying dbapi connection instance:
db.connection().get_connection()
If you need to disconnect from the given database, use the disconnect method:
db.disconnect('foo')
Executed INSERT INTO "users" ("email", "name", "updated_at") VALUES ('[email protected]', 'foo', '2015-04-0
Note: These log messages above are those logged for MySQL and PostgreSQL connections which support displaying full request sent to the database. For SQLite connections, the format is as follows:
Executed ('SELECT COUNT(*) AS aggregate FROM "users"', []) in 0.12ms
CHAPTER 3
Query Builder
3.1 Introduction
The database query builder provides a fluent interface to create and run database queries. It can be used to perform
most database operations in your application, and works on all supported database systems.
Note: Since Eloquent uses DBAPI packages under the hood, there is no need to clean parameters passed as bindings.
Note: The underlying DBAPI connections are automatically configured to return dictionaries rather than the default
tuple representation.
3.2 Selects
3.2.1 Retrieving all row from a table
users = db.table('users').get()
for user in users:
print(user['name'])
This method will return a list of role titles. It can return a dictionary if you pass an extra key parameter.
roles = db.table('roles').lists('title', 'name')
3.2.9 Or statements
users = db.table('users').where('age', '>', 25).or_where('name', 'John').get()
10
3.3 Joins
The query builder can also be used to write join statements.
3.3. Joins
11
If you would like to use a where style clause on your joins, you may use the where and orWhere methods on a join.
Instead of comparing two columns, these methods will compare the column against a value:
3.5 Aggregates
The query builder also provides a variety of aggregate methods, such as count, max, min, avg, and sum.
users = db.table('users').count()
price = db.table('orders').max('price')
price = db.table('orders').min('price')
price = db.table('orders').avg('price')
total = db.table('users').sum('votes')
12
3.7 Inserts
3.7.1 Insert records into a table
db.table('users').insert(email='[email protected]', votes=0)
db.table('users').insert({
'email': '[email protected]',
'votes': 0
})
Note: It is important to note that there is two notations available. The reason is quite simple: the dictionary notation,
though a little less practical, is here to handle columns names which cannot be passed as keywords arguments.
13
3.8 Updates
3.8.1 Updating records
db.table('users').where('id', 1).update(votes=1)
db.table('users').where('id', 1).update({'votes': 1})
Note: Like the insert statement, there is two notations available. The reason is quite simple: the dictionary
notation, though a little less practical, is here to handle columns names which cannot be passed as keywords arguments.
db.table('users').increment('votes', 5)
db.table('users').decrement('votes')
db.table('users').decrement('votes', 5)
3.9 Deletes
3.9.1 Deleting records
db.table('users').where('age', '<', 25).delete()
3.9.3 Truncate
db.table('users').truncate()
3.10 Unions
The query builder provides a quick and easy way to union two queries:
14
first = db.table('users').where_null('first_name')
users = db.table('users').where_null('last_name').union(first).get()
To lock for update on a SELECT statement, you may use the lock_for_update method on a query:
db.table('users').where('votes', '>', 100).lock_for_update().get()
15
16
CHAPTER 4
The ORM
4.1 Introduction
The ORM provides a simple ActiveRecord implementation for working with your databases. Each database table has
a corresponding Model which is used to interact with that table.
Before getting started, be sure to have configured a DatabaseManager as seen in the Basic Usage section.
from eloquent import DatabaseManager
config = {
'mysql': {
'driver': 'mysql',
'host': 'localhost',
'database': 'database',
'username': 'root',
'password': '',
'prefix': ''
}
}
db = DatabaseManager(config)
And thats pretty much it. You can now define your models.
17
Note that we did not tell the ORM which table to use for the User model. The plural snake case name of the class
name will be used as the table name unless another name is explicitly specified. In this case, the ORM will assume the
User model stores records in the users table. You can specify a custom table by defining a __table__ property
on your model:
class User(Model):
__table__ = 'my_users'
Note: The ORM will also assume that each table has a primary key column named id. You can define a
__primary_key__ property to override this convention. Likewise, you can define a __connection__ property to override the name of the database connection that should be used when using the model.
Once a model is defined, you are ready to start retrieving and creating records in your table. Note that you will need to
place updated_at and created_at columns on your table by default. If you do not wish to have these columns
automatically maintained, set the __timestamps__ property on your model to False.
Note: All methods available on the Query Builder are also available when querying models.
4.2.6 Aggregates
You can also use the query builder aggregate functions:
18
If you feel limited by the builders fluent interface, you can use the where_raw method:
users = User.where_raw('age > ? and votes = 100', [25]).get()
If you are using Read / Write connections, you can force the query to use the write connection with the following
method:
user = User.on_write_connection().find(1)
19
Warning: When using __guarded__, you should still never pass any user input directly since any attribute that
is not guarded can be mass-assigned.
You can also block all attributes from mass-assignment:
__guarded__ = ['*']
Note: Your models will probably have auto-incrementing primary keys. However, if you wish to maintain your own
primary keys, set the __autoincrementing__ property to False.
You can also use the create method to save a model in a single line, but you will need to specify either the
__fillable__ or __guarded__ property on the model since all models are protected against mass-assigment by
default.
After saving or creating a new model with auto-incrementing IDs, you can retrieve the ID by accessing the objects
id attribute:
inserted_id = user.id
20
To add a deleted_at column to your table, you may use the soft_deletes method from a migration (see
Schema Builder):
21
table.soft_deletes()
Now, when you call the delete method on the model, the deleted_at column will be set to the current timestamp.
When querying a model that uses soft deletes, the deleted models will not be included in query results.
4.6 Relationships
Eloquent makes managing and working with relationships easy. It supports many types of relationships:
One To One
One To Many
Many To Many
Has Many Through
Polymorphic relations
Many To Many polymorphic relations
The first argument passed to the has_one method is the class of the related model. Once the relationship is defined,
we can retrieve it using Dynamic properties:
phone = User.find(1).phone
22
The Eloquent ORM assumes the foreign key of the relationship based on the model name. In this case, Phone model
is assumed to use a user_id foreign key. If you want to override this convention, you can pass a second argument
to the has_one method. Furthermore, you may pass a third argument to the method to specify which local column
should be used for the association:
return self.has_one(Phone, 'foreign_key')
return self.has_one(Phone, 'foreign_key', 'local_key')
In the example above, the Eloquent ORM will look for a user_id column on the phones table. You can define a
different foreign key column, you can pass it as the second argument of the belongs_to method:
return self.belongs_to(User, 'local_key')
Additionally, you pass the third parameter which specifies the name of the associated column on the parent table:
return self.belongs_to(User, 'local_key', 'parent_key')
Now you can access the posts comments via Dynamic properties:
comments = Post.find(1).comments
Again, you may override the conventional foreign key by passing a second argument to the has_many method. And,
like the has_one relation, the local column may also be specified:
return self.has_many(Comment, 'foreign_key')
return self.has_many(Comment, 'foreign_key', 'local_key')
4.6. Relationships
23
class Comment(Model):
@property
def post(self):
return self.belongs_to(Post)
If you want to use an unconventional table name for your pivot table, you can pass it as the second argument to the
belongs_to_many method:
return self.belongs_to_many(Role, 'user_role')
Of course, you also can define the inverse og the relationship on the Role model:
class Role(Model):
@property
def users(self):
return self.belongs_to_many(Role)
24
name - string
posts:
id - integer
user_id - integer
title - string
Even though the posts table does not contain a country_id column, the has_many_through relation will
allow access a countrys posts via country.posts:
class Country(Model):
@property
def posts(self):
return self.has_many_through(Post, User)
If you want to manually specify the keys of the relationship, you can pass them as the third and fourth arguments to
the method:
return self.has_many_through(Post, User, 'country_id', 'user_id')
4.6. Relationships
25
The imageable relation on the Photo model will return either a Staff or Order instance, depending on which
type of model owns the photo.
Polymorphic relation table structure
To help understand how this works, lets explore the database structure for a polymorphic relation:
staff
id - integer
name - string
orders
id - integer
price - integer
photos
id - integer
path - string
imageable_id - integer
imageable_type - string
The key fields to notice here are the imageable_id and imageable_type on the photos table. The ID will
contain the ID value of, in this example, the owning staff or order, while the type will contain the class name of the
owning model. This is what allows the ORM to determine which type of owning model to return when accessing the
imageable relation.
26
taggables
tag_id - integer
taggable_id - integer
taggable_type - string
The Post and Video model will both have a morph_to_many relationship via a tags method:
class Post(Model):
@property
def tags(self):
return self.morph_to_many(Tag, 'taggable')
The Tag model can define a method for each of its relationships:
class Tag(Model):
@property
def posts(self):
return self.morphed_by_many(Post, 'taggable')
@property
def videos(self):
return self.morphed_by_many(Video, 'taggable')
27
If you need even more power, you can use the where_has and or_where_has methods to put where conditions
on your has queries:
posts = Post.where_has(
'comments',
lambda q: q.where('content', 'like', 'foo%')
).get()
If you need to add further constraints to which comments are retrieved, you may call the comments method and
continue chaining conditions:
28
Note: Relationships that return many results will return an instance of the Collection class.
This loop will execute 1 query to retrieve all the books on the table, then another query for each book to retrieve the
author. So, if we have 25 books, this loop will run 26 queries.
To drastically reduce the number of queries you can use eager loading. The relationships that should be eager loaded
can be specified via the with_ method.
for book in Book.with_('author').get():
print(book.author.name)
In this example, the author relationship will be eager loaded as well as the authors contacts relation.
29
In this example, were eager loading the users posts only if the posts title contains the word first.
When passing a query as a constraint, only the where clause is supported, if you want to be more specific you can use
a callback:
users = User.with_({
'posts': lambda q: q.where('title', 'like', '%first%').order_by('created_at', 'desc')
})
30
user.save()
You can also pass a dictionary of attributes that should be stored on the pivot table for the relation:
user.roles().attach(3, {'expires': expires})
Sometimes you might want to create a new related model and attach it in a single command. For that, you can use the
save method:
role = Role(name='Editor')
User.find(1).roles().save(role)
31
Now, when you update a Comment, the owning Post will have its updated_at column updated.
Note that each retrieved Role model is automatically assigned a pivot attribute. This attribute contains e model
instance representing the intermediate table, and can be used as any other model.
By default, only the keys will be present on the pivot object. If your pivot table contains extra attributes, you must
specify them when defining the relationship:
return self.belongs_to_many(Role).with_pivot('foo', 'bar')
Now the foo and bar attributes will be accessible on the pivot object for the Role model.
If you want your pivot table to have automatically maintained created_at and updated_at timestamps, use the
with_timestamps method on the relationship definition:
return self.belongs_to_many(Role).with_timestamps()
32
4.12 Timestamps
By default, the ORM will maintain the created_at and updated_at columns on your database table automatically. Simply add these timestamp columns to your table. If you do not wish for the ORM to maintain these
columns, just add the __timestamps__ property:
class User(Model):
__timestamps__ = False
4.12. Timestamps
33
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 an Builder query builder object and the Model its applied to, and is responsible for
adding any additional where clauses that the scope wishes to add. The remove method also receives a Builder
object and Model and is responsible for reversing the action taken by apply. In other words, remove should
remove the where clause (or any other clause) that was added. So, for our SoftDeletingScope, 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
34
class User(Model):
@accessor
def first_name(self):
first_name = self.get_raw_attribute('first_name')
return first_name[0].upper() + first_name[1:]
35
class User(Model):
@mutator
def first_name(self, value):
self.set_raw_attribute(value.lower())
Note: If the column being mutated already has an accessor, you can use it has a mutator:
from eloquent.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())
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:]
36
class User(Model):
__dates__ = ['synchronized_at']
class User(Model):
def get_dates(self):
return ['created_at']
When a column is considered a date, you can set its value to a UNIX timestamp, a date string YYYY-MM-DD, a
datetime string, a native date or datetime and of course an Arrow instance.
To completely disable date mutations, simply return an empty list from the get_dates method.
class User(Model):
def get_dates(self):
return []
Now the is_admin attribute will always be cast to a boolean when you access it, even if the underlying value is
stored in the database as an integer. Other supported cast types are: int, float, str, bool, dict, list.
The dict cast is particularly useful for working with columns that are stored as serialized JSON. For example, if your
database has a TEXT type field that contains serialized JSON, adding the dict cast to that attribute will automatically
deserialize the attribute to a dictionary when you access it on your model:
__casts__ = {
'options': 'dict'
}
37
Once you have created the accessor, just add the value to the __appends__ property on the model:
class User(Model):
__append__ = ['is_admin']
@accessor
38
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 models dictionary and
JSON forms. Attributes in the __appends__ list respect the __visible__ and __hidden__ configuration on
the model.
39
40
CHAPTER 5
Schema Builder
5.1 Introduction
The Schema class provides a database agnostic way of manipulating tables.
Before getting started, be sure to have configured a DatabaseManager as seen in the Basic Usage section.
from eloquent import DatabaseManager, Schema
config = {
'mysql': {
'driver': 'mysql',
'host': 'localhost',
'database': 'database',
'username': 'root',
'password': '',
'prefix': ''
}
}
db = DatabaseManager(config)
schema = Schema(db)
The table variable is a Blueprint instance which can be used to define the new table.
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')
41
The table builder contains a variety of column types that you may use when building your tables:
Command
table.big_increments(id)
table.big_integer(votes)
table.binary(data)
table.boolean(confirmed)
table.char(name, 4)
table.date(created_on)
table.datetime(created_at)
table.decimal(amount, 5, 2)
table.double(column, 15, 8)
table.enum(choices, [foo, bar])
table.float(amount)
table.increments(id)
table.integer(votes)
table.json(options)
table.long_text(description)
table.medium_integer(votes)
table.medium_text(description)
table.morphs(taggable)
table.nullable_timestamps()
table.small_integer(votes)
table.soft_deletes()
table.string(email)
table.string(votes, 100)
table.text(description)
table.time(sunrise)
table.timestamp(added_at)
table.timestamps()
.nullable()
.default(value)
.unsigned()
42
Description
Incrementing ID using a big integer equivalent
BIGINT equivalent to the table
BLOB equivalent to the table
BOOLEAN equivalent to the table
CHAR equivalent with a length
DATE equivalent to the table
DATETIME equivalent to the table
DECIMAL equivalent to the table with a precision and scale
DOUBLE equivalent to the table with precision, 15 digits in total and 8 afte
ENUM equivalent to the table
FLOAT equivalent to the table
Incrementing ID to the table (primary key)
INTEGER equivalent to the table
JSON equivalent to the table
LONGTEXT equivalent to the table
MEDIUMINT equivalent to the table
MEDIUMTEXT equivalent to the table
Adds INTEGER taggable_id and STRING taggable_type
Same as timestamps(), except allows NULLs
SMALLINT equivalent to the table
Adds deleted_at column for soft deletes
VARCHAR equivalent column
VARCHAR equivalent with a length
TEXT equivalent to the table
TIME equivalent to the table
TIMESTAMP equivalent to the table
Adds created_at and updated_at columns
Designate that the column allows NULL values
Declare a default value for a column
Set INTEGER to UNSIGNED
Warning: The column change feature, while tested, is still considered in beta stage. Please report any encountered
issue or bug on the Github project
Warning: 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')
43
Or, you may choose to add the indexes on separate lines. Below is a list of all available index types:
Command
table.primary(id)
table.primary([first, last])
table.unique(email)
table.index(state)
Description
Adds a primary key
Adds composite keys
Adds a unique index
Adds a basic index
Description
Drops a primary key from the users table
Drops a unique index from the users table
Drops a basic index from the geo table
44
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')
Note: When creating a foreign key that references an incrementing integer, remember to always make the foreign key
column unsigned.
Description
Drops the created_at and deleted_at columns
Drops the deleted_at column
45
46
CHAPTER 6
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
databases schema.
Note: 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': ''
}
}
class CreateTableUsers(Migration):
def up(self):
"""
Run the migrations.
"""
pass
def down(self):
47
"""
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:
eloquent 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:
eloquent migrations:make add_votes_to_users_table -c databases.py --table=users
eloquent migrations:make create_users_table -c databases.py --table=users --create
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 eloquent.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')
48
Chapter 6. Migrations
49