Can't associate post to Devise user - ruby-on-rails

I'm having extreme difficulty associating post to a user registered in devise.
I generated a post scaffold and got everything set up correctly in Devise.
I added a migration to the post that included a user_id field
The user model has_many :posts
The Post model belongs_to :user
For some reason I cannot connect the user with the post. Am I missing something?
thanks all!
My controller for posts
def create
#user = User.find(params[:id])
#post = #user.posts.create(params[:post])
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render action: 'show', status: :created, location: #post }
else
format.html { render action: 'new' }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end

First of all you need a user to associate a post:
#user = User.find(params[:id]) # or just use current_user as you are using Devise
As long as you have has_many association you can do the following:
#post = #user.posts.build(params[:post]) # to return newly created object without saving it to the database
#post = #user.posts.create(params[:post]) # to create and save record to the database
That's it.

Related

Rails Associations: belongs_to has_many confusion

I've read through the following tutorial and found the curious line:
notice that the create function is written in such a way that there has be a #post before creating a #comment.
You can see the supporting controller code:
Class CommentsController < ApplicationController
----
def create
#post = Post.find(current_post)
#comment = #post.comments.create(post_params) ## 'Essential stuff'
respond_to do |format|
if #comment.save
format.html { redirect_to action: :index, notice: 'Comment was successfully created.' }
format.json { render :show, status: :created, location: #comment }
else
format.html { render :new }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
----
end
Indeed, "current_post" implies that the post was created BEFORE the comment.
But what if I want both to be created simultaneously? For example, suppose my USER has_many EMAILS, and each EMAIL belongs_to a USER. Then, when creating a new user, I may want to have an expandable form that allows the user to add one, two, three, or twenty emails while creating his account.
How could this be done?
Nested Attributes is the rails way of doing what you want to achieve.
Checkout http://railscasts.com/episodes/196-nested-model-form-part-1
You need to consider using nested form, have a look into this gem, very easy to implement. It will allow the user to add multiple emails as required.

Rails web shop with Devise login and current_user

I am making a web shop application in Rails with Devise as a login gem. The structure is as follows:
A user table which has a basket_id column and in its model I have set has_one :basket
The basket table belongs_to :user
In my basket_controller.rb I want to use the create method to get the user
# POST /baskets
# POST /baskets.json
def create
#basket = Basket.new(basket_params)
#basket.user_id = current_user.id
respond_to do |format|
if #basket.save
format.html { redirect_to #basket, notice: 'Basket was successfully created.' }
format.json { render :show, status: :created, location: #basket }
else
format.html { render :new }
format.json { render json: #basket.errors, status: :unprocessable_entity }
end
end
The problem is that the current_user method that should be automatically generated by Devise is not found and therefore the user_id in the Baskets table is not set. Also I have no idea where would I set the basket_id in the Users table or this should be done automatically because of the relationship defined in the models?
Any help would be appreciated. Thanks!
For current_user to work you need to add before_filter :authenticate_user! to your controller. If that does not work maybe is a csrf problem that you can solve placing
= csrf_meta_tags
In your layout.

How do I display error messages in the same URL as the form URL in Rails forms?

(I've broken out the 2nd question that originally was part of this post into a separate post)
I am creating a product landing page with Rails in which users can enter their email address to be notified when the product launches. (Yes, there are services/gems etc that could do this for me, but I am new to programming and want to build it myself to learn rails.)
On submit of the form, if there are errors, the app currently redirects to '/invites' I would like to instead display error messages on the same page/URL as the original form? (In my case, the form is located at root while the error messages are displaying at '/invites')
I have read the Rails Guide on Routes and numerous stackoverflow posts on handling form errors nothing I've found seems to answer the question I have.
Update: Based on the reply from #rovermicrover I would like to clarify that, while I'm open to an Ajax solution, I'm fine with a page refresh that displays the error message. (I was not able to get the recommendation by #rovermicrover to function as desired - see my response to that solution below for more details.)
What I did:
Invite model:
class Invite < ActiveRecord::Base
attr_accessible :email
validates :email, :presence => {:message => "Please enter an email address."}
end
My routes file:
SuggestionBoxApp::Application.routes.draw do
root to: 'invites#new'
resources :invites
end
This is what I have in the Invites controller (I've only included the actions I'm referencing: new, create, show - it's basically the default of what Rails might generate):
class InvitesController < ApplicationController
def show
#invite = Invite.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #invite }
end
end
def new
#invite = Invite.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #invite }
end
end
def create
#invite = Invite.new(params[:invite])
respond_to do |format|
if #invite.save
format.html { redirect_to #invite }
format.json { render json: #invite, status: :created, location: #invite }
else
format.html { render action: "new" }
format.json { render json: #invite.errors, status: :unprocessable_entity }
end
end
end
end
Please let me know if there is any additional info I can provide in helping to answer this question. Thanks!
Make the form 'remote'
form_for #invite, :remote => true
....
Then in the controller
def create
#invite = Invite.new(params[:invite])
respond_to do |format|
if #invite.save
format.html { redirect_to #invite }
format.js { render :action => 'create_suc'}
else
format.html { render action: "new" }
format.js { render :action => 'create_fail' }
end
end
end
/invites/create_suc.js.erb
$('#errors').remove()
$('#new_invite').prepend("<div class='Thanks'>Thanks for signing up</div>")
$('#new_invite').hide("")
/invites/create_fail.js.erb
$('#new_invite').html('<%= escape_javascript render("form", :invite => #invite) %>');
Forms is a partial with your.... form in it, and also the handling of all errors on #invite.
There is a way to do this without resorting the making the form submit "remote", from a pure Ruby on Rails perspective. However, you can do this only if the browser has enabled cookies.
The idea is to save the form data in the session information in case of an error.
Just remember to delete the session data in case of success.
def new
#invite = Invite.new(session[:invite])
respond_to do |format|
format.html # new.html.erb
format.json { render json: #invite }
end
end
def create
#invite = Invite.new(params[:invite])
respond_to do |format|
if #invite.save
session.delete(:invite)
format.html { redirect_to #invite }
format.json { render json: #invite, status: :created, location: #invite }
else
session[:invite] = params[:invite]
format.html { render action: "new" }
format.json { render json: #invite.errors, status: :unprocessable_entity }
end
end
end

How to redirect to create action of another controller

I understand that I cannot POST on an HTML redirect, but my situation requires that I redirect to create action after authenticating user. I would like to know how to bypass this restriction:
In particular, I would like to allow an user to fill out a post without logging in using Omniauth. I save the post to session[:post] using an AJAX call. Then, the user can login using omniauth and persist the post.
I have a PostsController with create action that handle initial ajax call, and also handle html request after authenticating user:
def create
#post = Post.new(params[:post])
respond_to do |format|
format.html{
if #post.save
redirect_to #post, notice: 'Post was successfully created.'
else
render action: "new"
end
}
format.json {
if session[:post] = #post
render json: #post, status: :created, location: #post
else
render json: #post.errors, status: :unprocessable_entity
end
}
end
end
end
Then, in my controller that handles callback from Facebook, I have:
class ServicesController < ApplicationController
def create
... authentication logic here ...
sign_in(:user, service.user)
redirect_to :controller => "posts", :action =>"create"
end
method_alias: :facebook, :create
However, this doesn't work, because I can't redirect to a "create" action. How can I accomplish this task?
In the code you posted, you never read the content of the session. I think it can work if you change your code with this :
Change initialization of #post:
#post = Post.new(params[:post]) || session[:post] # Find object in session if not
And add after post.save :
session.delete :post # clean session after successful creation
New full method:
def create
#post = Post.new(params[:post]) || session[:post] # Find object in session if not in params
respond_to do |format|
format.html{
if #post.save
redirect_to #post, notice: 'Post was successfully created.'
session.delete :post # clean session after successful creation
else
render action: "new"
end
}
format.json {
if session[:post] = #post
render json: #post, status: :created, location: #post
else
render json: #post.errors, status: :unprocessable_entity
end
}
end
end
end
You can create a method on a Post model and call it both from PostsController and ServicesController to save the post (though in this case it's quite trivial: new, then save, so you achieve nothing in terms of DRY, may be some encapsulation benefits). Or create a common mixin containing the create_post method with all the logic. Then and include it into SessionsController and PostsController and call it from 'create'.
In the mixin module:
def create_post(allow_json=false)
#post = Post.new(params[:post])
respond_to do |format|
format.html {
if #post.save
redirect_to #post, notice: 'Post was successfully created.'
else
render "posts/new"
end
}
if allow_json
... your post-saving & json-processing logic ...
end
end
end
In PostsController:
def create
create_post(true)
end
In SessionsController:
def create
... authentication logic here ...
sign_in(:user, service.user)
create_post(false)
end
I didn't compile and try, so I only hope it works. In general, I must say there's something basically wrong, so I'd look for other architectural solutions to achieve the same results, but as a quick-and-dirty approach it should probably work.
I found a hack to avoid this issue: I let #create handling AJAX call and write to session, then create another action to persist it into my database after user get authenticated:
class PostsController < ApplicationController
def create
#post = Post.new(params[:post])
respond_to do |format|
format.json {
if session[:post] = #post
render json: #post, status: :created, location: #post
else
render json: #post.errors, status: :unprocessable_entity
end
}
end
end
def persist
#post = session[:post]
if #post.save
session.delete :post
redirect_to #post, notice: 'Post was successfully created.'
else
render action: "new"
end
end
Then in my routes.rb, I have:
resources :posts do
collection do
get 'persist'
end
end
Finally, in my ServicesController:
sign_in(:user, service.user)
redirect_to persist_posts_path

Rails: Pass Parameters to New Controller during 'redirect_to'

I'm using Ryan Bates' Rails Cast on Wicked Wizard Forms to create a multi-step form. I don't have a current_user method defined (not using an authentication gem) - so, I'm trying to pass the user.id parameter during the redirect_to - unfortunately, I can't seem to get it to work. Any help is appreciated!
My user controller create method
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to controller: 'user_steps', id: 'user.id' }
#format.html { redirect_to #user, notice: 'User was successfully created.' }#
format.json { render json: #user, status: :created, location: #user }
else
format.html { render action: "new" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
The user_steps controller that to which I am redirecting:
class UserStepsController < ApplicationController
include Wicked::Wizard
steps :gender, :items, :brands, :final
def show
render_wizard
end
end
You should pass it through as a param, ideally, which the redirect_to method will do for you if you use a proper route path.
Example:
redirect_to(user_steps_path(#user))
In your case, if you don't have a named route, you might do this:
redirect_to(controller: 'user_steps', id: #user.to_param)
In URLs it's advisable to use the to_param method. id is used for database queries.
What you're passing in is literally 'user.id' as a parameter. It will not be evaluated.

Resources