Ruby on rails relationships - ruby-on-rails

I am extremely new to ruby and programming in general. In the copy, paste, and pray stage as I like to call it. I am trying to restrict access of editing posts and comments to the creator but when i create a post the user_id isn't populating in the database.
thanks in advance for the help.
routes
map.resources :user_sessions
map.resources :users
map.resources :questions, :has_one => :user, :has_many => :answers
map.login "login", :controller => "user_sessions", :action => "new"
map.logout "logout", :controller => "user_sessions", :action => "destroy"
the user model
class User < ActiveRecord::Base
acts_as_authentic
has_many :questions
has_many :answers
end
the question model
class Question < ActiveRecord::Base
validates_presence_of :question, :tag
validates_length_of :question, :minimum => 5
validates_length_of :tag, :minimum =>4
belongs_to :user
has_many :answers
end
the answer model
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :user
end
enter code here
the question controller
class QuestionsController < ApplicationController
before_filter :find_question,
:only => [:show, :edit, :update, :destroy]
# GET /questions
# GET /questions.xml
def index
#questions = Question.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #questions }
end
end
# GET /questions/1
# GET /questions/1.xml
def show
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #question }
end
end
# GET /questions/new
# GET /questions/new.xml
def new
##question = Question.new
#user = Question.new
end
# GET /questions/1/edit
def edit
end
# POST /questions
# POST /questions.xml
def create
#question = Question.new(params[:question])
##question = Question.user.new(params[:question])
if #question.save
flash[:notice] = 'Question was successfully created.'
redirect_to(#question)
else
render :action => "new"
end
end
end
# PUT /questions/1
# PUT /questions/1.xml
def update
if #question.update_attributes(params[:question])
flash[:notice] = 'Question was successfully updated.'
redirect_to(#question)
else
render :action => "edit"
end
end
# DELETE /questions/1
# DELETE /questions/1.xml
def destroy
#question.destroy
redirect_to(questions_url)
end
private
def find_question
#question = Question.find(params[:id])
end
answer controller
class AnswersController < ApplicationController
def index
#question = Question.find(params[:question_id])
#answer = #question.answers
end
def show
#question = Question.find(params[:question_id])
#answer = #question.answers.find(params[:id])
end
def new
#question = Question.find(params[:question_id])
##question = Question
#answer = #question.answers.build
##answer = Answer.new
#redirect_to questions_url(#answer.question_id)
end
def create
##question = Question.find(params[:question_id])
# #question = Question
#answer = Answer.new(params[:answer])
if #answer.save
redirect_to question_url(#answer.question_id)
else
render :action => "new"
end
end
def edit
#question = Question.find(params[:question_id])
#answer = #question.answers.find(params[:id])
end
def update
#question = Question.find(params[:question_id])
#answer = Answer.find(params[:id])
if #answer.update_attributes(params[:answer])
redirect_to question_answer_url(#question, #answer)
else
render :action => "edit"
end
end
def destroy
#question = Question.find(params[:question_id])
#answer = Answer.find(params[:id])
#answer.destroy
respond_to do |format|
format.html {redirect_to #question}
format.xml {head :ok}
end
end
end

You need to scope your model to the related object for ActiveRecord to populate foreign keys. This is easiest using a helper method. If you wanted to scope to the user:
Excerpted from one of my apps using Authlogic:
class ApplicationController < ActionController::Base
helper_method :current_user_session, :current_user
protected
def current_user_session
#current_user_session ||= UserSession.find
end
def current_user
#current_user ||= current_user_session && current_user_session.user
end
end
Then you can scope e.g. current_user.answers.build, or current_user.answers.find(params[:id]
As answers belong to users and questions. You're going to have to set the scope to whichever object makes the most sense. Assuming you decided it's the user object, you'll have to set the question_id yourself Add #answer.question = #question to your controller action. Don't get into setting foreign keys manually e.g. #answer.question_id = #question.id when ActiveRecord will happily do it for you.

Do you have a current_user that is authenticated? If not, you need one. I haven't used AuthLogic but there should be some good tutorials on how to do that.
Assuming you do have a current_user, the easiest solution would be to do something like:
def create
#answer = Answer.new(params[:answer])
#answer.user_id = current_user.id <--- add this line
if #answer.save
redirect_to question_url(#answer.question_id)
else
render :action => "new"
end
end

Related

How to get a timeline of posts of only people someone follows

I'm trying to do something like Instagram where you can see images of people you only follow. A user can follow another user and create new posts.
This is what i have for following and unfollowing in my Users controller.
def following
#user = User.find(params[:id])
current_user.mark_as_following #user
respond_to do |format|
format.html {redirect_to #user}
format.js
end
end
def unfollow
#user = User.find(params[:id])
#user.unmark :following, :by => current_user
respond_to do |format|
format.html {redirect_to #user}
format.js
end
end
Here is my my Posts controller
class PostsController < ApplicationController
load_and_authorize_resource
def show
#post = Post.find(params[:id])
end
def new
#post = Post.new
end
def create
#post.user_id = current_user.id
if #post.save
redirect_to #post
else
render :new
end
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update_attributes(update_params)
redirect_to #post
else
render :edit
end
end
private
def update_params
params.require(:post).permit(:caption, :image)
end
def create_params
params.require(:post).permit(:caption, :user_id, :image)
end
end
you can create a controller like this
class FollowingPostsController < ApplicationController
def index
#posts = current_user.following_posts
end
end
and in your User model
class User < ActiveRecord::Base
def following_posts
#assuming that following_users returns the list of following users
self.following_users.map{ |user| user.posts }.flatten(1)
end
end
Or you can get the list of posts with :
Post.where(user_id: self.following_users.ids)

Nested Comments with nested answers: undefined method `answers' for nil:NilClass | Rails

I am trying to implement nested answers into comments, which are nested into auctions.
There is a auctions.rb model, which:
has_many :comments, dependent: :destroy
has_many :answers, :through => :comments
a comments.rb model, which:
belongs_to :auction
has_many :answers, dependent: :destroy
a answers.rb model, which:
belongs_to :comment
the answers_controller inherits from the comments_controller:
class AnswersController < CommentsController
before_action :all_answers, only: [:index, :create, :update, :destroy]
before_action :set_answer, only: [:edit, :update, :destroy]
respond_to :html, :js
# New Answer (Form)
def new
#answer = Answer.new
#comments.answers.build
end
# Create Answer
def create
#answer = #comment.answers.build(answer_params)
#answer.user_id = current_user.id
#answer.save
end
# Edit Answer
def update
#answer.update!(answer_params)
end
# Delete Answer
def destroy
#answer = Comment.find(params[:id])
#comment = #answer.comment
#answer.destroy
end
private
def all_answers
#answers = #comment.answers.all
end
def set_answer
#answer = #comment.answers.find(params[:id])
end
def answer_params
params.require(:comment).permit(:body)
end
end
The Error:
NoMethodError in Auctions#show app/views/comments/_comment.html.erb
where line #20 raised: undefined method `answers' for nil:NilClass
<div class="col s12" id="answer-form" style="display:none;"></div>
</div>
<div class="row">
<div class="col s12" id="answers"><%= render #comment.answers %></div>
</div>
With <%= render #comment.answers %> I want to display all existing answers below the related comment. What am I doing wrong?
auction_controller
class AuctionsController < ApplicationController
# Index of all auctions
def index
#auctions = Auction.all
end
# Show Auction by :id
def show
#auction = Auction.find(params[:id])
# Find Seller by ID
#seller = User.find(#auction.user_id)
# Find highest bid, by finding all related bids and ordering in descending and picking the first
#highest_bid = Bid.where(auction_id: params[:id]).order("amount DESC").first
# Find product
#product = Product.find(#auction.product_id)
end
# New Auction Form
def new
#auction = Auction.new
end
# Edit Auction
def edit
#auction = Auction.find(params[:id])
end
# Create new Auction
def create
# Create new Auction
#auction = Auction.new(auction_params)
# Save Id of User (Seller)
#auction.user_id = current_user.id
# If auction was created successfully
if #auction.save
# display the created auction
redirect_to #auction, :notice => "Auction created"
else
# display Form again if unsuccessful
render 'new'
end
end
# Update existing Auction
def update
#auction = Auction.find(params[:id])
# Validation
if #auction.update(auction_params)
redirect_to #auction, :notice => "Auction updated"
else
render 'edit'
end
end
# Delete Auction
def destroy
#auction = Auction.find(params[:id])
#auction.destroy
redirect_to auctions_path, :notice => "Auction deleted"
end
private
# set required parameters for new created Auctions
def auction_params
params.require(:auction).permit(:condition, :product_name)
end
end
comments_controller
class CommentsController < ApplicationController
before_action :set_auction
before_action :all_comments, only: [:index, :create, :update, :destroy]
before_action :set_comment, only: [:edit, :update, :destroy]
respond_to :html, :js
# New Comment (Form)
def new
#comment = Comment.new
#auction.comments.build
end
# Create Comment
def create
#comment = #auction.comments.build(comment_params)
#comment.user_id = current_user.id
#comment.save
end
# Edit Comment
def update
#comment.update!(comment_params)
end
# Delete Comment
def destroy
#comment = Comment.find(params[:id])
#auction = #comment.auction
#comment.destroy
end
private
def set_auction
#auction = Auction.find(params[:auction_id])
end
def all_comments
#comments = #auction.comments.all
end
def set_comment
#comment = #auction.comments.find(params[:id])
end
def comment_params
params.require(:comment).permit(:body)
end
end
Normal Comments work. Only Comment Answers don't work.
The error happens in Auctions#show, the error clearly tells you that you are trying to call answers on a nil object. Therefore, it means #comment is nil in that view.
In fact, if you check the show action, you never fetch/assign any object to #comment.
# Show Auction by :id
def show
#auction = Auction.find(params[:id])
# Find Seller by ID
#seller = User.find(#auction.user_id)
# Find highest bid, by finding all related bids and ordering in descending and picking the first
#highest_bid = Bid.where(auction_id: params[:id]).order("amount DESC").first
# Find product
#product = Product.find(#auction.product_id)
end
In order to fix it, make sure #comment is properly assigned to a Comment instance.
There's another problem here:
def new
#answer = Answer.new
#comments.answers.build
end
You haven't got a variable called #comments, hence your form can't actually build answers off it. In fact, you're calling #comment in two other methods, where I can't even see it being declared:
def all_answers
#answers = #comment.answers.all
end
def set_answer
#answer = #comment.answers.find(params[:id])
end
The only time you've declared #commentis in the destroymethod:
def destroy
#answer = Comment.find(params[:id])
#comment = #answer.comment
#answer.destroy
end
Even then, it's weird.
Why are you calling the Comment model with the #answer variable? Surely you'd have an Answer model with comments attached by way of a has_many relationship?
I'd recommend you keeping it brain-dead simple:
#app/models/answer.rb
class Answer < ActiveRecord::Base
has_many :comments
end
#app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :answer
end
This means when you call your actions controller, you'll be able to apply the following:
def show
#answer = Answer.find params[:id]
#comment = #answer.comments.build
end
If you wanted to have comments act as answers, you need to keep the models separate. Use a hierchy gem, like closure tree. This way, you'll be able to keep your Answers/Comments in hierarchy order, whilst keeping the Models consistent.

Cannot find user without id?

i'm getting this error for my products and user table.
--Couldn't find user without an id
def set_user
#user = User.find(params[:user_id])
end
I have nested the routes like so..
resources :users do
resources :products do
resources :reviews
end
end
and here is my products controller..
class ProductsController < ApplicationController
before_action :require_signin, except: [:index, :show]
before_action :set_user
def index
#products = #user.products
end
def show
#product = Product.find(params[:id])
end
def edit
#product = Product.find(params[:id])
end
def update
#product = Product.find(params[:id])
if #product.update(product_params)
redirect_to [#user, #product], notice: "Product successfully updated!"
else
render :edit
end
end
def new
#product = #user.products.new
end
def create
#product = #user.products.new(product_params)
#product.user = current_user
if #product.save
redirect_to user_products_path(#product, #user), notice: "Product successfully created!"
else
render :new
end
end
def destroy
#product = Product.find(params[:id])
#product.destroy
redirect_to user_products_path(#product, #user), alert: "Product successfully deleted!"
end
private
def product_params
params.require(:product).permit(:title, :description, :posted_on, :price, :location, :category)
end
def set_user
#user = User.find(params[:user_id])
end
end
All i am trying to do is associate the user and product so the product belongs_to user, and the user has_many products.
class Product < ActiveRecord::Base
belongs_to :user
has_many :reviews
class User < ActiveRecord::Base
has_secure_password
has_many :reviews, dependent: :destroy
has_many :products, dependent: :destroy
As other users have mentioned the params[:user_id] value is probably nil.
That said, you already appear to have a current_user defined in the scope of the controller. I see it referenced in the create action. I'd bet that it was set by the require_sign_in before_action. Given what I think you are trying to do, it probably makes your set_user before_action a bit redundant.
You can probably just refer to current_user in your controller anywhere you are currently using #user. Alternatively, you might set #user = current_user in the set_user before_action.
SideNote:
Looking a bit closer at your create action:
def create
#product = #user.products.new(product_params)
#product.user = current_user
if #product.save
redirect_to user_products_path(#product, #user), notice: "Product successfully created!"
else
render :new
end
end
Correct me if I'm wrong but I believe doing something like #model.association.new sets the model_id for the newly created association object so I would change the two lines
#product = #user.products.new(product_params)
#product.user = current_user
to simply be
#product = current_user.products.new(product_params)
For any action of your controller you should pass user_id param.
The reason of error is params[:user_id] equal nil

Trying to add lists to my collections (form_for, routing)

Hey guys I am trying to be able to add lists to my collections. I am new to rails any help would be appreciated. Currently I am trying to build a form to make a new list, but the new/create actions seem to be messed up.
The lists will end up living in the collections show view later via ajax.
Ultimately the goal is for each user to own multiple collections and in each collection there will be multiple lists, in each lists multiple items.
Collections
class CollectionsController < ApplicationController
def index
#user = User.find(current_user)
#collection = Collection.where(:user_id => current_user.id)
end
def new
#collection = Collection.new
end
def show
#collection = Collection.find(params[:id])
#list = List.all
end
def create
#collection = Collection.new(collection_params)
#collection.user_id = current_user.id
# render :text => CGI.escapeHTML(#collection.inspect)
if #collection.save
redirect_to root_path(#user)
else
render 'new'
end
end
def edit
#collection = Collection.find(params[:id])
end
def update
if #collection.update(collection_params)
redirect_to root_path(#user)
else
render 'edit'
end
end
def destroy
#collection.destroy
redirect_to root_path(#user)
end
private
def collection_params
params.require(:collection).permit(:alias, :notes, :visibility)
end
def find_collection
#collection = #user.collection.find(params[:id])
end
end
Lists
class ListsController < ApplicationController
def index
#list = List.all
end
def new
#list = List.new
end
def create
#collection = Collection.find(params[:collection_id])
#list =
#collection.lists.create(comments_params)
if #collection.lists.save
redirect_to root_path(#user)
else
render 'new'
end
end
end
Users
class UsersController < ApplicationController
def index
#user = User.find(current_user)
#collection = Collection.where(:user_id => current_user.id)
#user.collection = Collection.where(:user_id => current_user.id)
# render :text => CGI.escapeHTML(#collection.inspect)
end
end
The link I was trying
<%= link_to '<i class="fa fa-plus-square"></i> Add Subcategory'.html_safe, new_collection_list_path(#collection.id) %>
Current routes
devise_scope :user do
authenticated :user do
root 'collections#index', as: :authenticated
resources :collections do
resources :lists
end
end
Failed form_for
<%= form_for([#collection, #collection.lists.build]) do |f| %>
<% end %>
Models
Users has_many :collections
Collections belong_to :user
has_many :lists
Lists belong_to :collection
Change your ListsController new and create actions to the following:
def new
#collection = Collection.find(params[:collection_id])
end
def create
#collection = Collection.find(params[:collection_id])
#list = #collection.lists.build(params[:list])
if #list.save
redirect_to root_path(#user)
else
render 'new'
end
end

Rails Has_Many :Through confusion

I'm working on creating a basic survey app as I'm learning rails. I've setup a has_many through relationship between the surveys as the questions (as questions may be used in multiple surveys). I've been struggling with adding a question to a survey though. Any idea what I need to do to create a new surveytization when creating my new question (and thus adding the question to the survey)? I'm able to do it in the console but am struggling with translating that to the controllers/views/params - if you know of any good documentation about those I'd love to check them out to (but thus far haven't found much).
It seems to error out when I try to assign my #survey variable using the :survey_id in the params I'm sending to the Question controller.
I really appreciate your help!
Question.rb:
class Question < ActiveRecord::Base
has_many :answers, dependent: :delete_all
validates :title, presence: true
has_many :surveytizations
has_many :surveys, :through => :surveytizations
end
Survey.rb
class Survey < ActiveRecord::Base
has_many :surveytizations
has_many :questions, :through => :surveytizations
end
Surveytization.rb:
class Surveytization < ActiveRecord::Base
has_many :surveys
has_many :questions
validates :survey_id, presence: true
validates :question_id, presence:true
end
SurveyController.rb:
class SurveysController < ApplicationController
before_action :set_survey, only: [:show, :edit, :update, :destroy]
before_action :set_question
# GET /surveys
# GET /surveys.json
def index
#surveys = Survey.all
end
# GET /surveys/1
# GET /surveys/1.json
def show
end
# GET /surveys/new
def new
#survey = Survey.new
end
# GET /surveys/1/edit
def edit
end
# POST /surveys
# POST /surveys.json
def create
#survey = Survey.new(survey_params)
respond_to do |format|
if #survey.save
format.html { redirect_to #survey, notice: 'Survey was successfully created.' }
format.json { render action: 'show', status: :created, location: #survey }
else
format.html { render action: 'new' }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /surveys/1
# PATCH/PUT /surveys/1.json
def update
respond_to do |format|
if #survey.update(survey_params)
format.html { redirect_to #survey, notice: 'Survey was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
# DELETE /surveys/1
# DELETE /surveys/1.json
def destroy
#survey.destroy
respond_to do |format|
format.html { redirect_to surveys_url }
format.json { head :no_content }
end
end
def add_question(question)
surveytizations.create!(question_id: question.id)
end
def remove_question(question)
surveytizations.find_by(question_id: question.id).destroy
end
def find_question(question)
#question = surveytizations.find_by(question_id: question.id)
end
private
# Use callbacks to share common setup or constraints between actions.
def set_survey
#survey = Survey.find(params[:id])
end
def set_question
#question = Question.new
end
# Never trust parameters from the scary internet, only allow the white list through.
def survey_params
params.require(:survey).permit(:title)
end
end
Survey show.html.erb:
<p id="notice"><%= notice %></p>
<p>
<strong>Title:</strong>
<%= #survey.title %>
</p>
<%= link_to 'Edit', edit_survey_path(#survey) %> |
<%= link_to 'Back', surveys_path %>
<%= link_to "Add Question", new_question_path(:survey_id => #survey.id)%>
QuestionController:
class QuestionsController < ApplicationController
before_action :set_question, only: [:show, :edit, :update, :destroy]
# GET /questions
# GET /questions.json
def index
#questions = Question.all
end
# GET /questions/1
# GET /questions/1.json
def show
#answers = #question.answers
end
# GET /questions/new
def new
#question = Question.new
#survey = Survey.find(:survey_id)
end
# GET /questions/1/edit
def edit
end
# POST /questions
# POST /questions.json
def create
#question = Question.new(question_params)
respond_to do |format|
if #question.save
format.html { redirect_to #question, notice: 'Question was successfully created.' }
format.json { render action: 'show', status: :created, location: #question }
else
format.html { render action: 'new' }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /questions/1
# PATCH/PUT /questions/1.json
def update
respond_to do |format|
if #question.update(question_params)
format.html { redirect_to #question, notice: 'Question was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
# DELETE /questions/1
# DELETE /questions/1.json
def destroy
#question.destroy
respond_to do |format|
format.html { redirect_to questions_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_question
#question = Question.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def question_params
params.require(:question).permit(:title, :single_response, :surveytization)
end
end
One problem is your join relation should have belongs_to instead of has_many, to get the has_many through working:
class Surveytization < ActiveRecord::Base
belongs_to :survey
belongs_to :question
validates :survey_id, presence: true
validates :question_id, presence:true
end
Notice the :survey and :question are singular name in the belongs_to
To Add a question to a survey you can
# create new question or find existing question and store it in #question
#question
# get the survey into #survey
#survey
#survey.questions << #question
This will magically create the surveytization as well. Now that question will belong to that survey.
You Don't even have to call #survey.save! after.

Resources