New to Rails, please forgive me for a simple question but I'm stumped.
Rails 4.1.1 and Ruby 2.1.2
I am creating a small Rails app (Zombie Twitter) based on the videos I have been following from Code School.
I have four controllers: Application, Welcome, Zombie and Tweets.
I have three models: User, Zombie, Tweet.
User (from Devise), has_one :zombie
.
Zombie, belongs_to :user
and has_many :tweets
.
Tweet, belongs_to :zombie
.
A new zombie record is created when a user is registered. This works.
However, the zombie_id field in the tweets table is not populated when creating a new tweet. This I cannot figure out and is my problem.
Also: Can someone explain the build and build_model methods and when to use them? I get a method missing error when I call something along the lines of @zombie.tweets.build
. Is it necessary to call these methods in Rails 4.1.1 or is this a holdover from an earlier version of Rails?
User.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :zombie
accepts_nested_attributes_for :zombie, allow_destroy: true
end
Zombie.rb
class Zombie < ActiveRecord::Base
belongs_to :user
has_many :tweets
validates_uniqueness_of :zombie_name
end
Tweet.rb
class Tweet < ActiveRecord::Base
belongs_to :zombie
attr_accessor :zombie_id
accepts_nested_attributes_for :zombie
end
ApplicationController.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :set_zombie
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) << [zombie_attributes: :zombie_name]
end
def set_zombie
if user_signed_in?
@zombie = Zombie.includes(:tweets).find_by user_id: current_user.id
end
end
end
TweetController.rb
class TweetsController < ApplicationController
before_action :set_tweet, only: [:show, :edit, :update, :destroy]
# GET /tweets/new
def new
@tweet = current_user.zombie.tweets.new
end
# POST /tweets
# POST /tweets.json
def create
@tweet = current_user.zombie.tweets.new(tweet_params)
logger.debug params
respond_to do |format|
if @tweet.save
format.html { redirect_to :root, notice: 'Tweet was successfully created.' }
format.json { render :show, status: :created, location: @tweet }
else
format.html { render :new }
format.json { render json: @tweet.errors, status: :unprocessable_entity }
end
end
end
def set_tweet
@tweet = Tweet.find(params[:id])
end
def tweet_params
params.require(:tweet).permit(:status, :is_public)
end
end
/tweets/_form.html.rb
<%= form_for([@zombie, @tweet]) do |f| %>
<% if @tweet.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@tweet.errors.count, "error") %> prohibited this tweet from being saved:</h2>
<ul>
<% @tweet.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label 'Public' %>
<%= f.check_box :is_public, checked: true %>
</div>
<div class="field">
<%= f.label :status %><br>
<%= f.text_area :status %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
create_zombies_migration
class CreateZombies < ActiveRecord::Migration
def change
create_table :zombies do |t|
t.string :zombie_name
t.timestamps
end
add_reference :zombies, :user, index: true
add_index :zombies, [:user_id, :id]
add_index :zombies, :zombie_name, unique: true
end
end
create_tweets_migration
class CreateTweets < ActiveRecord::Migration
def change
create_table :tweets do |t|
t.text :status, :limit => 140
t.boolean :is_public, default: true
t.timestamps
end
add_reference :tweets, :zombie, index: true
add_index :tweets, [:zombie_id, :id], unique: true
end
end
Last, here is the record from development.log that details the POST request to create a tweet.
You need to remove attr_accessor :zombie_id
from tweet.rb
.
And add t.references :zombie
in create_tweets_migration
zombie_id
should be column in database, not virtual attribute.
PS. accepts_nested_attributes_for :zombie
also has no sense in tweet.rb
.