Rails 4 - Input User data into Model - ruby-on-rails

I'm building a marketplace app where sellers can list items to sell. I use devise for authentication and also have user routes to collect user inputs after they sign up.
I'm trying to set this form up to collect two seller profile inputs from users and save it in the user model. But the controller does not read the user I get an error param not found: user error.
my routes: the match line was added for the profile form I'm posting about.
devise_for :users
resources :users, only: [:update, :edit]
match "/users/:id/sellerprofile", to: "users#sellerprofile", via: [:get, :put], as: :sellerprofile
my user controller method: note that I'm using the update process for a different form so I don't think I can use it for this form.
def sellerprofile
#user = User.find(params[:id])
# if params[:user]
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to root_url, notice: 'Your profile was successfully updated.' }
else
format.html { render action: 'edit' }
end
end
#end
end
def update
#user.attributes = user_params
Stripe.api_key = ENV["STRIPE_API_KEY"]
token = params[:stripeToken]
recipient = Stripe::Recipient.create(
:name => user_params["bankaccname"], #if i want to save to db, use this: #user.bankaccname. not saving it
:type => "individual", #gives you only one set of error messages. i don't need the respond
:bank_account => token #to block either i think. eg. when i enter firstname only, i get two error
) #msgs
#user.recipient = recipient.id
respond_to do |format|
if #user.save
format.html { redirect_to edit_user_url, notice: 'Your account was successfully updated.' }
else
format.html { render action: 'edit' }
end
end
end
def user_params
params.require(:user).permit(:bankaccname, :profileimage, :profilestory)
end
sellerprofile.html.erb
<%= form_for #user, url: sellerprofile_path(#user), html: { method: :put, :multipart => true } do |f| %>
<div class="form-group">
<%= f.label :your_story %><i> (required)</i>
<%= f.text_area :profilestory, class:"form-control" %>
</div>
<div class="form-group">
<%= f.label :profile_image %><i> (required)</i>
<%= f.file_field :profileimage, class:"form-control" %>
</div>
<div class="form-group">
<%= f.submit class:"btn btn-primary" %>
</div>
<% end %>
user.rb:
has_attached_file :profileimage,
:styles => { :profile => "200x200", :p_thumb => "100x100>" },
:default_url => ""
validates_attachment_content_type :profileimage, :content_type => /\Aimage\/.*\Z/

Routes
First things first:
#config/routes.rb
devise_for :users
resources :users, only: [:update, :edit] do
match :sellerprofile, via: [:get, :put], as: :sellerprofile
end
This is a much cleaner way to handle the route.
--
Controller
Next, you need to handle the data-processing capability of the controller. To do this, you need to make sure you're sending the right params (which I believe you are. If you aren't it will be in the url) of the form:
<%= form_for #user, url: sellerprofile_path, method: :put, html: { multipart: true } do |f| %>
You may wish to observe this routing structure inside Rails:
This is the standard routing structure inside Rails (called from the resources directive). This means that if you want to use a route equivalent to update, you will likely be able to omit the #user variable from it (although I've not tested this)
-
In order to make this work as correctly as you need, you need to ensure you're passing the parameters as follows:
params => {
"user" => {
"id" => "1",
"other" => "value",
}
This will be handled by the form_for method. When you call the user_params in your controller, the params.require block looks out for the user param; the permit block looking for the individual values contained within it.
This will allow you to do the following:
#app/controllers/users_controller.rb
def UsersController < ApplicationController
def sellerprofile
#user = User.find params[:id]
#user.update user_params
end
private
def user_params
params.require(:user).permit(:x, :y, :z)
end
end
This should work as you need
--
Update
Personally, I don't see the difference between what you're doing here, and with the update method?
If you want to use Stripe details in the update method, why not just have a separate method for the stripe processing, and call it if you have certain params set:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
def update
#user.attributes = user_params
Stripe.api_key = ENV["STRIPE_API_KEY"]
token = params[:stripeToken]
if token. present?
recipient = stripe
#user.recipient = recipient.id
end
respond_to do |format|
if #user.update user_params
format.html { redirect_to edit_user_url, notice: 'Your account was successfully updated.' }
else
format.html { render action: 'edit' }
end
end
end
end
private
def stripe
return Stripe::Recipient.create(
:name => user_params["bankaccname"], #if i want to save to db, use this: #user.bankaccname. not saving it
:type => "individual", #gives you only one set of error messages. i don't need the respond
:bank_account => token #to block either i think. eg. when i enter firstname only, i get two error
)
end
def user_params
params.require(:user).permit(:x, :y, :z)
end
end

Related

How to send arbitrary parameters to controller using simple_form

I'm building a newsletter management form and I want to use simple_form. The email parameter should be sent to the email_subscriber#manage controller/action via POST method.
routes.rb
get 'email/login' => 'email_subscribers#login', as: 'email_login'
get 'email/manage' => 'email_subscribers#manage', as: 'email_manage'
email_subscribers_controller.rb
def login
end
def manage
#subscriber = EmailSubscriber.find_by_email(safe_params(:email))
unless #subscriber
# redirect_to email_login_path, notice: 'That email does not exist.'
end
end
email/login form
<%= render :layout => 'application/container' do %>
<%= simple_form_for(#subscriber, path: :email_manage_path, method: :get) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :email, as: :email %>
</div>
<div class="form-actions">
<%= f.button :submit, value: 'Manage Subscription' %>
</div>
<% end %>
The login route is where the form is. It allows the user to enter their email in order to unsubscribe from the newsletter.
The form should redirect to the manage action, passing the email parameter for which there is no corresponding model.
The current form doesn't work. For some reason it redirects to the EmailSubscribers index page.
Changing the email_manage route to POST causes missing route POST email/login which makes no sense because the form is posting to email_manage_path, not the email_login_path
Thanks
EDIT:
rake routes output (opens in this same tab)
http://pastebin.com/eFGdvxid
You can actually model this as a conventional RESTful resource instead:
resources :subscriptions
namespace :subscriptions do
resources :logins
get '/login', to: 'logins/create'
end
The advantage is that you get a much simpler setup that follows the canonical crud verbs and you also use the correct HTTP verbs.
The only unconventional part here is that we add an additional route to create via GET:
# app/controllers/subscriptions/logins_controller.rb
class Subscriptions::LoginsController < ApplicationController
rescue_from ActionController::ParameterMissing, with: :subscription_not_found
# GET /subscriptions/logins/new
def new
#subscription = Subscription.new
end
# POST /subscriptions/logins
# GET /subscriptions/login
def create
#subscription = Subscription.find_by_email(email_param)
if #subscription
redirect_to edit_subscription_path(#subscription)
else
subscription_not_found
end
end
private
def subscription_not_found
render :new, error: 'Email could not be found.'
end
def email_param
if request.post?
params.require(:subscription).fetch(:email)
else
params.fetch(:email)
end
end
end
Since we actually are binding to a resource you can set the form up in a very straight forward way. We also add a GET route which lets the user log in directly from a link.
The form is very straight forward.
<%= simple_form_for(#subscription, path: subscriptions_sessions_path) do %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :email, as: :email %>
</div>
<div class="form-actions">
<%= f.button :submit, value: 'Manage Subscription' %>
</div>
<% end %>
You can then create a pretty run of the mill CRUD controller that lets the user edit or unsubscribe:
# app/controllers/subscriptions_controller.rb
class SubscriptionsController < ApplicationController
before_action :set_subscription, only: [:edit, :update, :destroy]
rescue_from ActiveRecord::RecordNotFound, with: :invalid_email
def new
#subscription = Subscription.new
end
def create
#subscription = Subscription.new(subscription_params)
if #subscription.save(subscription_params)
redirect_to root_path, success: 'Your subscription settings have been creates'
else
render :new
end
end
def edit
end
def update
if #subscription.update(subscription_params)
redirect_to root_path, success: 'Your subscription settings have been updated'
else
render :edit
end
end
def destroy
#subscription.destroy
redirect_to root_path
end
private
def set_subscription
#subscription = Subscription.find(params[:id])
end
def subscription_params
params.require(:subscription).permit(:foo, :bar, :baz)
end
end

Param in URL not passed to a form - Rails 4

When accessing URL localhost:3000/buyers/sign_up?email=abc%40abc.com , the sign-up form is displayed but email field stays empty and is not auto-filled with abc#abc.com.
Could this be related to the implemented Single Table Inheritance (STI) ? -> Buyer inherits from User.
routes.rb
devise_for :users, :controller => {:sessions => 'sessions'}, :skip => :registrations do
delete '/logout', :to => 'sessions#destroy', :as => :destroy_user_session
get '/login', :to => 'sessions#new', :as => :new_user_session
post '/login', :to => 'sessions#create', :as => :new_user_session
end
devise_for :sellers, :skip => :sessions
devise_for :buyers, :skip => :sessions
resources :users
devise/registrations/new.html.erb
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %></div>
<div><%= f.submit "Sign up" %></div>
<% end %>
users_controller
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render action: 'show', status: :created, location: #user }
else
format.html { render action: 'new' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:first_name, :last_name, :name, :type, :email)
end
end
Just found the answer.
Simply need to add value in the form + a condition to check presence of the e-mail param:
<%= f.email_field :email, value: params[:email] %>
Params
The problem isn't to do with STI - it's to do with how you're passing variables / parameters to your form.
You must remember that when you use form_for, Rails will automatically populate the provided HTML elements with data from the ActiveRecord object - in that if your object has the attribute email, the form will have that data populated
If you want to populate the form with new data (from a URL), you'll either have to be able to populate the #user.email attribute (as suggested by JTG), or take the params from the URL & pass them through to your form
--
The ways I would look at doing this would include:
Controller-based (the best)
Front-end "params" based
The controller method can be accessed as follows:
#app/controllers/users_controller.rb
Class UsersController < ApplicationController
def new
#user = User.new(email: params[:email])
end
end
This should work, as the param should be made available to the controller during the request.
-
The alternative, as you've discovered, is to populate the value of the element on your page:
<%= f.text_field :email, value: params[:email] %>
This will leave the :email field blank if no email parameter was / is defined.
Making a request to new#User (which is what you are requesting with /buyers/sign_up) won't autofill any of the sign_up form by default
However, you can put
def new
#user = User.new
#user.email = params[:email] if params.has_key? "email"
end
for the url
/buyers/sign_up?email=abc%40abc.com
And it should auto complete the email textfield of your form.
if your url was structured like so...
/buyers/sign_up?user[email]=abc%40abc.com
Then you should be able to use the much more elegant
def new
#user = User.new(params[:user])
end
in your new call. That way you can send in more than one parameter

Devise + CanCan: Admin manages Users

With my set up I have 2 types of Devise users Admins and Users I would like to be able to have the admins manage the users.
I have found some tutorials about this but they approach the problem from the perspective of a single User model with roles.
So far I've gotten to the point where, when I'm logged in as an admin, I can list the users, destroy the users and create new users, however, when I try to edit a user I get a blank form (as opposed to one that's populated by user information)
Any advice would be appreciated.
Below are the relevant files. Let me know if you need to see anything else.
/config/routes.rb
TestApp::Application.routes.draw do
devise_for :admins
devise_for :users
root to: 'pages#home'
# Admin Routes
if Rails.env.production?
devise_for :admins, :skip => [:registrations]
else
devise_for :admins
end
namespace :admins do
resources :users
end
authenticated :admin do
# For production because of skip registrations
get 'admins/edit' => 'devise/registrations#edit', as: :edit_admin_registration
put 'admins' => 'devise/registrations#update', as: :admin_registration
get 'admins/dashboard' => 'admins#dashboard', as: :admin_dashboard
devise_scope :admin do
get 'admins/list' => 'admins/users#index', as: :manage_users
get 'admins/users/new' => 'admins/users#new', as: :new_admins_user
get 'admins/users/:id/edit' => 'admins/users#edit', as: :edit_admins_user
post 'admins/users' => 'admins/users#create', as: :users
delete 'admins/users/:id' => 'admins/users#destroy', as: :destroy_admins_user
end
# Manage Content Routes
get '/pages/manage' => 'pages#manage', as: :manage_pages
get '/products/manage' => 'products#manage', as: :manage_products
end
authenticated :user, :admin do
get '/products' => 'products#index'
get '/pages/4' => 'products#index'
get '/gallery' => 'products#index'
end
unauthenticated do
devise_scope :users do
get '/pages/4' => 'devise/registrations#new'
get '/gallery' => 'devise/registrations#new'
end
end
resources :pages
resources :products
end
/controllers/admins_controller.rb
class AdminsController < ApplicationController
load_and_authorize_resource
def dashboard
render "admins/dashboard"
end
def index
respond_to do |format|
format.html
end
end
def destroy
#admin.destroy
redirect_to manage_admins_path
end
end
/controllers/admins/users_controller.rb
class Admins::UsersController < ApplicationController
load_and_authorize_resource
def index
#users = User.all
respond_to do |format|
format.html
end
end
def new
#resource = User.new
respond_to do |format|
format.html
end
end
def edit
#user = User.find(params[:id])
end
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to manage_users_path, notice: 'User was successfully created.' }
else
format.html { render new_admin_user_path }
end
end
end
def update
#user = User.find(params[:id])
if params[:user][:password].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to manage_users_path, notice: 'User was successfully updated.' }
else
format.html { render action: "edit" }
end
end
end
def destroy
#user = User.find(params[:id])
#user.destroy
redirect_to manage_users_path
end
# private
# def check_permissions
# authorize! :create, resource
# end
end
/views/admins/users/edit.html.haml
.input-form
%h2
Edit #{resource_name.to_s.humanize}
= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f|
= devise_error_messages!
%fieldset{id: "edit-your-account"}
.field.required
= f.label :first_name
= f.text_field :first_name, :autofocus => true
.field.required
= f.label :last_name
= f.text_field :last_name
.field.required
= f.label :company
= f.text_field :company
.field.required
= f.label :phone
= f.text_field :phone
.field.required
= f.label :email
= f.email_field :email
.field.required
= f.label :password
= f.password_field :password
%span.instructions
(leave blank if you don't want to change it)
- if devise_mapping.confirmable? && resource.pending_reconfirmation?
%br
Currently waiting confirmation for:
= resource.unconfirmed_email
.field.required
= f.label :password_confirmation
= f.password_field :password_confirmation
.field.required
= f.label :current_password
= f.password_field :current_password
%span.instructions
(we need your current password to confirm your changes)
.field
= f.submit "Update"
= link_to "Back", :back
/helpers/admins_helper.rb
module AdminsHelper
# Devise helpers for Admin::UsersController
def resource_name
:user
end
def resource
#resource ||= User.new
end
def devise_mapping
#devise_mapping ||= Devise.mappings[:user]
end
end
Looks like renaming your instance variable should do the trick. Your edit template passes the object resource to the form but the object you've loaded from the database is set to #user:
# Admins::UsersController
def edit
#user = User.find(params[:id])
end
# AdminsHelper
def resource
#resource ||= User.new
end
You could pass this instance variable to the form_for instead, or rename #user as #resource so the helper method will return the proper instance.

Runtime error - called id for nil in Rails 3 project

I'm getting this error when I try to submit my form (/POSTS/SHOW):
RuntimeError in Posts#show
Showing /Users/fkhalid2008/loand/app/views/posts/show.html.erb where line #1 raised:
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
Extracted source (around line #1):
1: <%= form_remote_tag (:update => 'message', :url => {:controller => 'main', :action => 'send_message', :user_id => #post.user.id}) do %>
2: <br>
3: <br />
4: <br />
How do I fix this?
Relevant code is below:
/VIEWS/POSTS/SHOW
<%= form_remote_tag (:update => 'message', :url => {:controller => 'main', :action => 'send_message', :user_id => #post.user.id}) do %>
<br>
<br />
<br />
<div class="field">
Hello! My name is <%= f.text_field :subject %> and I'm contacting you in response to your ad. I'm interested in learning more so get in touch! Here's my contact details: <%= f.text_field :body %>.
Submit
<% end %>
POST MODEL
class Post < ActiveRecord::Base
belongs_to :user
attr_accessible :title, :job, :location, :salary
validates :title, :job, :location, :salary, :presence => true
validates :salary, :numericality => {:greater_than_or_equal_to => 1}
default_scope :order => 'posts.created_at DESC'
end
USER MODEL
class User < ActiveRecord::Base
has_many :posts
has_one :profile
has_private_messages
attr_accessible :email
validates_presence_of :email
validates_uniqueness_of :email, :message =>"Hmm, that email's already taken"
validates_format_of :email, :with => /^([^\s]+)((?:[-a-z0-9]\.)[a-z]{2,})$/i, :message => "Hi! Please use a valid email"
end
POSTS CONTROLLER
def show
#post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render :json => #post }
end
end
def new
#post = Post.new
#post.user = current_user
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #post }
end
end
def edit
#post = Post.find(params[:id])
end
def create
#post = Post.new(params[:post])
#post.user = current_user
respond_to do |format|
if verify_recaptcha && #post.save
format.html { redirect_to :action=> "index"}
format.json { render :json => #post, :status => :created, :location => #post }
else
format.html { render :action => "new" }
format.json { render :json => #post.errors, :status => :unprocessable_entity }
end
end
end
def update
#post = Post.find(params[:id])
#post.user = current_user
respond_to do |format|
if #post.update_attributes(params[:post])
format.html { redirect_to #post, :notice => 'Post was successfully updated.' }
format.json { head :ok }
else
format.html { render :action => "edit" }
format.json { render :json => #post.errors, :status => :unprocessable_entity }
end
end
end
APPLICATION CONTROLLER (this is where I am defining current_user)
class ApplicationController < ActionController::Base
protect_from_forgery
private
def current_user
#_current_user ||= session[:current_user_id] &&
User.find_by_id(session[:current_user_id])
end
end
MAIN CONTROLLER (send_message is defined here)
class MainController < ApplicationController
def send_message
message = Message.new
message.subject = params[:subject]
message.body = params[:message]
message.sender = User.find session[:user]
message.recipient = User.find params[:user_id]
if message.save
ContactMailer.deliver_message_email message.recipient.email, message.id, request.host
return redirect_to "/posts"
else
render :text => "Hmm. Something seems to be wrong...let me look into it"
end
end
You don't have a user assigned to the post record represented by the #post instance variable.
Presumably a user needs to be logged in to make a post?
Also presumably you have a current user defined somewhere?
Your controller actions that use this form need to assign the user to the post record
def new
#post = Post.new
#post.user = current_user # You will need to get the current user from somewhere
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #post }
end
end
UPDATE
To make sure that your current user is assigned you should add a check to ensure the user is logged in in the controller actions. This is normally done by adding a before filter to authorize the current user which will redirect back to the login page if the current use is logged out.
Have a look at this rails cast to explain logging in and out and redirecting on a before filter http://railscasts.com/episodes/250-authentication-from-scratch
There is a revised version of the cast here but you will need a subscription for that
http://railscasts.com/episodes/250-authentication-from-scratch-revised
well worth paying for IMO
End of update
You will need to / should also assign the current user in whatever actions update the post record - i.e. the create and update actions in EXACTLY the same way.
Also, because you have not got a user assigned to a post record then you need to handle this scenario in the form so that you don't get 500 errors
You can use the #post.user.blank? boolean check to help you with this
Something like
<% if #post.user.blank? %>
<h2>There is no user assigned to this post record! This should never happen ad you should never see this message, please contact support if etc... </h2>
<% else %>
<!-- Place all your current form code here -->
<% end %>
You are getting the error because #post.user is nil in :user_id => #post.user.id.
Make sure you define #post in your post controller's show action and that it has a valid user association.

Update database using 'update_attributes' through 'has_many'

I'm having a problem getting my first app (I'm a total newbie) to save a new associated record. I have two models (users and pictures) with a has_many/belongs_to association. I have set up the userController so that it can create a new picture as below:
def new_picture
#user = User.find(current_user.id)
#picture = #user.pictures.build
end
def create_picture
#user = User.find(params[:id])
#picture = #user.pictures.build(params[:picture])
if #picture.save
flash[:notice] = "Your picture was successfully added."
redirect_to :action => 'show', :id => #user.id
else
render :template => "new_picture"
end
end
and I use
<%= link_to("add picture", :action => 'new_picture', :id => #user.id) if current_user %>
to add a new one. But I'd also like to be able to edit. So I updated the usercontroller with some new code:
def edit_picture
#user = User.find(current_user.id)
#picture = #user.pictures.find(params[:id])
end
# When the user clicks the save button update record
def update_picture
#user = User.find(current_user.id)
#picture = #user.pictures.find(params[:picture])
respond_to do |format|
if #picture.update_attributes(params[:picture])
flash[:notice] = "Your picture was successfully updated."
redirect_to :action => 'show', :id => #user.id
else
render :template => "new_picture"
end
end
end
and added the edit link to show.erb:
<%= link_to("edit picture", :action => 'edit_picture', :id => picture.id) if current_user %>
It loads the edit form fine, with the data all in the right place, but on save all it's doing is giving me the error 'ArgumentError in UsersController#update_picture' with a bunch of Unknown key(s) from my pictures table.
Could somebody explain why? I feel like there is one piece of the jigsaw I haven't quite understood here....
Thanks in advance!
UPDATE: View code is as follows:
<h1>New picture for <%= #user.name %></h1>
<% form_for :picture, #picture, :html => { :multipart => true }, :url => {:action => 'update_picture', :id => #user.id} do |f| %>
Can't seem to see your problem in the view code, however you can do the same thing more elegantly (RESTful) as a nested route. That way you might be able to see the problem more clearly.
config/routes.rb:
resources :users do
member do
resources :pictures
end
end
app/controllers/pictures_controller.rb:
class PicturesController < ApplicationController
before_filter :find_picture, :only => [:edit, :update]
def edit
end
def update
if #picture.update_attributes params[:picture]
flash[:notice] = "Your picture was successfully updated."
redirect_to user_path(current_user)
else
render :edit
end
end
protected
def find_picture
#picture = current_user.pictures.find params[:id]
end
end
app/views/pictures/edit.html.erb:
<%= form_for [current_user, #picture] do |f| %>
<!-- some stuff -->
<% end %>
and to link to your edit form:
<%= link_to_if current_user, 'edit picture',
edit_user_picture_path(:user => current_user, :id => picture) %>
I suggest adding 'accepts_nested_attributes_for :pictures to the user model, and then do
<%= form_for #user do |form| %>
.. user fields
<%= form.fields_for :pictures do |picture_form| %>
.. picture fields
<% end %>
<%= form.submit %>
<% end %>
in the view.
Another option is to create a new controller for the pictures. That may be simpler.

Resources