Gram Model
First Story
As a
User,
I want to be able to
create/read/delete/ a post (text only)
so that
I have my minigram
Review of MVC
We think in terms of Model, View and Controller when design application.
First question is, what is our data? Where do we store?
Minigram:
Our grams table will look like this in the database:
title | content | location |
---|---|---|
Model
http://guides.rubyonrails.org/active_record_basics.html
Active Record as an Object Relational Mapping (ORM) Framework.
Active Record gives us several mechanisms, the most important being the ability to:
- Represent models and their data
- Represent associations between these models
- Represent inheritance hierarchies through related models
- Validate models before they get persisted to the database
- Perform database operations in an object-oriented fashion
Why are Models important?
In building any web app, you will most probably have data and a need to store data.
So, you will definitely need to:
- Create the database
- Create the table
- Set up the Ruby class to interact with the database
Remember this as the first building block of a Rails web app!
$ bin/rails generate model create_gram
invoke active_record
create db/migrate/20160321030039_create_create_grams.rb
create app/models/create_gram.rb
First. what is db/migrate/<timestamp>_create_products.rb
?
That is a Rails Migration file (http://guides.rubyonrails.org/migrations.html).
- See how you can generate a new migration in the Docs.
Migration
Rails Migration codifies changes to database. Allow you to deploy the same changes to databases in multiple environments without having to type SQL.
This is one of the single most important unique selling point of Rails when it came out.
You can add columns to a migration with the syntax attr
:type
:index
And Rails will also created these attributes automagically:
id
created_at
updated_at
Open the migration file db/migrate/<timestamp>_create_create_grams.rb
:
class CreateCreateGrams < ActiveRecord::Migration
def change
create_table :create_grams do |t|
t.timestamps null: false
end
end
end
Add our columns:
class CreateCreateGrams < ActiveRecord::Migration
def change
create_table :create_grams do |t|
t.string :title
t.text :content
t.string :location
t.timestamps null: false
end
end
end
Create database first:
$> bin/rake db:create
Let’s go through what rake db:create
is..
rake db:create
creates the database as specified in config/database.yml
(postgres, gem ‘pg’)
This is required, before you can create any tables!
Then migrate the database:
$> bin/rake db:migrate
== 20160321030039 CreateCreateGrams: migrating ================================
-- create_table(:create_grams)
-> 0.0166s
== 20160321030039 CreateCreateGrams: migrated (0.0167s) =======================
Let’s go through what rake db:migrate
is..
rake db:migrate
executes all db/migration/<timestamp>.rb
files that have not been ran,
and updates the database accordingly.
To see the status of migrations in a database, do rake db:migrate:status
.
To undo the most recent change to the database, do rake db:rollback
.
This is destructive and can delete your data. Use with care.
Model file
# app/models/gram.rb
class Gram < ActiveRecord::Base
end
This is a class. A Gram class. This Gram class’s parent is the ActiveRecord::Base class.
So, it has whatever methods that its parent has (inheritance). The methods in the parent allows Gram class to interact with the grams
table, based on naming conventions.
rails console
How do we use this Gram class to interact with database?
$> rails console --sandbox
--sandbox
option: Rollback database modifications on exit.
>> Gram
>> Gram.connection
>> gram = Gram.new ..
>> gram.save
>> Gram.create ..
>> Gram.update ..
>> Gram.destroy
Creating a Gram from the console
- Gram.new
- gram.save
- Gram.create(...)
Updating a Gram from the console
- gram.update(...)
- getter / setters / save
- gram.title = nil
- gram.title = "Food"
- gram.save
- gram.title # => "Food"
Destroying a Gram from the console
- gram.destroy
Learn these methods from:
Query the database
>> Gram.all
>> Gram.first; Gram.last
>> Gram.count
>> Gram.find(1)
>> Gram.find_by(title: “Book”)
>> Gram.where(title: “Book”)
>> Gram.order(id: :desc)
>> Gram.limit(2)
Learn these methods from:
SQL versus ORM
Structured Query Language v.s. Object-relational Mapping
>> Gram.all
Gram Load (0.3ms) SELECT "grams".* FROM "grams"
>> Gram.first
Gram Load (0.7ms) SELECT "grams".* FROM "grams" ORDER BY "grams"."id" ASC LIMIT 1
>> Gram.find(1)
Gram Load (0.2ms) SELECT "grams".* FROM "grams" WHERE "grams"."id" = $1 LIMIT 1 [["id", 1]]
ORM
Gram.all
Gram.first
Gram.find(1)
SQL
SELECT "grams".* FROM "grams"
SELECT "grams".* FROM "grams" ORDER BY "grams"."id" ASC LIMIT 1
SELECT "grams".* FROM "grams" WHERE "grams"."id" = $1 LIMIT 1 [["id", 1]]
Validations
http://guides.rubyonrails.org/active_record_validations.html
- Database Agnostic
- Unavoidable
- Convenient to Test
- Convenient to Maintain
Pros and Cons of:
- Database constraints and/or Stored Procedures
- Client Side Validations
- Controller-level Validations
- No Validations?!
Trigger Validations
create create! --> save valid? save! invalid? update --> update!
The bang versions (e.g. save!) raise an exception if the record is invalid. The non-bang versions don't, save and update return false, create just returns the object.
validates_presence_of
validates_absence_of
validates_length_of
validates_format_of
validates_inclusion_of
validates_exclusion_of
validates_numericality_of
validates_acceptance_of
validates_associated
validates_confirmation_of
validates_each
validates_uniqueness_of
validates_with
These also come with different style:
http://api.rubyonrails.org/classes/ActiveModel/Validations/HelperMethods.html
Skip Validations
decrement!
decrement_counter
increment!
increment_counter
toggle!
touch
update_all
update_attribute
update_column
update_columns
update_counters
save(validate: false)
http://guides.rubyonrails.org/active_record_validations.html#skipping-validations
Go through:
https://github.com/jollygoodcode/property_expert/blob/lectures/validations/lectures/02-validation.md