Set foreign key on new/create action in Rails - ruby-on-rails

Alright so I'm pretty new to this rails stuff so please bear with me...
I'm trying to make the most simple application ever, a Christmas list, and I need a little bit of help. Let me fill you in:
I scaffolded a person and an item. I modified my models a little bit and here is what they look like.
class Person < ActiveRecord::Base
has_many :item, :dependent => :destroy
end
class Item < ActiveRecord::Base
belongs_to :person
end
class CreateItems < ActiveRecord::Migration
def change
create_table :items do |t|
t.integer :person_id
t.string :description
t.timestamps
end
end
end
class CreatePeople < ActiveRecord::Migration
def change
create_table :people do |t|
t.string :name
t.timestamps
end
end
end
Seems like that's all cool. The index action on the people_controller lists all the people(duh)
<% #people.each do |person| %>
<tr>
<td><%= link_to person.name, "/people/#{person.id}" %></td>
</tr>
<% end %>
and when you click on one, it calls the show action(same controller) which gets all of the items for that person
def show
#person = Person.find(params[:id])
#items = Item.where(:person_id => params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #person }
end
end
and pulls the show view
<table>
<% #items.each do |item| %>
<tr>
<td><%= item.description %></td>
<td><%= link_to 'Edit', edit_item_path %></td>
<td><%= link_to 'Remove', "" %></td>
</tr>
<% end %>
</table>
<br/>
<%= link_to 'Add', :controller => :items, :action => :new, :id => #person.id %>
The link at the bottom is to add a new item for the person who's summary we are viewing. So then in the new action on the items_controller I have:
def new
#item = Item.new
#item.person_id = params[:id]
respond_to do |format|
format.html # new.html.erb
format.json { render json: #item }
end
end
Now I know this doesn't get saved until #item.save is called. I imagine this happens from the _form.html.erb submit button which in turn calls the create action in the controller?
def create
#item = Item.new(params[:item])
respond_to do |format|
if #item.save
format.html { redirect_to #item, notice: 'Item was successfully created.' }
format.json { render json: #item, status: :created, location: #item }
else
format.html { render action: "new" }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
I'm just a little confused why this is never getting set, it seems like it should be so easy(I'm sure it is haha). Also while I'm at it, you may have noticed I have no link for my 'Remove' link above. This is because I also couldn't figure out how to destroy action from that link to remove the correct item.
Like I said, this is all new to me. I appreciate any help! Please feel free to critique EVERYTHING I have done here. I don't have feelings :)

As Vibhu said, it's quite likely your issue stem from the fact that you should have has_many :items (note the plural) in your Person controller.
To add a hidden filed in your form specifying the person's id, add this in your creation form:
f.hidden_field :person_id

Related

Creating items with scaffolding not showing attributes after creation - Rails/Devise

I'm relatively new to Rails. I'm trying to create an application that can allow users to create video game items and store them under their own users. I'm using the latest version of Rails and Devise.
Using scaffolding as a base, I created the Videogame model/controller within my application. After linking the video game models to the user who created them, it seems that any attributes that are entered into the creation form are not saving, or at the very least just not showing up on the videogames/index page. After trying to search around on Google and StackOverflow, I couldn't find any similar questions/guides to work with.
Any ideas on how to fix this? Any help for a Rails newbie would be greatly appreciated.
Below I've posted all files that may be relevant. Please let me know if anything else is needed. To see the whole project, see http://github.com/bmmart2/collection-manager
Image after item creation
Index page of two created items
Here is my controller:
class VideogamesController < ApplicationController
before_action :set_videogame, only: [:show, :edit, :update, :destroy]
# GET /videogames
# GET /videogames.json
def index
if user_signed_in?
#videogame = current_user.videogames.all
else
redirect_to :root
end
end
# GET /videogames/1
# GET /videogames/1.json
def show
end
# GET /videogames/new
def new
#videogame = current_user.videogames.new
end
# GET /videogames/1/edit
def edit
end
# POST /videogames
# POST /videogames.json
def create
#videogame = current_user.videogames.create(videogame_params)
respond_to do |format|
if #videogame.save
format.html { redirect_to #videogame, notice: 'Videogame was successfully created.' }
format.json { render :show, status: :created, location: #videogame }
else
format.html { render :new }
format.json { render json: #videogame.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /videogames/1
# PATCH/PUT /videogames/1.json
def update
respond_to do |format|
if #videogame.update(videogame_params)
format.html { redirect_to #videogame, notice: 'Videogame was successfully updated.' }
format.json { render :show, status: :ok, location: #videogame }
else
format.html { render :edit }
format.json { render json: #videogame.errors, status: :unprocessable_entity }
end
end
end
# DELETE /videogames/1
# DELETE /videogames/1.json
def destroy
#videogame.destroy
respond_to do |format|
format.html { redirect_to videogames_url, notice: 'Videogame was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_videogame
#videogame = Videogame.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def videogame_params
params.require(:videogame).permit(:title, :publisher, :platform, :year, :condition, :upc)
end
end
Videogame model:
class Videogame < ApplicationRecord
belongs_to :user
attr_accessor :title, :platform, :upc, :condition, :publisher, :year
end
Videogame db migration file:
class CreateVideogames < ActiveRecord::Migration[5.2]
def change
create_table :videogames do |t|
t.string :title
t.string :publisher
t.integer :condition
t.string :platform
t.string :year
t.string :upc
t.timestamps
end
add_index :videogames, :user_id
end
end
add_user_refs_to_videogame migration:
class AddUserRefsToVideogame < ActiveRecord::Migration[5.2]
def change
add_reference :videogames, :user, foreign_key: true
end
end
Edit: show view for video game
<p id="notice"><%= notice %></p>
<p>
<strong>Title:</strong>
<%= #videogame.title %>
</p>
<p>
<strong>Publisher:</strong>
<%= #videogame.publisher %>
</p>
<p>
<strong>Platform:</strong>
<%= #videogame.platform %>
</p>
<p>
<strong>Year:</strong>
<%= #videogame.year %>
</p>
<p>
<strong>Condition:</strong>
<%= #videogame.condition %>
</p>
<p>
<strong>Upc:</strong>
<%= #videogame.upc %>
</p>
<%= link_to 'Edit', edit_videogame_path(#videogame) %> |
<%= link_to 'Back', videogames_path %>
I believe the attr_accessor line in your videogame.rb file is causing the problem. Try deleting it and see if that fixes the problem.

List all models belonging to another model in rails

In the show view for the 'service' model I want to be able to show all of the 'reviews' that are associated with this service. Need to know what to put in the 'service' show view as well as the 'service'/'review' controller.
Service model:
class Service < ActiveRecord::Base
has_many :reviews
end
Review model:
class Review < ActiveRecord::Base
has_one :service
belongs_to :service
end
Service show view:
<table>
<thead>
<tr>
<th>Name</th>
<th>Date</th>
<th>Review</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% #reviews.each do |review| %>
<tr>
<td><%= review.name %></td>
<td><%= review.date %></td>
<td><%= review.review %></td>
<td><%= link_to 'Show', review %></td>
<td><%= link_to 'Edit', edit_review_path(review) %></td>
<td><%= link_to 'Destroy', review, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
Review schema:
create_table "reviews", force: true do |t|
t.string "name"
t.string "date"
t.text "review"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "service_id"
end
Review controller:
def index
#reviews = Review.all
respond_with(#reviews)
end
def show
respond_with(#review)
end
def new
#review = Review.new
respond_with(#review)
end
def edit
end
def create
#review = Review.new(review_params)
#review.save
respond_with(#review)
end
Service controller:
def new
#service = Service.new
end
# GET /services/1/edit
def edit
end
# POST /services
# POST /services.json
def create
#service = Service.new(service_params)
respond_to do |format|
if #service.save
format.html { redirect_to #service, notice: 'Service was successfully created.' }
format.json { render :show, status: :created, location: #service }
else
format.html { render :new }
format.json { render json: #service.errors, status: :unprocessable_entity }
end
end
end
If you want to show all of the reviews that are associated with the service in the service show page then, you need to tweak your service_controller show action like this
def show
#service = Service.find(params[:id])
#reviews = #service.reviews
end
I don't really get what exactly are you trying to accomplish. If you want to access the reviews for a certain service in the show view, you can do that by simply doing
#reviews = #service.reviews
That's if you have a #service object in your service controller.
If that's not something you needed please edit your question and provide a clearer explanation.

Rails 4, build / create multiple table entries in the controller from passed in params

I am new to rails and am trying to create multiple entries in a table through my Test and TestQuestions models without success. Ultimately I would like to select 50 questions based on their category. I am stuck at this step, trying to pass category_id parameters to update my test_questions table from the TestController / Test Model.
The commented out line in the controller below: "#test.test_questions.build(:question_id => 5).save" works to make one question number 5, but when I call #test.build_category_test!(category_params).save instead to call a method and pass an array,I get the error undefined method `build_category_test!' for #Test:0x000000047f0928
Models
class TestQuestions < ActiveRecord::Base
belongs_to :test
end
class Test < ActiveRecord::Base
has_many :test_questions, class_name: "TestQuestions",
foreign_key: "test_id"
def self.build_category_test!(category_ids)
unless category_ids.blank?
category_ids.each do |u|
test_questions.build!(:question_id => 5)
end
end
end
end
Controller
class TestsController < ApplicationController
def create
#test = Test.new(test_params)
respond_to do |format|
if #test.save
##test.test_questions.build(:question_id => 5).save
#test.build_category_test!(category_params).save
format.html { redirect_to #test, notice: 'Test was successfully created.' }
format.json { render action: 'show', status: :created, location: #test }
else
format.html { render action: 'new' }
format.json { render json: #test.errors, status: :unprocessable_entity }
end
end
def category_params
params[:category][:category_ids]
end
end
View of test/new.html.erb
<%= form_tag tests_path, :method => :post do %>
<%= hidden_field_tag "user_id", #user_id %>
<ul>
<% for c in #categories %>
<li>
<%= check_box_tag "category[category_ids][]", c.id %>
<%= c.category %>
</li>
<% end %>
</ul>
<%= submit_tag "Create Test" %>
Log of parameters: "user_id"=>"1", "category"=>{"category_ids"=>["1", "2"]}, "commit"=>"Create Test"}
The method build_category_test! should be an instance method:
def build_category_test!(category_ids) # #test.build_category_test!
Instead of a class method:
def self.build_category_test!(category_ids) # Test.build_category_test!

Devise: current_user == nil?

My goal is to display the 'edit' and 'delete' buttons only to the user who created the listing. However, for some reason, current_user is returning nil. Any idea why this is happening?
Here is my user model:
class User < ActiveRecord::Base
has_many :listings
has_many :thoughts
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Here is my listing model:
class Listing < ActiveRecord::Base
belongs_to :user
has_many :thoughts
end
<% #listings.each do |listing| %>
<tr>
<td><%= listing.title %></td>
<td><%= listing.school %></td>
<td><%= listing.price %></td>
<td><%= listing.description %></td>
<% if current_user == listing.user %>
<td><%= link_to 'Show', listing %></td>
<td><%= link_to 'Edit', edit_listing_path(listing) %></td>
<td><%= link_to 'Delete', listing, method: :delete, data: { confirm: 'Are you sure?' } %></td>
<% else %>
<td><%= link_to 'Show', listing %></td>
<% end %>
</tr>
<% end %>
Here is the create action in the Listing controller
def create
#listing = Listing.new(listing_params)
respond_to do |format|
if #listing.save
format.html { redirect_to #listing, notice: 'Listing was successfully created.' }
format.json { render action: 'show', status: :created, location: #listing }
else
format.html { render action: 'new' }
format.json { render json: #listing.errors, status: :unprocessable_entity }
end
end
end
Here is my create_listings migration
class CreateListings < ActiveRecord::Migration
def change
create_table :listings do |t|
t.string :title
t.string :school
t.integer :price
t.text :description
t.timestamps
end
end
end
Make sure you have before_filter :authenticate_user! set in your controller:
class ListingsController < ActionController::base
before_filter :authenticate_user!
def index
#listings = Listing.all
end
end
As for your create method, so long as the table has a user_id column you just need to set the user for that listing:
def create
#listing = Listing.new(listing_params)
#listing.user = current_user
respond_to do |format|
if #listing.save
format.html { redirect_to #listing, notice: 'Listing was successfully created.' }
format.json { render action: 'show', status: :created, location: #listing }
else
format.html { render action: 'new' }
format.json { render json: #listing.errors, status: :unprocessable_entity }
end
end
end
Lastly, for the listing to belong to a user it needs to be able to record that users id. Make sure your table has a user_id column:
class CreateListings < ActiveRecord::Migration
def change
create_table :listings do |t|
t.string :title
t.string :school
t.integer :price
t.text :description
t.integer :user_id
t.timestamps
end
end
end
You can re-run this migrate if it is your latest by calling rake db:migrate:redo from the console.
If it isn't the latest you need to run the down and up specifically with VERSION=xxx to for that migrates ID (follow the steps here: 4.3 Running Specific Migrations). THIS WILL EMPTY THE TABLE.
If you need to keep date in that table then you need to write a new migrate with just the command add_column :listings, :user_id, :integer.
I'd do something likesigned_in? && current_user.id == listing.user_id

Placing Forums within Categories (Rails 4)

I am in the process of creating a forum using Ruby on Rails (I'm pretty new at this) and have managed to get myself utterly stuck.
**Version Ruby on Rails 4.0 **
A forum software can have many Categories, and within these Categories you can have multiple forums.
The main page would look similar to this:
Category 1
Forum 1
Forum 2
Category 2
Forum 3
Forum 4
Forum 5
When you create a forum, you should have a drop down menu that allows you to select which category you wish to place it in.
At first I created two different scaffolds- One for Categories and one for Forums. I used a foreign key to connect the two. I do not know if this is the best method, but I could not get them to interact at all. I ended up screwing up my code so badly I have very little to show for it.
I tried using Adding Sub-categories in Rails4 and categories and sub-categories model rails for solutions but both ended up causing errors.
Here is some of my code. It's not much, but maybe you can tell me where to even begin. If there is a better way of doing this (not using two tables), let me know. I would love to hear the best possible way of doing this without using gems
WARNING: my code is an utter mess.
Migration
class AddForeignToForums < ActiveRecord::Migration
def change
add_column :forums, :category_id, :integer
end
end
Forum Controller (I know I am missing something that will allow me to connect to the Category, I just don't know what)
class ForumsController < ApplicationController
before_action :set_forum, only: [:show, :edit, :update, :destroy]
# GET function. view/forums/index.html.erb
def index
#forums = Forum.all
end
# GET /forums/1. view/forums/show.html.erb
def show
#forum = Forum.find(params[:id])
end
# GET /forums/new. view/forums/new.html.erb
# Be able to list all the Categories.
def new
#forum = Forum.new
#categories = Category.all
end
# GET /forums/1/edit
# Be able to list all the categories.
def edit
#forum = Forum.find(params[:id])
#categories = Category.all
end
# POST /forums
# Allows the creation of a new forum
# Lindsey note: how to save category_idea. Assign to category.
def create
#forum = Forum.new(forum_params)
respond_to do |format|
if #forum.save
#forum = Forum.new(:name => params[:forum][:name],
:category_id => params[:forum][:category_id])
format.html { redirect_to #forum, notice: 'Forum was successfully created.' }
format.json { render action: 'show', status: :created, location: #forum }
else
format.html { render action: 'new' }
format.json { render json: #forum.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /forums/1
# Allows the update of forums.
def update
respond_to do |format|
if #forum.update(forum_params)
format.html { redirect_to #forum, notice: 'Forum was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #forum.errors, status: :unprocessable_entity }
end
end
end
# DELETE /forums/1
def destroy
#forum.destroy
respond_to do |format|
format.html { redirect_to forums_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_forum
#forum = Forum.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def forum_params
params.require(:forum).permit(:name, :description, :category_id)
end
end
Forum Model
class Forum < ActiveRecord::Base
belongs_to :category
end
Category Model
class Category < ActiveRecord::Base
has_many :forums, :dependent => :destroy,
end
Category Index.html.erb
<tbody>
<% #categories.each do |category| %>
<tr>
<td><%= link_to category.name, category %></td>
<td><%= link_to ' (Edit', edit_category_path(category) %></td>
<td><%= link_to '| Destroy)', category, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% category.forums.each do |forum| %>
<tr>
<td><li><%= link_to forum.name, forum %></li></td>
</tr>
<% end %>
<% end %>
</tbody>
Forum _form.html.erb
<%= form_for(#forum) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<%= f.label :category_id %><br />
<%= f.select :category_id, Category.all.map{|c| [c.name, c.id]} %>
<div class="actions">
<%= f.submit %>
</div>
You probably want a table for forums, a table for categories, and a join table that includes a forum_id and category_id - name this forum_categories
class Forum < ActiveRecord::Base
has_many :forum_categories
has_many :categories, :through => :forum_categories
end
And, with categories, you'll do the reverse
class Categories < ActiveRecord::Base
has_many :forum_categories
has_many :forums, :through => :forum_categories
end
For adding categories in the view, you can use checkboxes or a multiple select box. The name of this input will be either
f.check_box 'category_ids[]'
or
f.select 'category_ids[]'
This will submit a param in an array format that will allow you to update the forum.category_ids with a simple
forum.create(params[:forum])
In your view, instead of #forums, you'll list category.forums
<% category.forums.each do |forum| %>
<%= forum.name %>
<% end %>
Hopefully this will get you started.
EDIT
For a single category on Forum, you've done well. Just a few smaller changes:
class Category < ActiveRecord::Base
# belongs_to :category - this can be removed
has_many :forums # Do you want to delete the forums if the category is removed? You don't need the classname option.
end
In the drop down - you'll do something like this...
f.select :category_id, Category.all.map{|c| [c.name, c.id]}

Resources