##MOMA App - first rails model app
ARTISTS - name, DoB, nationality, bio
PAINTINGS - style, title, date, artist_id
no id for artists because an artist can have multiple paintints. (ARTISTS has_many PAINTINGS, but PAINTING belongs to an ARTIST). Thus you don't need an id for the artists.
- create the new rails app, then generate the Artist Model (in terminal)
rails new MomaApp
pushd MomaApp
rails generate model Artist
# convention to have model singular, name of model should be singular and capitalized.
subl . & #to open sublime text- setup your migration file in db/ migrate/ (sublime text)
class CreateArtists < ActiveRecord::Migration
def change
create_table :artists do |t|
t.string :name
t.string :nationality
t.date :dob
t.string :period
t.text :image
t.timestamps
end
end
end
- create your database: (terminal)
rake db:create
#creates our database, first time you run this,
#it creates the schema (bundle execute vs db:create)
rake db:migrate
#creates migration files
#(creates tables in your database that you've created with db:create)
Everytime you create a new migration file, you'll have to run rake db:migrate (all the fields in your db/migration files that we modified, or added, you'll need to re-run db:migrate).
rails generate model or rails generate migration creates a new migration file.
- Generate the painting table now that your painting db has been migrated
rails generate model Painting- modify your Painting migration file: (add columns)
class CreatePaintings < ActiveRecord::Migration
def change
create_table :paintings do |t|
t.string :title
t.string :year
t.string :medium
t.string :style
t.text :image
t.integer :artist_id
t.timestamps
end
end
end
- then run
rake db:migrateagain to create the new Painting table in your database.
Models: we have artist.rb and painting.rb (MomaApp/ app / models/)
attr_accessible similar to attr_accessor for ruby.
**once you've run rake:db migrate, you can't modify the migration file anymore ('cuz the table is now created in your db), unless you go run 'rake db:rollback` to go back to the previous db and modify it). You don't want to drop tables if it's live db.
See below for workflow to actually drop a db.
- MomaApp/ App/ models/ artist.rb:
class Artist < ActiveRecord::Base
attr_accessible :name, :nationality, :dob, :period, :image
has_many :paintings
end
- MomaApp/ App/ models/ painting.rb:
class Painting < ActiveRecord::Base
attr_accessible :title, :year, :medium, :style, :image, :artist_id
belongs_to :artist
end
- has_many
- belongs_to
- has_one
If the relationship goes both ways (as in, an artist has many paintings and paintings have many artists), then you'll need to create a join table. Rails create that for you automatically.
rails g model Collection name:string if you want to create a collection table. (we will omit this for our first round).
example: http://guides.rubyonrails.org/association_basics.html
class Physician < ActiveRecord::Base
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ActiveRecord::Base
belongs_to :physician
belongs_to :patient
end
class Patient < ActiveRecord::Base
has_many :appointments
has_many :physicians, through: :appointments
end
- MomaApp/ db/ seeds.rb
first delete everything in it.. this is not what you would normally do, it's only when you're creating some fake data to play around with in development mode.
Artist.delete_all
Painting.delete_all
Artist.create(:name => 'Vincent van Gogh', :nationality => 'Dutch', :dob => '3/2/1795', :period => 'Renaissance', :image => 'http://cdn.theatlantic.com/static/mt/assets/science/vangogh_photo.jpg')
Artist.create(:name => 'Pablo Picasso', :nationality => 'Spain', :dob => '5/3/1686', :period => 'Impressionistic', :image => 'http://cdn.enjoyourholiday.com/wp-content/uploads/2012/10/Picasso-portrait.jpg')
Artist.create(:name => 'Salvador Dali', :nationality => 'Spain', :dob => '4/9/1583', :period => 'Modern', :image => 'http://www.biography.com/imported/images/Biography/Images/Profiles/D/Salvador-Dali-40389-2-402.jpg')
Artist.create(:name => 'Claude Monet', :nationality => 'French', :dob => '3/9/1275', :period => 'Post Modern', :image => 'http://www.biography.com/imported/images/Biography/Images/Profiles/M/Claude-Monet-WC-9411771-2-402.jpg')
Artist.create(:name => 'Henri Matisse', :nationality => 'French', :dob => '8/3/1376', :period => 'Realists', :image => 'http://upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Portrait_of_Henri_Matisse_1933_May_20.jpg/220px-Portrait_of_Henri_Matisse_1933_May_20.jpg')
Painting.create(:title => 'Self Portrait', :year => '1245', :medium => 'oil', :style => 'awesome', :image => 'http://www.artcyclopedia.org/art/van-gogh-self.jpg', :artist_id => 1)
Painting.create(:title => 'A Woman', :year => '1656', :medium => 'canvas', :style => 'cool', :image => 'http://www.arttherapyblog.com/uimages/2010/10/picasso_woman_b.jpg', :artist_id => 2)
Painting.create(:title => 'Butterfly Ship', :year => '1837', :medium => 'water colors', :style => 'quick', :image => 'http://www.idealnetworker.com/home/idealnet/public.html/wp-content/uploads/2011/04/art-salvador-dali.jpg', :artist_id => 3)
Painting.create(:title => 'Twilight', :year => '1488', :medium => 'fabric', :style => 'modern', :image => 'http://framingpainting.com/UploadPic/Claude%20Monet/big/Venice%20Twilight.jpg', :artist_id => 4)
Painting.create(:title => 'Lydia', :year => '1734', :medium => 'oil', :style => 'other', :image => 'http://www.arthermitage.org/Henri-Matisse/Portrait-of-Lydia-Delectorskaya.jpg', :artist_id => 5)
- then run
rake db:seed
rake db:drop drops (clears, delete, ALL GONE, BLANK) all the database previously created.
rake db:create && rake db:migrate && rake db:seed you can daisy chain your commands.
**One way to check if your rake db:create is working.. you should see a "development.sqlite3" file in your db/ migrate folder.
in SQL, you need to type SELECT "artists".* FROM "artists" to get all the artists in the database. for rails, in the rails console, type:
irb(main):001:0> Artist.all # gives u all the artists
irb(main):002:0> Painting.all # gives us all the paintings
irb(main):003:0> a = Artist.first # returns Artist id:1
irb(main):004:0> a.paintings # returns all the paintings linking to Artist with id:1
irb(main):005:0> Artist.last
irb(main):006:0> Artist.where(:name => "Pablo Picasso")
# returns an array with name matching "Pablo Picasso"
irb(main):007:0> Artist.where(:name => "Pablo Picasso").first.paintings
#array of paintings of "Picasso"
irb(main):008:0> Artist.find(1)
irb(main):009:0> Artist.find_by_name("Pablo Picasso")
irb(main):010:0> p = Painting.last
irb(main):011:0> p.delete
irb(main):012:0> p1 = Painting.new
#temporarily creates a new painting object,
#but it's not saved into the database until you save it
irb(main):013:0> p1.title = "Painting"
irb(main):014:0> p1.save
#now an id is assigned and saved to the db
irb(main):015:0> p2 = Painting.new(title:"this painting", year: 2013, medium: "paint")
irb(main):016:0> p2.save
irb(main):017:0> p3 = Painting.create(title: "stuff", year: 2014)
#this creates and saves to the db a new painting simultaneously
irb(main):018:0> Time.now
irb(main):019:0>
irb(main):020:0> j = Artist.create(name: "Jackie")
irb(main):021:0> j.build_painting(:title => "awesome") #for has one relationships
irb(main):022:0> j.paintings.build(title: "awesome") #for has_many relationships
#build is similar to new but it automatically assigns the artist id
.where is similar to .find
we put our validations in the top of our model.validates_presence_of and validates_uniqueness_of (from rails)
in your MomaApp/ app/ models/ painting.rb file:
class Painting < ActiveRecord::Base
attr_accessible :title, :year, :medium, :style, :image, :artist_id
belongs_to :artist
validates_presence_of :year, :artist #<== add validations
validates_uniqueness_of :title
#when you validate :artist, it automatically validates :artist_id as well
end
now if you go back to rails c
irb(main):001:0> p = Painting.create(title: "new painting")
(0.0ms) begin transaction
(0.0ms) rollback transaction
=> #<Painting id: nil, title: "new Painting", year: nil, medium: nil, style: nil, image: nil, artist_id: nil, created_at: nil, updated_at: nil>
irb(main):002:0> p.errors #show all your errors
(0.0ms) begin transaction
(0.0ms) rollback transaction
=> #<Painting id: nil, title: "new Painting", year: nil, medium: nil, style: nil, image: nil, artist_id: nil, created_at: nil, updated_at: nil>
irb(main):003:0> a = Artist.last
irb(main):004:0> a.paintings.build(:title => "awesome", "year => 2013).save
irb(main):005:0>
```
class Painting < ActiveRecord::Base
attr_accessible :title, :year, :medium, :style, :image, :artist_id
belongs_to :artist
validates_presence_of :year, :artist validates_uniqueness_of :title
validate :ian #if our validation returns false, it will return an error & won't let us save
def ian
if self.year.to_i > Time.now.year
errors.add(:year, "Year is in the future")
end
#self.year.to_i < Time.now.year
#self because we're going to be doing this on an instance of the painting
end
#when you validate :artist, it automatically validates :artist_id as well end
<h5> Controllers </h5>
create an 'artists_controller.rb' file in MomaApp/ app/ controllers:
class PaintingsController < ApplicationController
def new # for GET request (gets info)
@painting = Painting.new
# only the new.html.erb has access to new
# , so the .new request can have the same instance variable as show
end
def create # for POST request (submit forms) end
def index # GET, read a list of multiple objects @paintings = Painting.all end
def show #GET, read just one object @painting = Painting.find(params[:id]) # we ask the model to find Painting through its params and # assign it to an instance variable @painting # this instance variable is accessible to our view # so here controller communicates b/w model & view ############################ # only the show.html.erb file has access to show end
def edit #GET, form where you can edit end
def update #POST end
def destroy #deletes end
end
in rails, you generally want "RESTful" routes if you follow rails conventions.
Resources generates restful routes for you, so you won't have to do it manually. (more on this later)
RAILS CONTROLLERS--
CRUD
Create -- new + create
Read -- index + show
Update -- edit + update
Destroy -- destroy
in Config/routes.rb
MomaApp::Application.routes.draw do
resources :paintings
end
then run `rake routes`, which gives you a way to view all the routes created in routes.rb:
paintings GET /paintings(.:format) paintings#index
POST /paintings(.:format) paintings#create
new_painting GET /paintings/new(.:format) paintings#new edit_painting GET /paintings/:id/edit(.:format) paintings#edit painting GET /paintings/:id(.:format) paintings#show PUT /paintings/:id(.:format) paintings#update DELETE /paintings/:id(.:format) paintings#destroy
<h3> Creating our first View page </h3>
a) Create a "paintings" folder in MomaApp / Views / Layouts
b) Create a file in this folder called "index.html.erb"
<%= @paintings %>
type `rm -rf public/index.html` to remove default rails index page
type `rails s` to run your server.. then go to localhost:3000/paintings to see your page
c) Create a second view file: "new.html.erb", which will include a form to input new paintings.
h1> Add a new painting
<%= form_for @painting do |f| %>
<%= f.label :artist_id %>
<%= f.select :artist_id, Artist.all.map{|artist| [artist.name, artist.id]}, :include_blank => true, :prompt => "Pick Artist!" %>
<%= f.text_field :title %>
<%= f.label :title %>
<%= f.text_field :year %>
<%= f.label :year %>
<%= f.text_field :medium %>
<%= f.label :medium %>
<%= f.text_field :style %>
<%= f.label :style %>
<%= f.text_field :image %>
<%= f.label :image %>
<%= f.submit %>
<% end %>
<hr>
<h3> Rails Model Workflow recap: <h3>
1. rails generate model Name < same as `rails g model Name`> (creates migration file)
2. add columns /types/ fields in your migration files
3. rake db:create
4. rake db:migrate
5. write seed.rb file, use Active Record methods: rake db:seed
6. If you screw up, rake db:drop
<hr>
sqlite3 is the default database for rails.. normally used for development. we'll update this for production mode.
**For better highlighting when you're working on rails, hit cmd + shift + p while on sublime text
Type out Rails
choose Set Syntax: Ruby on Rails
**`Cmd Shift F`(mac) or `Ctrl Shift F`(linux) in sublime text to search for something in multiple files within your app.
<h4> render vs. redirect_to </h4>
Each request from the browser creates a new instance of your controller.
https://gist.github.com/jcasimir/1210155
<hr>
Homework: https://gist.github.com/jherrlin/630e925ec7ac15c4e796
List has_many Items
Today's notes: https://github.com/jherrlin/ClassNotes/blob/master/wk4d2/models.md