Skip to content

Instantly share code, notes, and snippets.

@screamingmunch
Last active December 19, 2015 12:59
Show Gist options
  • Select an option

  • Save screamingmunch/5958979 to your computer and use it in GitHub Desktop.

Select an option

Save screamingmunch/5958979 to your computer and use it in GitHub Desktop.
Rails Models!! Fat models skinny controllers

##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.

  1. 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
  1. 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
  1. 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.

  1. Generate the painting table now that your painting db has been migrated
rails generate model Painting
  1. 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
  1. then run rake db:migrate again 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.

Relationships:

  1. MomaApp/ App/ models/ artist.rb:
class Artist < ActiveRecord::Base
   attr_accessible  :name, :nationality, :dob, :period, :image

   has_many :paintings
end
  1. MomaApp/ App/ models/ painting.rb:
class Painting < ActiveRecord::Base
  attr_accessible :title, :year, :medium, :style, :image, :artist_id

  belongs_to :artist
end

types of Relationships:

  • 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.

  • has_many through
  • 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
    

    seeding the database:

    1. 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)
    
    1. then run rake db:seed

    Rolling back your database

    if you ever get an error and need to reset your database( say you missed a field in your migration file, and need to redo your tables in the database):

    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.

    Active Records

    in terminal, type: `rails console` - essentially an irb for your rails app. We'll play in rails console to see how cool Active Record is..

    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

    Validations

    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>
    
    you can write your own custom validations
    ``` 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

    Sample resource route (maps HTTP verbs to controller actions automatically):

    resources :products

    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"
    
    

    These are our paintings:

    <%= @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
    
    
    
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment